2013-11-07 (Thu) [長年日記]
_ Array#to_procとHash#to_proc
Symbol#to_procが存在するのはもちろんのこと、ArrayやHashにto_procを定義するテクニックは便利なので、割と使われています。
[Ruby on Rails 4.0.1リリース!大量のバグ修正、3系からの移行も少し簡単になりましたより引用]
というのを見て、最近はそういう流行があるのかと思っていたら、松田さん曰く
@yukihiro_matz @shugomaeda そんなもんまったく使われてないし便利でもなんでもないと思いますよ。例によってというかなんというかfacetsにはあるみたいですが、特に便利なようには見えないですね。
[Twitter / a_matsuda: @yukihiro_matz @shugomaeda ...より引用]
とのこと。
でもそう言われるとほんとにそうなのかなと考えたくなる。
Array#to_proc
案1
まず、Adding Array#to_proc to Rubyの案。
class Array def to_proc lambda { |target| target.send(*self) } end end %w(kung ruby bar).map &[:+, "-fu"] #=> ["kung-fu", "ruby-fu", "bar-fu"]
配列の最初の要素をメソッド名として、残りの要素を引数とする案。
発想的にはSymbol#to_procの延長線上にあるものだけど、Symbol#to_procより読みにくく感じるのと、最初の要素と残りの要素の扱いがまったく異なるところが気持ち悪い。
a.map &[:+, "-fu"]
よりa.map { |i| i + "-fu" }
の方が読みやすいし、a.map {|i|i+"-fu"}
のように空白を詰めると文字数も変わらない。
案2
続いてArray#to_procの
Person.find(:all).collect {|person| [person.name, person.id]}
を
Person.find(:all).collect(&[:name,:id])
と書けるようにしようという案。
そもそもActiveRecordで全部の列をSELECTしてからcollectで間引くコードは筋が悪い。
最近のActiveRecordなら
Person.pluck(:name, :id)
でいい。
ActiveRecord以外での使い道もあまりないように思うし、map/collect以外に使えなさそうなのも減点要素。
Hash#to_proc
案1
まず、Facetsの案。
c = Class.new do attr_accessor :a end h = {:a => 1} o = c.new h.to_proc.call(o) o.a #=> 1
のようにHashの要素を与えられたオブジェクトのsetterに渡してセットするというもの。
Array#to_procの案2にちょっと似てるけど、用途が限定的過ぎてHashに持たせる機能としては適切だとは思えない。
案2
A Hash Is A Function (Hash#to_proc)の案。
class Hash def to_proc lambda {|x| self[x] } end end
のように定義して、
specialism_codes.map {|code| SPECIALISMS[code] }
を
specialism_codes.map &SPECIALISMS
と書けるようにしようというもの。
ここでfunctionと言っているのは数学的な意味での関数で、たしかに、キーの集合を定義域、値の集合を値域とするような関数とみなせるし、Hash.new {|h,k| k.to_s}
のようにデフォルト値をブロックで与えた場合はプログラミングの文脈においても関数そのもの。
Hash#callはないけど、Hash#[]を使えばProc#[]と多相的に扱えるし。
例えば
NG_WORDS = { "XXX" => true, "YYY" => true, ... }
のようなHashを用意しておくと、
clean_words = words.reject(&NG_WORDS)
や
if words.any?(&NG_WORDS) ... end
のようにmap以外の使い方もできそう。
結論
Hash#to_procの案2だったらあってもいいかなと思うが、今までほしいと思ったことがなかったので必要かと言われるとよくわからない。
他の案はArrayやHashに定義すべきものなのかというところに疑問がある(まあ、Symbol#to_procもそうだったんだけど)し、便利かどうかも疑問だったり、用途が限定されすぎている。