Ruby には言語の機能としての列挙型がない。
ちょっと使いたいことがあって、いくつかのパターンで列挙型ぽく振舞うものを書いてみた。
require 'forwardable' require 'ostruct' class Array def to_enum_hash(start_from = 0) keys = self.dup vals = Array.new(self.size) {|i| start_from.to_i + i } Hash[*keys.zip(vals).flatten] end end module Enumeration def Enumeration.new(*args) initial_number, ordered_symbols = parse(args) OpenStruct.new(ordered_symbols.to_enum_hash(initial_number)).freeze end def enum(*args) initial_number, ordered_symbols = parse(args) define_enum(ordered_symbols, initial_number, self) end private def parse(args) if args.last.is_a?(Hash) initial_number = args.pop[:start_from] end initial_number ||= 0 ordered_symbols = args.map {|a| a.to_sym } [initial_number, ordered_symbols] end def define_enum(names, value, target) return if names.size == 0 target.const_set(names.shift, value) define_enum(names, value.next, target) end end class Module include Enumeration end
使い方はそれぞれこんなかんじ。
require 'enumeration' FeeTable = [ [1200, 600], # sex[:male] [800, 400] # sex[:female] ] class Sex; enum :Male, :Female; end class Generation; enum :Adult, :Child; end p FeeTable[Sex::Male][Generation::Child] #=> 600 sex = Enumeration.new :male, :female generation = Enumeration.new :adult, :child p FeeTable[sex.female][generation.adult] #=> 800 sex = [:male, :female].to_enum_hash generation = [:adult, :child].to_enum_hash p FeeTable[sex[:female]][generation[:child]] #=> 400
OpenStruct を使った実装が使っているときの見た目が一番 Ruby ぽいかなぁ。