ようへいの日々精進XP

よかろうもん

2018 年 02 月 15 日(木)

ジョギング

日課

  • お休み

夕飯

  • また, 出汁にこだわった

今日のるびぃ ~ yield と Proc (3) ~

引き続き, プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで の読書メモ.

Proc.new と lambda

Proc オブジェクトの生成方法.

Proc.new { |a, b| a + b }
proc { |a, b| a + b }

以下, lambda メソッドを利用した生成方法.

# -> アロー演算子
->(a, b) { a + b }
lambda { |a, b| a + b }

以下, 実行例.

[1] pry(main)> p = Proc.new { |a, b| a + b }
=> #<Proc:0x000055628235c6d8@(pry):1>
[2] pry(main)> p.class
=> Proc
[3] pry(main)> p = proc { |a, b| a + b }
=> #<Proc:0x00005562822f3bd8@(pry):3>
[4] pry(main)> p.class
=> Proc
[5] pry(main)> p = ->(a, b) { a + b }
=> #<Proc:0x0000556282278780@(pry):5 (lambda)>
[6] pry(main)> p.class
=> Proc
[7] pry(main)> p = lambda { |a, b| a + b }
=> #<Proc:0x00005562821ec550@(pry):7 (lambda)>
[8] pry(main)> p.class
=> Proc

Proc オブジェクトが Proc.new で作られたものなのか, lamda メソッドで作られたものなのかを判断したい場合には lambda? メソッドを利用する.

[1] pry(main)> p = proc { |a, b| a + b }
=> #<Proc:0x00005579e7146160@(pry):1>
[2] pry(main)> p.lambda?
=> false
[3] pry(main)> p = ->(a, b) { a + b }
=> #<Proc:0x00005579e708d430@(pry):3 (lambda)>
[4] pry(main)> p.lambda?
=> true

何れも Proc オブジェクトを生成するが, 以下の点について挙動が異なる.

  • 引数の扱い
  • return と break の挙動

Proc.new と lambda ~ 引数の扱い ~

  • Proc.new では引数の数に対して寛容
  • lambda 式では引数の数に対して厳密
[1] pry(main)> p = Proc.new { |a, b| a.to_i + b.to_i }
=> #<Proc:0x0000560d3ee8acf0@(pry):1>
[2] pry(main)> p.call(10, 20)
=> 30
[3] pry(main)> p.call(10)
=> 10
[4] pry(main)> p.call(10, 20, 100)
=> 30
[5] pry(main)> p = ->(a, b) { a.to_i + b.to_i }
=> #<Proc:0x0000560d3fa0c010@(pry):5 (lambda)>
[6] pry(main)> p.call(10, 20)
=> 30
[7] pry(main)> p.call(10)
ArgumentError: wrong number of arguments (given 1, expected 2)
[8] pry(main)> p.call(10, 20, 100)
ArgumentError: wrong number of arguments (given 3, expected 2)

Proc.new と lambda ~ return と break の挙動 ~

return の場合...

  • Proc.new で生成した Proc オブジェクト内で return を呼ぶとメソッドそのものから抜ける
  • lambda で生成した Proc オブジェクト内で return を呼ぶと lambda の処理から抜ける
[1] pry(main)> def func
[1] pry(main)*   p = Proc.new { return 1 }
[1] pry(main)*   p.call
[1] pry(main)*   2
[1] pry(main)* end  
=> :func
[2] pry(main)> p func
1
=> 1
[3] pry(main)> def func
[3] pry(main)*   p = -> {return 1}
[3] pry(main)*   p.call
[3] pry(main)*   2
[3] pry(main)* end  
=> :func
[4] pry(main)> p func
2
=> 2

break の場合...

  • Proc.new で生成した Proc オブジェクト内で break を呼ぶと例外が発生する
  • lambda で生成した Proc オブジェクト内で break を呼んだ場合, 例外は発生しないが map のループ処理は中断されず, 最後まで実行される
[1] pry(main)> def proc_func
[1] pry(main)*   p = Proc.new { |n| break n * 10 }  
[1] pry(main)*   r = [1, 2, 3, 4, 5].map(&p)  
[1] pry(main)* end  
=> :proc_func
[2] pry(main)> 
[3] pry(main)> def lambda_func
[3] pry(main)*   p = ->(n) { break n * 10 }  
[3] pry(main)*   r = [1, 2, 3, 4, 5].map(&p)  
[3] pry(main)* end  
=> :lambda_func
[4] pry(main)> p proc_func
LocalJumpError: break from proc-closure
[5] pry(main)> p lambda_func
[10, 20, 30, 40, 50]
=> [10, 20, 30, 40, 50]

ふむふむ.