ruby&active support for expert 3
DESCRIPTION
TRANSCRIPT
Ruby & ActiveSupportfor expert藤岡岳之(xibbar)
Vol.3
このセッションって
xibbarこと藤岡が自分の勉強がてら気づいたことを発表するマニアックなセッションですrubyを詳しくというよりは、Railsのソースを読んでびっくりしたことから派生して、rubyとrailsの仕組みを調べた結果ですhttp://xibbar.net/rails_tohoku/ にPDFを置いておきました。
今日の内容
ブロック、クロージャ、イテレータlambda、procSymbol#to_procの仕組み
ブロックとは{ puts "Hello, world" puts "Hello, world" puts "Hello, world"}
do puts "Hello, world" puts "Hello, world" puts "Hello, world"end
コードの塊
イテレータとは?
本来は繰り返しのためのものRubyは繰り返し以外にも使えるそのため、ブロック付きメソッド呼び出しというようになってきて、段々とイテレータとは呼ばなくなって来ているっぽい
[1,2,3].each{¦n¦ puts n}
open("/tmp/sample.txt","w"){¦f¦ f.write("Hello world\n")}
イテレータ !イテレータ
ブロックをオブジェクト化hello1 = Proc.new do puts "Hello, world" puts "Hello, world" puts "Hello, world"end
hello2 = proc do puts "Hello, world" puts "Hello, world" puts "Hello, world"end
hello3 = lambda do puts "Hello, world" puts "Hello, world" puts "Hello, world"end
hello1.callhello2.callhello3.call
% ruby sample2.rbHello, worldHello, worldHello, worldHello, worldHello, worldHello, worldHello, worldHello, worldHello, world
解説ポイント•Proc.newとprocと
lambdaの違い•callで呼び出される
ブロックを関数のように
sum = lambda {¦x,y¦ x + y}
puts sum.call(2,3)
% ruby sample3.rb5
解説ポイント•できることはわかったけど、どういう時に便利かなぁ。
ブロック付メソッド呼出
class Foo def three(&arg) arg.call yield yield endend
foo=Foo.newfoo.three{ puts "Hello world"}
% ruby sample4.rb Hello worldHello worldHello world
解説ポイント•単に3回ブロックの中を繰り返すだけのメソッド
•yieldでメソッドの中からブロックが呼び出されるのがキモ
ブロックに引数を渡す
class Foo def my_logger(filepath,&arg) file=open(filepath,"w") arg.call(file) file.close endend
foo=Foo.newfoo.my_logger("log.txt"){¦file¦ puts "Hello world" file.write "Hello world logging.\n"}
% ruby sample5.rb Hello world
% cat log.txt Hello world logging.
解説ポイント•ブロックの中で使えるロガーもどき•繰り返しだけがブロックではないという気合い
Procをメソッドに渡すclass Foo def my_logger(filepath) file=open(filepath,"w") yield(file) file.close endend
foo=Foo.newscript=proc{¦file¦ puts "Hello world" file.write "Hello world logging.\n"}foo.my_logger("log.txt",&script)
% ruby sample6.rb Hello world
% cat log.txt Hello world logging.
解説ポイント•ブロックの代わりにブロックオブジェクトを渡せる。
•&をつける必要あり。
class Foo def my_logger(filepath) file=open(filepath,"w") yield(file) file.close endend
foo=Foo.newfoo.my_logger("log.txt"){¦file¦ puts "Hello world" file.write "Hello world logging.\n"}
クロージャrubyのブロックはクロージャ
def counter_closure count = 0 lambda {¦n¦ count += n }end
count = 10counter = counter_closureputs counter.call(1)puts counter.call(2)puts counter.call(3)puts counter.call(4)counter = counter_closureputs counter.call(5)puts counter.call(6)
% ruby sample8.rb13610511
解説ポイント•ブロックの中は変数を保持し続けている。•callされた時に初期化されたりしない。•作りなおされると、そこで最初からになる•他の言語を知らないから、あんまり突っ込まないで^^;
•Cだったら関数ポインタで実現するしかないよなぁ。。。
クロージャとは引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決する関数のこと。やっぱりよくわからんが。。。他の言語の不便さを体感しないと無理かなぁ。コード中に別環境のコードを書く機能があるかどうかだと思うんだけど。。。自信なし。でも便利。
Symbol#to_proc# The same as people.collect { ¦p¦ p.name }people.collect(&:name)
# The same as people.select { ¦p¦ p.manager? }.collect { ¦p¦ p.salary }people.select(&:manager?).collect(&:salary)
マニュアルより
class Symbol def to_proc Proc.new { ¦*args¦ args.shift.__send__(self, *args) } endend
ソースはたったの1行
とっても便利で、冗長だった部分がDRYで嬉しいんだけど、なんでこうなるの???
to_procってそもそも?マニュアルより
class Foo def to_proc Proc.new {¦v¦ p v} endend
[1,2,3].each(&Foo.new)
=> 1 2 3
つまりブロックに渡すとto_procの中身をブロックの代わりに実行する。
to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます(デフォルトで Proc、Method オブジェクトは共に to_proc メソッドを持ちます)。to_proc
はメソッド呼び出し時に実行され、Proc オブジェクトを返すことが期待されます。
Symbol#to_procは
よーく見ると、args[0]をsendしている。selfは呼出し元なので、下の場合は:emailになる。User.find(:all).map{|user|user.email}とUser.find(:all).map(&:email)が等価になる。なんでシンボルを与えて、メソッドが返って来るかがわかる。send(:email)ってな具合に評価されるよ!ってのを覚えておくといいと思います。modelに独自メソッドを定義した時も呼び出せます。
class Symbol def to_proc Proc.new { ¦*args¦ args.shift.__send__(self, *args) } endend==>> user.__send__(:email) の繰り返しになる
おしまい
おしまい次回以降予定継続method_missingfind_by_email_and_nameってどういう仕組み?erb、bindings