[Ruby]クロージャーを使ってブロックを1回だけ実行する

こんにちは、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, … Continue reading [Ruby]クロージャーを使ってブロックを1回だけ実行する