Hatena::Grouprubyist

Going My Ruby Way このページをアンテナに追加 RSSフィード

Ruby ロゴ (C) Ruby Association LLC

2011年07月16日(土)

Ruby1.9 の lambda の構文

| 13:51 |  Ruby1.9 の lambda の構文 - Going My Ruby Way を含むブックマーク はてなブックマーク -  Ruby1.9 の lambda の構文 - Going My Ruby Way  Ruby1.9 の lambda の構文 - Going My Ruby Way のブックマークコメント

Ruby1.9 で 新しい lambda の構文がありました。

全然知りませんでした。忘れないうちにメモ。

>> add = -> a,b { a + b }   # add = labmda {|a,b| a + b } と同じ
=> #<Proc:0x00000001a5c318@(irb):23 (lambda)>
>> add.(1,2)                # add.call(1,2) と同じ
=> 3
>> add.call(1,2)
=> 3
>> add[1,2]
=> 3

proc.() は lambda でなくとも ProcMethod で使えます。

>> mul = proc {|a,b| a * b }
=> #<Proc:0x0000000197b3e0@(irb):37>
>> mul.(1,2)
=> 2
>> mul.call(1,2)
=> 2
>> mul[1,2]
=> 2

>> m = "hello".method(:to_s)
=> #<Method: String#to_s>
>> m.call
=> "hello"
>> m.()
=> "hello"
>> m[]
=> "hello"

こちらも、ついでにメモ。

Symbol に & をつけてブロックが与えられます。

&:sym
は
{|obj,*args| obj.__send__(:sym, *args)}
引数がない場合は
{|obj| obj.__send__(:sym)}

のような感じです。

>> [1,2,3].map(&:to_s)  # [1,2,3].map {|n| n.to_s } と同じ
=> ["1", "2", "3"]

>> require 'kconv'
=> true
>> ["日本語", "漢字"].map(&:toeuc)
=> ["\x{C6FC}\x{CBDC}\x{B8EC}", "\x{B4C1}\x{BBFA}"]

>> (1..10).inject(&:+)
=> 55
(1..10).inject(&:+)
は
(1..10).inject {|a,b| a.__send__(:+, b) }
は
(1..10).inject {|a,b| a.+(b) }
は
(1..10).inject {|a,b| a + b }

のようなイメージ。

(send__send__ を再定義しても影響しない。実装が実際にはどうなのか不明)

---

さらに、ついでのメモ。

Symbol#to_proc

>> plus = :+.to_proc
=> #<Proc:0x00000001739910>
>> plus.(1,2)
=> 3

こういうことらしい。(2011/07/20 追記)

class Symbol
  def to_proc
    lambda {|x| x.send(self)}
  end
end

----

さらに、ついでのメモ。curry 化。(前にも書いた気もしますが。。。)

>> f = -> a,b,c { a + b + c }
=> #<Proc:0x00000001e1d538@(irb):1 (lambda)>
>> f.('today','is','rain')
=> "todayisrain"

>> f1 = f.curry   # カリー化(currying)
=> #<Proc:0x00000001e13cb8 (lambda)>

>> today = f1.call('today')  # 'today' を部分適用
=> #<Proc:0x00000001e0d840 (lambda)>
>> today.('is','rain')
=> "todayisrain"

>> yesterday = f1.call('yesterday')  # 'yesterday' を部分適用
=> #<Proc:0x00000001dfa420 (lambda)>
>> yesterday.('was','rain')
=> "yesterdaywasrain"

>> today_is = today.curry.call('is') # カリー化、部分適用
=> #<Proc:0x00000001de5778 (lambda)>
>> today_is.('fine')
=> "todayisfine"

カリー化は Proc#curry がしているようなこと、

部分適用は引数の一部をあらかじめ適用させておくこと、です。

---

さらにメモ。引数の数とカリー化。

> f = -> a,b,c { a + b + c }
=> #<Proc:0x00000001e34c10@(irb):41 (lambda)>
>> m = f.curry.call('today').curry.call('is').curry.call('fine')
=> "todayisfine"

m は引数を取らない Proc にはなりませんでした。

>> m = f.curry.call('today').curry.call('is').curry
=> #<Proc:0x00000001e0c2b0 (lambda)>
>> m.call('OK')
=> "todayisOK"
>> m = f.curry.call('today').curry.call('is')
=> #<Proc:0x00000001df56a0 (lambda)>
>> m.call('OK')
=> "todayisOK"

m は同じ proc です。

Proc#curry は引数を取る proc を作れない場合はカリー化しないみたいです。

Ruby サイトのドキュメントを見ました。カリー化Proc は十分な数の引数が渡されるとproc を実行して、足りないと部分適用したカリー化Proc を返す、ようです。

可変引数 Proc で試した場合

>> f = proc {|a,b,*args| a + b + (args * ' ')}
=> #<Proc:0x00000001eb1080@(irb):34>
>> f1 = f.curry                        # カリー化
=> #<Proc:0x00000001eac468>
>> today = f1.call('today')
=> #<Proc:0x00000001ea5140>
>> today_is = today.curry.call('is')   # ここでは、カリー化されない
=> "todayis"

>> today_is = today.curry.call('is','fine')
=> "todayisfine"

Proc#.curry で固定する引数の数を指定できます。

>> f = proc {|a,b,*args| a + b + (args * ' ')}
=> #<Proc:0x00000001da41d8@(irb):53>
>> f1 = f.curry(3)                      # 3 に指定
=> #<Proc:0x00000001d93f18>
>> today = f1.call('today')
=> #<Proc:0x00000001d82a60>
>> today_is = today.curry.call('is')    # カリー化される
=> #<Proc:0x00000001d79ed8>

引数の「十分な数」は Proc#.arity で確認できます。

>> f = -> a,b,c { a + b + c }
=> #<Proc:0x00000001ceb340@(irb):59 (lambda)>
>> f.arity
=> 3

カリー化Proc は3回目(3レベル目?)で proc を実行します。2回までは、カリー化Proc を返します。(上の実験の通りです)

>> f = proc {|a,b,*args| a + b + (args * ' ')}
=> #<Proc:0x00000001c52938@(irb):65>
>> f.arity
=> -3

可変引数の場合は、必要な数に 1 を足して符号を負にした数が返ります。

 -( 必要な引数の数 + 1 )

カリー化Proc は2回目で proc を実行。1回目は カリー化 Proc を返します。(上の実験の通りです)