RubyのProcクラスとProc.newメソッドの使用法
Procは、ブロックとそのコンテキスト(ローカル変数のスコープとスタックフレーム)をオブジェクト化して得られるプロシージャオブジェクトです。Proc は名前のない関数のように使用できますが、ローカル変数のスコープをインポートしません(動的ローカル変数を Proc のローカル変数として使用することは可能です)。
次の例では、Proc が常にローカル変数のスコープを維持しているため、まさに var 変数が呼び出されています。
var = 1
$foo = Proc.new { var }
var = 2
def foo
$foo.call
end
p foo # => 2
Proc を生成したメソッドから戻った後、Proc 内で戻りまたは再試行があると LocalJumpError 例外がスローされます。
def foo
proc { return }
end
foo.call
# => in `call': return from proc-closure (LocalJumpError)
def foo
proc { retry }
end
foo.call
# => in `call': retry from proc-closure (LocalJumpError)
Proc の前に "&" をつけて、ブロックを持つメソッドに渡すと、ブロックの呼び出しと同じように動作します。しかし、厳密な意味では以下のような違いもあります。
# No problem.
(1..5).each { break }
# No problem in ruby 1.6.7, 1.8. In 1.6.8, an exception occurs
proc = Proc.new { break }
(1..5).each(&proc)
# LocalJumpError in ruby 1.6
# In ruby 1.8, run each again
proc = Proc.new { retry }
(1..5).each(&proc)
#=> retry from proc-closure (LocalJumpError)
これはまさにProcオブジェクトを呼び出しブロックとして使用した場合の制限です。
Proc.new
Proc.new { ... }
ブロックとそのコンテキストをオブジェクト化した結果を返します。
ブロックが与えられない場合、メソッドが呼び出されたブロックは Proc オブジェクトに変換されて返されます。
def foo
pr = Proc.new
pr.call(1,2,3)
end
foo {|args| p args }
# => [1, 2, 3]
Proc.newメソッドはより深く
Proc.new は、ブロックとそのコンテキストをオブジェクト化した後の結果を返します。
ブロックが与えられない場合、メソッドが呼び出されたブロックは Proc オブジェクトに変換されて返されます。
def foo
pr = Proc.new
pr.call(1,2,3)
end
foo {|args| p args }
# => [1, 2, 3]
This is the same as the following example
def foo
yield(1,2,3)
end
foo {|args| p args }
# => [1, 2, 3]
主コールメソッドがブロックを運ばない場合、ArgumentError例外を発生させます。
def foo
Proc.new
end
foo
# => -:2:in `new': tried to create Proc object without a block (ArgumentError)
from -:2:in `foo'
from -:4
Proc.newを使用する場合、Proc#initializeメソッドが定義されていれば、オブジェクトの初期化時に呼び出されます。それ以外はproc.newと同じです。
ブロックを表すProcオブジェクトは、Proc.newメソッドを使用するか、procメソッドにブロックを指定して作成することができます。
ブロックはProc#callメソッドを呼び出すことで実行されます。Proc#callメソッドの呼び出しの引数はブロック変数として使用され、ブロック内の最後の式の値がProc#callの戻り値となります。proc#callはProc#[]という名前も持っています。
# Processing to determine if a year in the Western calendar is a leap year
leap = Proc.new do |year|
year % 4 == 0 && year % 100 ! = 0 || year % 400 == 0
end
p leap.call(2000) #=> true
p leap[2013] #=> false
p leap[2016] #=> true
ブロック変数に|*配列|の形式を設定すると、メソッドの引数のように可変個数の引数を配列として受け取ることができます。
double = Proc.new do |*args|
args.map{|i| i * 2 } # multiply all elements twice
end
p double.call(1, 2, 3) #=> [2, 3, 4]
p double[2, 3, 4] #=> [4, 6, 8]
このほか、デフォルトパラメータ、キーワードパラメータなど、共通メソッド定義時に使用できるパラメータ形式のほとんどすべてを、ブロック変数の定義で使用し、Proc#callメソッドに代入することができます。
関連
-
Ruby変数の詳細分析
-
Ruby on Rails:rakeとデータベースのデータ移行作業
-
Rubyのブロックとパラメータ渡しを説明する
-
UbuntuでRuby on RailsフレームワークとRubyMine IDEを設定する
-
RubyおよびRuby on RailsでJSON形式のデータをパースするためのチュートリアルの例
-
Rubyのデザインパターン開発におけるobserverパターンの一例
-
RubyのSimple FactoryパターンとFactory Methodパターンを利用する
-
Rubyのデバッグツールruby-debug-base19のインストールと設定(Windows版
-
Rubyのプライベートとプロテクトを簡単にご紹介します。
-
Ruby on Railsにおける国際化の簡単な紹介
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
RVMを使ってRuby/Railsのバージョン切り替えを制御する
-
Rubyフックメソッド使用例
-
Rubyオブジェクト指向プログラミングにおけるクラスメソッドとクラスエクステンション
-
Ruby on Railsで構築するアプリケーションの基本的なディレクトリ構造のまとめ
-
Rubyのgemパッケージマネージャの使い方とbundlerによる複数バージョンのgemの管理
-
デザインパターンのうち、ProxyパターンとDecorativeパターンを使ったRubyのコード例
-
Rubyにおける数値型と定数の例
-
Ruby on Railsのルーティング設定に関するいくつかのアドバイス
-
Rubyバージョン管理ツールRVMのインストールとチュートリアルの使用方法
-
Ruby WebDriverガイド