1. ホーム
  2. スクリプト・コラム
  3. ルビートピックス

Rubyのブロックとパラメータ渡しを説明する

2022-01-04 12:53:51

I. ブロック宣言 
  ブロックは、関数呼び出しの後に {.} または do..end で囲んで宣言します。一般に、1行のステートメントには {} が使用され、複数行のステートメントには do..end が使用されます。

(1..4).each{|v| print "#{v} "} #output 1 2 3 4 



  ブロックは関数の引数とは異なり、引数を取ることができます。引数は || でくくられ、もちろん複数の引数を取ることができます。これらの引数をどのように定義するかは、実際には関数の内部で定義されるのですが、これについては後ほど説明します。

第二に、ブロック内の変数へのアクセス 
  ブロックの外側にある変数にブロックの内側からアクセスできる、つまりブロックの外側にある変数がブロックの内側で見えるということです、次のように。

sum = 0 
(1..5).each do |v| 
  name = 'smile' #name is an in-block variable and can only be seen inside the block. Assume there are no variables with the same name outside the block. 
  sum += v #sum is visible inside the block 
end 
p sum #output 15, sum has changed. 
p name #Error! name is not accessible. 



  ブロックの中の変数がブロックの外に出ることができるだけに、誤って望まない外部変数を変更してしまう可能性があります。幸いRuby 1.9以降では、ブロック引数の末尾に";"を付け、";"の後にブロック変数を指定することで安全に宣言する方法が提供されています。

name = 'outside' 
sum = 0 
(1..5).each do |v;name| #name after ";", multiple variables can be declared, separated by commas 
  name = 'inside' #name is an in-block variable and can only be seen inside the block. Assume that there are no variables with the same name outside the block. 
  sum += v #sum is accessible within the block 
end 
p sum #output 15, sum has changed. 
p name #output outside, no change. 



3、yieldステートメント 
  これを見ると、関数がどのようにブロックを呼び出しているのか、よくわからないかもしれません。では、ブロック呼び出しを紹介しましょう。キーとなるのはyieldステートメントです。関数本体の中でyieldを使うと、関数がその関数のブロックを呼び出すことになる。

def threeTime 
  yield 
  yield 
  yield 
end 
threeTime{p 'Hello world!} 



  出力は、3行のHello world! yield call blockであることはもう明らかでしょう。
ブロックの引数はどうなっているのでしょうか?もうお分かりだと思いますが、これはyieldの引数で、通常の関数と同様にyieldは引数を取ることができます。例を見てください。

def takeBlock(p1) 
 if block_given? # Determine if there is a block, if there is no block declared at yield, there will be an error, so it is better to make the determination here. 
  yield(p1) # pass p1 to the block argument, both the s in the block declaration below 
 else 
  p1 
 end 
endie 
 
takeBlock("no block") # output "no block" 
takeBlock("no block") { |s| s.sub(/no /, '') } #output"block" 



  yieldはブロックにパラメータを渡すことができるので、今度はブロックがyieldに値を渡すことができるのでしょうか?答えはイエスです。ブロックの最後の文の値は自動的にyieldに渡されます。

def nTime 
 i = yield # The first time it is called, the value of the block is returned 
 (0..i).each {|v| yield(v)} # Here yield can also be placed in the block 
end 
nTime do |v| 
 print "#{v} " if v 
 9 # The number returned when the yield is called 
end 
#output 1 2 3 4 5 6 7 8 9 



もちろん、上記の例はあくまで例であり、実際にそのように定義している人はいない。より良い定義は次のようになる。

def nTime(n) 
 (0..n).each {|v| yield(v)} 
end 
nTime(9) do |v| 
 print "#{v} " 
end 



Arrayでのfindの実装を見てみましょう。

class Array 
 def find 
  for i in 0... .size 
   value = self[i] 
   return value if yield(value) 
  end 
  return nil 
 end 
end 
[1, 3, 5, 7, 9].find {|v| v > 5 } # Implement find the first number greater than 5, output 7.

 ブロックのおかげで、Rubyではfor文が少なく、コードがより人間らしく見え、コードを書くことがもはや退屈な作業ではなく、楽しいものになりました。

四、ブロックを渡すもう一つの方法

def fun #without arguments 
 yield 
end 
proc = ->{p 'haha'} 
 
fun &proc 
##### 
def fun2(x) # with arguments 
 yield x 
end 
proc2 = ->(x){p x} 
fun2 1,&proc2 



V. instance_eval()とinstance_exec()
Rubyでは、Objec#instance_eval(), Objec#instance_exec()メソッドを使ってコードブロックを挿入し、オブジェクトのContext Probleを行って、オブジェクト内のコード断片を掘り下げて操作するという非常に素晴らしい機能が提供されます。この機能を使えば、オブジェクトの動作をテストしたり、オブジェクトの現在の状態を確認したりすることが簡単にできます。

class MyClass 
 def initialize 
  @v = 1; 
 end 
end 
obj = MyClass.new 
obj.instance_eval do 
 puts self # => #<MyClass:0x007fbb2d0299b0> 
 puts @v # => 1 
end 
obj.instance_exec(5) { |x| puts x * @v } # => 5