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

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を考えた人になってみる

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

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

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

  1. 怒らないで!
  2. https://twitter.com/tmaeda/statuses/168380640734085120


blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS