列挙型クラス。
C# の列挙型とほぼ同じ動きをする物を作ってみました。 メソッドなどはStructクラスを 参考にしています。
何種類かの値のみを持つクラスを作成したい場合に使うと幸せになれるかもしれません。 Symbol クラスでは自分でその実際の値を指定できませんが、このクラスの場合は こちらで指定することが出来ます。
Symbol クラスとの比較
Symbol | Enum | |
---|---|---|
値の定義 | :Foo | Enum::XXX.define( :Foo, val ) |
値のクラス | Symbol | Enum::XXX |
値を得る | :Foo, "Foo".intern | Enum::XXX::Foo, Enum::XXX.new( val ) |
名前の取得 | id2name, to_s | name, to_s |
実際の値 | to_i | value |
実際の値の決定方法 | Ruby が決定 | 自前(省略した場合は自動で名前の Symbol ) |
クラスメソッド:
- Enum.new( [ [ name, ] entry ... ] )
Enum クラスに name という名前の新しいサブクラスを作って、それを返します。 サブクラスでは列挙型のメンバがそのクラスの定数として定義されています。例えば
cat = Enum.new( "Cat", :Noraneko, :Nameneko, :Doraemon ) printf "name:%s", cat::Noraneko.name
は "name:Noraneko" を出力します。
列挙型名 name は Enum のクラス定数名になりますので大文字で始まる必要があります。 entry は、Symbol か文字列で指定します。これらは作成する列挙型クラスの定数名になりまので 大文字で始まる必要があります。
name を省略した場合(第一引数が文字列以外の場合)、生成した構造体クラスは名前のないクラスとなります。 名前のないクラスは、最初に名前を求める際に代入されている定数名を検索し、 見つかった定数名をクラス名とします( Class.new を参照)。
例: 列挙型クラスのクラス名
p Enum.new( "Foo", :Bar, :Baz ) => Enum::Foo p Foo = Enum.new( :Bar, :Baz ) => Foo
entry が存在する場合 それぞれを引数に作成された 列挙型クラスの define メソッドを呼び出します。
作成される列挙型クラスは Enumerable が extend されています。
列挙型クラスのクラスメソッド:
- Enum::XXX.new( value )
- Enum::XXX[value]
列挙型のインスタンスを中身を value で作成します。
列挙型に定義されていない value を
- Enum::XXX.define( name [, value ] )
- Enum::XXX.define( array )
- Enum::XXX.define( hash )
最初の形式は、 文字列 name の名前を持つ列挙型を定義します。 value が存在する場合はその名前の列挙型の実際の値を value にします。
定義された列挙型値は Enum::XXX に名前が name の定数になりますので、 大文字で始まる必要があります。
Array が指定された場合その中のそれぞれを define メソッドの第一引数として呼び出します。 Hash の場合はキーを第一引数に、値を第二引数として define メソッドを呼び出します。
- Enum::XXX.member?( value )
value がこの列挙型に定義されている場合は真、されていない場合は偽を返却します。
- Enum::XXX.members
この列挙型に定義されている値の配列を返却します。
- Enum::XXX.each{|member| ... }
- Enum::XXX.each_member{|member| ... }
この列挙型に定義されているそれぞれの値でブロックを評価します。
- Enum::XXX.size
- Enum::XXX.length
この列挙型に定義されている値の数を返却します。
メソッド:
- name
- to_s
この列挙型インスタンスの名前を取得します。
- value
この列挙型インスタンスの実際の値を取得します。
- proper?
この列挙型インスタンスが定義されている値を持っているかどうかをチェックします。 定義されている場合は真、されていない場合は偽を返却します。
ソース
class Enum def self.new( arg1 = nil, *rest ) name, entry = if ( arg1.is_a?( String ) or arg1.nil? ) [arg1, rest] else [nil, rest.unshift( arg1 )] end klass = Class.new( self ) self.const_set( name, klass ) unless name.nil? klass.instance_eval{|klass| self.extend( Enumerable ) @index = {} @members = [] def new( value ) obj = self.allocate obj.__send__( :initialize, value ) obj end alias :[] :new def members @members.dup end def member?( value ) @index.has_key?( value.is_a?( self ) ? value.value : value ) end def define( arg1, arg2 = nil ) if ( arg1.is_a?( Hash ) ) arg1.each_pair{|name, value| self.define( name, value ) } elsif ( arg1.is_a?( Array ) ) arg1.each{|name| self.define( name ) } else name = arg1 value = if arg2.nil? then name.to_sym else arg2 end @members << self.new( value ) self.const_set( name, @members.last ) @index[value] = name.to_s.dup.freeze end end def each_member self.members.each{|one| yield( one ) } end alias :each :each_member def length self.members.length end alias :size :length } entry.each{|one| klass.define( one ) } klass end def initialize( value ) @value = value end def hash @value.hash end def eql?( other ) other.eql?( @value ) end def ==( other ) other == @value end def proper? self.class.member?( self.value ) end def name self.class.instance_variable_get( :@index )[self.value] end alias :to_s :name def value @value end def inspect "\#<enum #{self.class} #{self.proper? ? self.name : nil.inspect}, #{self.value.inspect}>" end end if __FILE__ == $0 Foo = Enum.new Foo.define( :Bar, 1 ) Foo.define( :Baz ) ## そのクラスの定数として登録される p Foo::Bar # => #<enum Foo Bar, 1> p Foo::Bar.name # => "Bar" p Foo::Bar.value # => 1 p Foo.new( 1 ) # => #<enum Foo Bar, 1> p Foo.member?( 1 ) # => true p Foo.member?( 2 ) # => false p Foo.members # => [#<enum Foo Baz, :Baz>, #<enum Foo Bar, 1>] ## 存在しない値から作成できるが正しくない p Foo.new( 2 ).proper? # => false ## 他の定義の仕方 ## Hash を使う Hoge = Enum.new( { :Foo => 100, :Bar => 200, :Baz => 300, } ) ## Array を使う Fuga = Enum.new( [:Foo, :Bar, :Baz] ) ## 可変長引数を使う Piyo = Enum.new( :Foo, :Bar, :Baz ) [Hoge, Fuga, Piyo].each{|one| p one.members } end