RubyのEnumerable#mapをもっと便利にしたいよ

次のような名前のリストがあって

langs = ["ruby", "python", "lisp", "haskell"]

名前の先頭を大文字にするとしたら
君ならどうする?


そう普通Enumerable#mapを使って
次のようにするよね

langs = ["ruby", "python", "lisp", "haskell"]

langs.map { |lang| lang.capitalize } # => ["Ruby", "Python", "Lisp", "Haskell"]

Enumerable#mapってほんと死ぬほど便利だよ
僕はRubyの魅力の80%は
mapが占めてるんじゃないかって
たまに感じることがあるよ.. :)


でもただ先の例で
大文字にするだけなのにブロックって
ちょっと大げさすぎると思わない?


もちろんそうなんだよ
そう思うRubyistが沢山いたから
mapは次のように書けるようにもなってるんだ

langs = ["ruby", "python", "lisp", "haskell"]

langs.map(&:capitalize) # => ["Ruby", "Python", "Lisp", "Haskell"]

シンボルに&を付けてmapに渡すと
暗黙的にSymbol#to_procが呼ばれて
そこで配列の各要素にcapitalizeが適用されるよ
この記法のお陰でmapは一層使い勝手が良くなってるよ


じゃあ今度は先の言語名のリストから
その「言語使いのリスト」に変換してほしいんだけど..
つまり言語名の後に'ist'を付けてほしいんだ*1


そう次のようにするよね

langs = ["ruby", "python", "lisp", "haskell"].map(&:capitalize)

langs.map { |lang| lang + 'ist' } # => ["Rubyist", "Pythonist", "Lispist", "Haskellist"]

これって悔しいよね
'ist'を足すだけなのに
またブロックを書かなきゃいけない


同じように次のような場合も
ブロックを書かなきゃいけない

[1, 2, 3].map { |i| i + 10 } # => [11, 12, 13]

(1..5).map { |i| i**2 } # => [1, 4, 9, 16, 25]

[[1,2,3,4], [5,6,7,8], [9,10,11,12]].map { |arr| arr.last(2) } # => [[3, 4], [7, 8], [11, 12]]

["ruby", "python", "lisp", "haskell"].map { |lang| lang[-2, 2] } # => ["by", "on", "sp", "ll"]


僕はすごく悔しいよ..


そんなわけで..


Enumerable#mappを考えたよ!

module Enumerable
  def mapp(op=nil, *args, &blk)
    op ? map { |e| op.intern.to_proc[e, *args]} : map(&blk)
  end
end

langs.mapp(:+, 'ist') # => ["Rubyist", "Pythonist", "Lispist", "Haskellist"]

つまりmappでは引数にメソッドと
その引数が取れるんだ

[1, 2, 3].mapp(:+, 10) # => [11, 12, 13]

(1..5).mapp(:**, 2) # => [1, 4, 9, 16, 25]

[[1,2,3,4], [5,6,7,8], [9,10,11,12]].mapp(:last, 2) # => [[3, 4], [7, 8], [11, 12]]

["ruby", "python", "lisp", "haskell"].mapp(:[], -2, 2) # => ["by", "on", "sp", "ll"]


ブロックを渡せばmapに処理を投げるから
mapの代わりとしても使えるよ

[1, 2, 3].mapp { |i| i + 10 } # => [11, 12, 13]

(1..5).mapp { |i| i**2 } # => [1, 4, 9, 16, 25]

[[1,2,3,4], [5,6,7,8], [9,10,11,12]].mapp { |arr| arr.last(2) } # => [[3, 4], [7, 8], [11, 12]]

["ruby", "python", "lisp", "haskell"].mapp { |lang| lang[-2, 2] } # => ["by", "on", "sp", "ll"]


誰でも考えそうだから
既出のアイディアだったらゴメンね


って
もっと高度なことをまめさんがしてました^^;
map が面倒なので DelegateMap - まめめも


まあ折角書いたから..
ゴメンナサイm(__)m


関連記事:RubyのSymbol#to_procを考えた人になってみる - hp12c


(追記:2012-2-14)Twitterを通してすごい荒業を知ったよ*2

 [1,2,3].map(&1.method(:+)) #=> [2,3,4]

スゴイね!
でもメソッド呼び出しのオブジェクトが
引数と入れ替わっちゃってるから:+, :*くらいしか
用途がなさそうだけどね..