ブロック、イテレータ
目次
ブロック、イテレータ
メソッドの引数の直後にブロックを渡すと、そのブロックをメソッド内で呼び出せるようになる。
メソッド内部でyieldを実行すると渡されたブロックの実行が行われる。
yield
yield
yield
end
threeTimes { print "test" } ⇒ testtesttest
ブロックが渡されたかどうかは組み込み関数のiterator?で判断する。
またyieldに引数を渡すとブロックの始めの|・・・|で受け取ることができる。
i = j = 1
if iterator?
while i <= max
yield( i )
i, j = j, i + j
end
else
array = Array.new()
fibonacci( max ) { |n| array.push( n ) }
return array
end
end
fibonacci( 1000 ){ |n| p n } ⇒ 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
p fibonacci( 1000 ) ⇒ [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
fibonacciはブロックが渡されたときはイテレータを実行し、渡されなかったときはArrayを返す。
さらに、yieldはブロックの最後の値を返り値として受け取ることができ、
Array.findも簡単に実装できる。
def find
each do |i|
return i if yield( i )
end
end
end
max = 30
print [ 1, 2, 3, 4, 5, 6 ].find{ |v| v * v > max } ⇒ 6
以上の例は「繰り返し」的な操作だったが、以下のような例も重要である。
def File.openAndProcess( *args )
f = File.open( *args )
yield f
f.close
end
end
File.openAndProcess( "data", "r") do |file|
print while file.gets ⇒ first second third
end
この例はファイルのオープン、処理、クローズをイテレータにより一つのメソッドでしている。
File.openAndProcessは引数を配列argsとして受け取り、File.openに配列argsを展開して渡している。
記述は同じ*argsだが受け取る時と渡すときで逆の操作をしていて、結果として「引数をそのまま渡す」ことを実現している。
サンプル
ファイルロックなどの処理に力を発揮する方法なので、RuBBS で配布されているRuBBSに含まれるlock.rbを参考のために載せておく(解説はしない)。
# Lock module
#
# Copyright (C) 2001, All right reserved by TADA Tadashi <sho@spc.gr.jp>
# You can redistribute it and/or modify it under GPL2.
#
module Lock
class LockError < StandardError; end
def Lock::lock( basefile = 'lock/lock', try = 10, timeout = 600 )
locking = false
lockfile = "#{basefile}.#$$.#{Time.now.to_i}"
try.times do
begin
File.rename( basefile, lockfile )
locking = true
break
rescue #rename failed
begin
Dir.glob( "#{basefile}.*" ).each do |oldlock|
if /^#{basefile}\.\d+\.(\d+)/ =~ oldlock and
Time.now.to_i - $1.to_i > timeout then
File.rename( oldlock, lockfile )
locking = true
break
end
end
rescue
end
end
sleep( 1 )
end
raise Lock::LockError, "LockError: #{lockfile} #{$!}" unless locking
if iterator? then
begin
yield
ensure
Lock::unlock( lockfile, basefile ) if locking
end
else
return lockfile
end
end
def Lock::unlock( lockfile, basefile = 'lock/lock' )
File.rename( lockfile, basefile )
end
end
多分Perlメモ>排他制御(ファイルロック)をするのRuby化+αだろう。
モジュール、例外処理等まだ出てきてないものも使っている。