【Ruby】Procのカリー化、lambraの呼び出し方、クロージャー(共有変数)
この本を読んでいます。
- 作者: まつもとゆきひろ,David Flanagan,卜部昌平(監訳),長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/01/26
- メディア: 大型本
- 購入: 21人 クリック: 356回
- この商品を含むブログ (124件) を見る
以下、気がついたことなど。
Procのカリー化、lambraの呼び出し方
実行方法も色々ある。
# lambraの呼び出し方いろいろ product = ->(x, y){x ** y} hoge = product.curry[3] p hoge.call(2) p hoge.(2) p hoge #<= Procオブジェクトが返る p "------------" a = lambda{|w,x,y,z|w+x+y+z}.curry[1,2,3] p a #<= Procオブジェクトが返る a = lambda{|w,x,y,z|w+x+y+z}.curry[1,2,3,4] p a #<= 引数がすべて指定された場合、実行結果が返る a = lambda{|w,x,y,z|p "hoge"}.curry[1,2,3] p a #<= Procオブジェクトが返る(ブロックで使われる引数が固定されているか、を見ているわけではない)
■実行結果
9 9 #<Proc:0x2fd3300 (lambda)> "------------" #<Proc:0x2fd2a30 (lambda)> 10 #<Proc:0x2fd25f8 (lambda)>
クロージャーと共有変数
Procオブジェクトはブロックが使うすべての変数の束縛を保持している。
束縛は動的である。
順を追って書くと、
def hello(name) p "hello #{name}" end name="yamada" hello(name) name = name << "desu"
■実行結果
"hello yamada"
Rubyは上から順に実行されるから、これは当然。
じゃあこれは?↓
def multiplier(n) execer = lambda{|data| data.collect{|x| x*n }} return execer end a=2 doubler = multiplier(a) a=3 p doubler.call([1,2])
■実行結果
[2, 4]
これは、変数aを再宣言することによってa.object_idの値が変わるため、
a=3の情報はProcオブジェクトに引き継がれないから。
じゃあこれは?
def method1(aaa, bbb) execer = lambda{ p aaa; p bbb} return execer end a="hogea" b="hogeb" meth_a = method1(a,b) #<= (ア) a << "dayo" b = b + "dayo" meth_a.call()
■実行結果
"hogeadayo" "hogeb"
String#<<メソッドは破壊的なため、
内容が書き換わったaがProcオブジェクト内で参照される。
String#+メソッドは破壊的ではないため、
bに再代入した時点でbのobject_id(ポインタ先)が変更されてしまいmethod1呼び出し時のb(ア)とは別ものとなってしまう。
オブジェクトIDを出力すると良く分かる。
def method1(aaa, bbb) execer = lambda{ p aaa; p bbb; p "no3 #{bbb.object_id}"} return execer end a="hogea" b="hogeb" meth_a = method1(a,b) a << "dayo" p "no1 #{b.object_id}" b = b + "dayo" p "no2 #{b.object_id}" meth_a.call()
■実行結果
"no1 25730268" "no2 25729716" "hogeadayo" "hogeb" "no3 25730268"
no3 はno1のポインタ先を見ている。
あんまりlambdaとは関係ない話になっちゃった。
でもこれが分かったら、
同じスコープ内のlambdaを定義すると、変数の値を更新できることは、自然に分かった。
def method1(n) execer = lambda{p n} setter = lambda{|data| n = data} return execer, setter end a=2 execer, setter = method1(a) execer.call setter.call(4) execer.call
■実行結果
2 4
これを踏まえたうえで、
evalメソッドが出てくる。
def method1(n) execer = lambda{p n} end execer = method1(2) execer.binding.eval("n='hoge'") execer.call
■実行結果
"hoge"
ふーむ。
lambdaとevalの組み合わせと聞くと難しそうに思えるけど、
そこまで特殊な感じはしない。