Rubyのブロック
メタプログラミングRubyを読んでいます。 第3章の記事です。
ブロック
「呼び出し可能オブジェクト」の1つで、スコープを制御します ブロックはオブジェクト指向ではなく関数型プログラミング言語の流れを汲んでいます
やること
- ブロックの基本
- スコープの概要とブロックをクロージャとして使って変数のスコープを越える方法
- instance_eval()にブロックを渡してスコープを操作する方法
- ブロックをProcやlambdaなどの呼び出し可能オブジェクトに変換してあとで呼び出す方法
ブロックの基本
ブロックが1行の時は{ }、複数行の時はdo...endを使いましょう
ブロックはメソッドに渡され、yieldキーワードを使ってそのままコールバックされます。
def block_callback return yield end > block_callback {"this is block."} => "this is block." > block_callback LocalJumpError: no block given (yield)
クロージャとは、Rubyが文脈によって変数のブロックを読み取ってくれる機能です。
def clojure_practice x = "x in method" yield("this is y") end > x = "this is x" > clojure_practice{|y| puts x; puts y} this is x this is y => nil
ここでxがメソッド内のものではなくブロックが定義されたときのものを見ています。これがクロージャです。
グローバル変数よりもmainオブジェクト(トップレベル)のインスタンス変数を使う方がいいです。
@top_var = "トップレベルの変数です" def practice_method @top_var end > practice_method => "トップレベルの変数です"
スコープゲート
以下の3つのキーワードでスコープが入れ替えられます。これをスコープゲートといいます。
- class
- module
- def
フラットスコープ
スコープをぶち抜く事を「入れ子構造のレキシカルスコープ」や「スコープのフラット化」といいます。
classやdefがあるとスコープが外れてしまうので、class、def以外でクラスとメソッドを定義します。
#スコープゲートを越えられない var = "スコープ確認用" class ScopePractice puts var def scope_method puts var end end NameError: undefined local variable or method `var' for ScopePractice:Class from (irb):3:in `<class:ScopePractice>' from (irb):2 #スコープゲートを越える var = "スコープ確認用" ScopePractice = Class.new do puts var + "クラス内" define_method(:scope_method) do puts var + "メソッド内" end end スコープ確認用クラス内 => ScopePractice > ScopePractice.new.scope_method スコープ確認用メソッド内
上だとスコープ外れてvarが見えませんが、下だとスコープがフラット化されたvarが見えるようになりました。
instance_eval()
インスタンス変数にアクセスするにはinstance_eval()を使います。
class ContextSearcher def initialize @var = "sample" end end > ContextSearcher.new.instance_eval{@var} => "sample" > v = "test" > ContextSearcher.new.instance_eval{@var = v} => "test"
このinstance_evalに渡したブロックをコンテキスト探査機と呼びます。
ここでは@varと@var = vが該当します。
呼び出し可能オブジェクト
Procオブジェクト
今まで書いてきたブロックはオブジェクトではありません。 ですが、ブロックを保管して後から実行したいときにオブジェクト化しておくと便利です。 ここで Procがでてきます。
def proc_method proc = Proc.new{puts "Procオブジェクトからの呼び出し"} proc.call end > proc_method Procオブジェクトからの呼び出し
この技術を遅延評価と呼びます。
また、ブロックをProcオブジェクトに変換するカーネルメソッドにlambda()とproc()があります。基本的に3つのうち好きなものを使えばいいようです。
Proc.newとlambda()の大きな違い
Proc.new()とlambda()にはいくつか違いがあって、その中でも最も影響が大きいのがreturnについてです。
- Proc.newのreturnはスコープのreturnキーワードです。後続の処理は実行されません。(例えば、メソッド内で定義されたならそのメソッドのreturnキーワードになります。)
- lambda()のreturnはそのlambda文を抜けるだけです。
Rubyistの多くはProcの機能が必要でない限りlambdaを選ぶみたいです。
lambda()とproc()の違いに関してはこちらに詳しく載っています。
ラムダ式(アロー演算子) Ruby1.9から導入されたlambdaを定義する記法です。以下の二つは同じ事を示します。
l -> {|x| x + 1} l = lambda{|x| x + 1}
以下に詳しく載っています
参考
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/10/10
- メディア: 大型本
- この商品を含むブログ (1件) を見る