原です。 久しぶりに提案です。型キャストの話です。 Ruby で数学などしていると、やたらクラスばかり作ってしまって、何がなん だか分からなくなることがあります。例えば、配列はそれをベクトルと見たり、 順列と見たり、集合と見たりするわけで、それぞれの「見なし」のためには面 倒でもそれぞれ Array に delegate するクラスを作り、その都度オブジェク トを new で作るのが正式な方法です。しかし、昔は組み込みクラスはインス タンス変数を持てなかったのでしかたが無かったのですが、今では delegate ではなく、直接 Array を継承するのもオーライかと思います。そこで、型変 換の仕組みを考えてみました。 ところで、実は既に型変換のようなものは備わっていて、例えば Array::[] があります。仮に Array の要素の総和を与える sum を定義することを考えま しょう。ここで Array に組み込んでしまうのはシステム全体に影響があるの で、サブクラスにします。 class Array2 < Array def sum tot = 0 each do |x| tot += x; end tot end end そすると、Array はうまいことできていて Array2::[] が Array::[] を 継承してうまく動いてくれます。だからこんな風に使えます。 (各行の和を求めるスクリプト) while line = gets puts( Array2[*line.split.collect{|x| Integer(x)}].sum ) end これは Array2[] がキャストを行ってくれているのだと見ることができます。 これに似たキャストの仕組みを全てのクラスに持たせようというわけです。 で、キャストの形式ですが、Array2**obj のようにしようかと思います。本当 は [] を使いたいところですが、既に使われいるので。しかも、** は結合力 が強く、かつ、右結合なのもいいところです。これを使うと上はこんな風に書 けます。 while line = gets puts( (Array2**line.split.collect{|x| Integer(x)}).sum ) end さて、これを行う仕組みですがいたってシンプルです。キャストをしたいクラ スには、メソッド casting を用意します。casting は基本的には配列を返し、 クラス Foo のインスタンス foo に対して Foo.new(* foo.casting) が foo.dup と同じ効果をもつように定義します。そして Class では、キャスト を casting を使って行うわけです。次が Ruby で書いた例です。 class Class def **(other) new(* other.casting) end end 一般のクラスでキャストしたい(されたい?)クラスでは次のように定義して おくといいでしょう。 class Foo def initialize(a, b, c) @a, @b, @c = a, b, c end def casting [@a, @b, @c] end end 基本は上の通りなのですが、組み込みクラス Array の new は特殊な意味があ るので、** はこのままではうまくありません。また、効率を考えると、多少 統一性が失われても、組み込みクラスに対しては余計な配列オブジェクトを作 らないように工夫しておいた方がいいでしょう。例えば、 class Class def **(other) if self <= Array or self <= Hash self[*other.casting] elsif self <= String new(other.casting) else new(*other.casting) end end end class String def casting; self; end end class Array def casting; self; end end class Hash def casting; to_a.flatten; end end class Range def casting [first, last, exclude_end?] end end みたいに。 で、提案なんですが、この Class#** と「組み込みクラスの」casting を組 み込みで用意したらどうかということです。 今見たように Ruby で書けるなら組み込みにする必要ないではないか、という 意見もありそうですが、TCPSocket.casting なんか自分で書くのちょっと面倒 ですよね。 また、これは余計かもしれませんが、次の Class#castable というのもあると 便利かもしれません。Ruby で書くと class Class private def castable eval "class Object; def #{name}; #{name}**self; end; end", TOPLEVEL_BINDING end end みたいな感じで、 class Foo castable end とすると、Object#Foo が Foo**(obj) をするようになるというものです。 (ただし先の定義例では castable は継承できません。) ご意見いかがでしょう。こういうのがあると便利だと思いませんか? class String2 < String castable def each(&b); each_byte(&b); end end "ABCDE".String2.collect{|x| x - ' '[0]} #=> [33, 34, 35, 36, 37] class Hash castable end %w{abc, 0, efg, 1}.Hash #=> {"abc" => "0", "efg" => "1"} 最後にもう一度、キャストの仕組みを Ruby で書きかけたものを添えておきま す。 -----^ cast.rb class Class def **(other) if self <= Array or self <= Hash self[*other.casting] elsif self <= String new(other.casting) else new(*other.casting) end end end class String def casting; self; end end class Array def casting; self; end end class Hash def casting; to_a.flatten; end end class Range def casting; [first, last, exclude_end?]; end end ##### TEST ##### class String2 < String def each(&b); each_byte(&b); end end p( (String2**"ABCDE").collect{|x| x - ' '[0]} ) #=> [33, 34, 35, 36, 37] class Array2 < Array def sum tot = 0 each do |x| tot += x; end tot end end p( (Array2**[1, 2, 3, 4, 5]).sum ) #=> 15 class Class private def castable eval "class Object; def #{name}; #{name}**self; end; end", TOPLEVEL_BINDING end end class String2 < String castable end p "ABCDE".String2.collect{|x| x - ' '[0]} #=> [33, 34, 35, 36, 37] class Array2 < Array castable end p [1, 2, 3, 4, 5].Array2.sum #=> 15 class Hash castable end p %w{abc 0 efg 1}.Hash #=> {"abc" => "0", "efg" => "1"} -----$ cast.rb