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

Rubyのブロック機能をより深く理解するために

2022-01-04 20:47:17

ブロックとは何ですか?

Rubyでは、ブロックは珍しいものではありません。ブロックの正式な定義は "ラップされたコードの一部" です。もちろん、この説明でわかりやすくなるとは思えませんが。

ブロックのもっと簡単な説明は "ブロックは変数に格納されたコードの一部であり、他のオブジェクトと同様にいつでも実行できます"。

では、いくつかのコードを見て、そのコードをRubyのブロック形式に再構築してみましょう。実際にコードを見て感じた方が直感的にわかると思います。

例えば、2つの数字を足すと?

puts 5 + 6
# => 11


まあ、そういう書き方でもいいんですけどね。しかし、このコードはブロック定義の前半部分しかやっていないのです。しかし、quot;wrap"やquot;stored in a variable"はしていないのです。

だから、どんどん修正していく必要があるんです。しかし、それをまとめる前に、より一般的な外観になるように改良してみましょう。

a = 5
b = 6
puts a + b
# => 11


OK ~ これは動作します - 前の数値を変数に置き換えたのです。このコードは合計処理を実行しますが、しかし、まだ変数に格納されていません。

では、実装してみましょう。

addition = lambda { |a, b| return a+b }
puts addition.call(5, 6)
# => 11


これで、きれいにまとまりましたね。

Rubyでブロックを作るには、「lambda」キーワードを使うのが最も一般的な方法です。他にも方法はありますが、今は無視してください。

この時点であなたはおそらく、「待てよ、これはクラスとオブジェクトがないことを除けば、メソッドのように見えるぞ」と思っていることでしょう。その通りです。ブロックはメソッドに似ていますが、オブジェクトと関連付けられていないのです。

では、ブロックについて詳しく見ていきましょう。

ブロックはコードのブロックを含みます。ブロックには名前を付けることができます。ブロック内のコードは、常に中括弧({})またはdo... ...endで囲まれています。

[1, 2, 3].each do |i|
 puts i
end

#=> 1
  2
  3



上記の例では、各メソッドの後にdo...end構造が続いており、これがブロックです。

Rubyでは、どのようなメソッドにもブロックを渡すことができます。

  def test;end
  test{ puts i}

def test
  yield
 end
 test{puts "hello test!"}

 def test(x)
  yield(x)
 end
 test('world!'){|x| puts "hello #{x}"}



yieldキーワードは、コードのブロックをマウントするだけでなく、ブロックに引数を渡すこともできます。

def test(&block)
  block.call("world")
 end

 test{|msg| puts "hello #{msg}"}
The block goes inside the method and has been & transformed into a Proc object.

 def test(&block)
  inner_test(&block)
 end

 def inner_test
  yield("haha! ")
 end

 test{|msg| puts "hello #{msg}"}



testメソッドで渡されたブロックはProcオブジェクトに変換され、その中のinner_testはこのProcオブジェクトを"&"でブロックに変換しています。

ブロックはオブジェクトなのか?もちろん、Rubyの他のすべてのものと同じように、ブロックはオブジェクトです。

empty_block = lambda { }
puts empty_block.object_id
# => 28765760
puts empty_block.class
# => Proc
puts empty_block.class.superclass
# => Object


このように、作成したブロックは object_id を持ち、これは Proc クラス (Ruby ではブロックと呼びます) に属し、それ自体が Object のサブクラスになっています。

この逆で、ブロックからメソッドを定義することもできます。メソッドとは、オブジェクトにバインドされたブロックのことで、オブジェクトの "state"にアクセスすることができます。

前の問題を実装するために、より伝統的な方法があります(そして、私の貧弱なオブジェクトのモデリングを許してください)。

class Calculator
 def add(a, b)
  return a+b
 end
end

puts Calculator.new.add(5, 6)
# => 11



このコードは確かにうまく動きます。では、ちょっとだけ改造してみましょう。

class Calculator
 def add(a, b)
  return a+b
 end
end

addition_method = Calculator.new.method("add")
addition = addition_method.to_proc

puts addition.call(5, 6)
# => 11



これで、伝統的な手法をブロックに変換したことになりますね

コードをブロック化しよう

足し算、引き算、掛け算、割り算をする4つのブロックを作ってみましょう。各ブロックは2つの値を変数として受け取り、演算を実行して結果を返します。

Addition = lambda { |a, b| return a+b }

Subtraction = lambda { |a, b| return a-b }

Multiplication = lambda { |a, b| return a*b }

Division = lambda { |a, b| return a/b }

# Use by call when using
Addition.call(5, 6)
# => 11