概要
なんとなくdo endを使う ということは分かっている人は多いと思いますが、
procとかが出てくると少しハードルが上がるため理解していない人が多かったように思います。
そのため改めて自分の知識の整理とともに、
多くの人がrubyの文法をしっかりと理解し効率よくコーディングができるようにまとめました。
blockとは
- rubyではメソッドに対して一連の手続きを渡すことができる
- blockは
do end
もしくは{ }
で記載する - 引数は
|x|
のように|
ではさんだところに通常のメソッドとかと同じように記載する
[*1..3].each do |i|
p i
end
# => 1
# => 2
# => 3
3.times { |i| p i }
# => 0
# => 1
# => 2
blockを使うメソッドのつくり方
- blockを実行するには yieldを使用する or &blockを使用する(後述)
- blockに仮引数を使用している場合はyieldに引数を渡すことで使用できる
- yieldは何回も使用できる
- yieldはblockが渡されていない場合に使用するとエラーになる
def three_times
3.times { yield }
end
three_times { p 'a' }
# => "a"
# => "a"
# => "a"
def three_times
3.times { |j| yield(j) }
end
three_times { |i| p i }
# => 0
# => 1
# => 2
def calculate
p yield(1, 2)
p yield(2, 3)
end
calculate { |a, b| a + b }
# => 3
# => 5
calculate { |a, b| a * b }
# => 2
# => 6
def three_times(&block)
3.times { yield } rescue p $!
end
three_times
# => #<LocalJumpError: no block given (yield)>
blockが渡されたかどうかを判断する
- block_given?を使用する
def three_times
p block_given?
end
three_times
# => false
three_times { p 'y' }
# => true
blockをインスタンスとして扱いたい
- blockは
&
を使用することでProcオブジェクトと相互変換できる- メソッドの引数として
&proc_object
をすることで、ブロックとして渡せる - メソッドの仮引数として
&block
とすることで、 ブロックをProcオブジェクトとして受け取れる- 引数で
&
を使わず素のblockを受け取る場合は、lambdaではなくprocになる(procとlambdaの違いは後述) - 引数でlambdaを
&
で渡した場合はlambdaとして受け取る
- 引数で
- メソッドの引数として
proc_object = Proc.new do |a|
p a
end
3.times(&proc_object)
# => 0
# => 1
# => 2
def three_times(&block)
3.times { |j| block.call(j) }
end
three_times { |i| i }
# => 0
# => 1
# => 2
procとlambda
- Procオブジェクトには
Proc
とProc(lambda)
というものが存在する(procとlambdaとそれぞれ呼ぶこととする)- どちらも
Proc
クラス
- どちらも
- procは
Proc.new { |x| }
もしくはproc { |x| }
と書く - lambdaは
lambda { |x| }
もしくは->(x) { }
と書く
p Proc.new {}
# => #<Proc:0x00007f3e59459978@/tmp/nvimaASrm8/2:1>
p Proc.new {}.instance_of?(Proc)
# => true
p lambda {}
# => #<Proc:0x00007f3e59459838@/tmp/nvimaASrm8/2:2 (lambda)>
p lambda {}.instance_of?(Proc)
# => true
procとlambdaの違い
引数の扱い
- 引数が余分な場合
- proc: スルーされる
- lambda: エラーになる
- 引数が足りない場合
- proc: nilとなる
- lambda: エラーになる
x = proc { |a, b| p [a,b] }
x.call(:a, :b, :c)
# => [:a, :b]
x = lambda { |a, b| p [a,b] }
x.call(:a, :b, :c) rescue p $!
# => #<ArgumentError: wrong number of arguments (given 3, expected 2)>
x = proc { |a, b| p [a,b] }
x.call(:a)
# => [:a, nil]
x = lambda { |a, b| p [a,b] }
x.call(:a) rescue p $!
# => #<ArgumentError: wrong number of arguments (given 1, expected 2)>
break, returnの挙動
- Procを定義したメソッド内で実行した場合
- return
- proc: Procを囲むメソッドの結果となる
- lambda: Procの結果となる
- break
- proc: LocalJumpErrorになる
- lambda: Procの結果となる
- return
- Procを定義したメソッドの外で実行した場合
- return
- proc: LocalJumpErrorになる
- lambda: Procの結果となる
- break
- proc: LocalJumpErrorになる
- lambda: Procの結果となる
- return
手続きの結果を返したいならlambdaでもprocでもnext
を使えばよし!
def hoge
x = proc { return 2 }.call
p "x: #{x}"
return 1
end
p "method: #{hoge}"
# => "method: 2"
def hoge
x = lambda { return 2 }.call
p "x: #{x}"
return 1
end
p "method: #{hoge}"
# => "x: 2"
# => "method: 1"
def hoge
x = proc { break 2 }.call
p "x: #{x}"
return 1
end
p "method: #{hoge}" rescue p $!
# => #<LocalJumpError: break from proc-closure>
def hoge
x = lambda { break 2 }.call
p "x: #{x}"
return 1
end
p "method: #{hoge}"
# => "x: 2"
# => "method: 1"
def hoge(x)
p "x: #{x.call}"
return 1
end
x = proc { break 2 }
p "method: #{hoge(x)}" rescue p $!
# => #<LocalJumpError: break from proc-closure>
x = lambda { break 2 }
p "method: #{hoge(x)}"
# => "x: 2"
# => "method: 1"