こんにちは、hachi8833です。BPS Advent Calendarの合間を縫うように通常記事もお送りいたします。
高林哲さんの昔のブログ「Ruby: 一回だけ実行されるブロック」を読んでて、「Procかlambdaでクロージャを作れば、もう少し簡潔にできるのでは?」と思ったのでやってみました。
run_once
メソッド
ここではlambdaを使ってみました。
やってみると教科書のコードサンプルみたいな簡潔さです。アラートを最初の1回だけ表示するヘルパーメソッドなんかに使えそうです。
うれしい反面ちょっとさみしいので、YARD記法も足してみました。
コード
# Runs a block only once
# @param the block to run
# @return the object that is returned from the block first; nil after that
def run_once(&block)
count = 1
lambda do
return if count == nil
count = nil
yield
end
end
- 当初、lambdaを略記法「
->
」で書いてましたが、後続のブロックが複数行なら略記ではなくlambda
と書くのがスタイルであるという指摘をmorimorihogeさんからいただいたので、改めました。 &block
による明示的なブロック仮引数を使用するのは標準的なスタイルなので、省略しないようにしましょう。
参考: Rubyスタイルガイド: bbatsov/ruby-style-guide
使い方
a = run_once { puts "hello" }
a.call #=> "hello"
a.call #=> nil
a.call #=> nil
a.call #=> nil
応用: run_times
メソッド
上のコードを少し変えれば、渡した数値の回数だけブロックを実行し、以後は実行しないようにもできます。
デフォルト回数は1にしてみました。
コード
# Runs a block n times
# @param [num] (keyword arg) is the number of times to run the block, as well as a block to run
# @return the object that is returned from the block by the times specified; nil after that
def run_times(num: 1, &block)
lambda do
return if num < 1
num -= 1
yield
end
end
使い方
# キーワード引数で回数を渡す(引数のかっこは省略できない)
a = run_times(num:3) { puts "hello" }
a.call #=> "hello"
a.call #=> "hello"
a.call #=> "hello"
a.call #=> nil
call
もなしにできればいいなと思いましたが、それは言わない約束ということで。
こぼれ話
クロージャ(closure)は、ブレスレットやネックレスなどを輪にして止めるときの方式・メカニズムを指すこともあるようです。closure rubyで検索してみて気が付きました。
追伸: リファクタリング
公開後、morimorihogeさんからリファクタリング案をいただきました。
run_times
の名前付き引数は冗長かも(ブロック以外の引数が1つしかないし、渡す値も明らかなので)- メソッドが2つになったのだから共通化してみては。
# リファクタリング後
def run_times(num, &block)
lambda do
return if num < 1
num -= 1
yield
end
end
def run_once(&block)
run_times(1, &block)
end