Ruby: Code Block

6111 ワード

コードブロックは何ですか?
コードブロックは、{...}またはdo..endによって囲まれたコードである.コードブロックは通常、匿名メソッドに似たカスタム演算を実現するために使用されます.コードブロックは、指定された変数(匿名でない)にProcオブジェクトとして付与されてもよい.以上の特性のため、コードブロックは、閉パケットなどの関数式プログラミングの特性を提供する.
匿名および非匿名コードブロック
デフォルトblockは匿名コードブロックであり、procは非匿名コードブロックである.Procを使用する利点は、繰り返し使用するために変数に保存できることです.blockはyieldキーワードで呼び出され、procはcallメソッドで呼び出されます.blockはメソッド呼び出し後(単一)、procはパラメータとしてメソッドに複数回入力することができる(複数回).
# implicit code block
def calculation(a, b)
  yield(a, b)
end
calculation(5, 6) { |a, b| a + b } # addition block     #=> 11
calculation(5, 6) { |a, b| a - b } # subtraction block  #=> -1
# explicit code block (lambda)
def calculation(a, b, operation)
  operation.call(a, b)
end
addition_block    = lambda { |a, b| a + b } # addition block
subtraction_block = lambda { |a, b| a - b } # subtraction block
calculation(5, 6, addition_block)    # addition      #=> 11
calculation(5, 6, subtraction_block) # subtraction   #=> -1

匿名と非匿名の相互転化
blockとprocは、&シンボルによって互いに変換することができる:先頭バンド&シンボルの全体(&foo)は、&シンボルを削除したfoo変数およびそれに対応するprocオブジェクトを参照することによってブロックと見なすことができる.
# implicit -> explicit
def calculation1(a, b, &block) 
  block.class #=> Proc
  block.call(a, b)
end
calculation1(1, 2) { |a, b| a + b } # => 3
addition_lambda = -> (a, b) { a + b }
calculation1(1, 2, &addition_lambda) # => 3
# explicit -> implicit
def calculation2(a, b)
  yield(a, b) if block_given?
end
calculation2(5, 5) { |a, b| a + b } # => 10
addition = -> (a, b) { a + b }
calculation2(5, 5, &addition) # => 10

lambda vs proc
同じ点
両方ともProc類のオブジェクトproc { 'foo' }.class # => Proc lambda { 'bar' }.class # => Proc lambdaには文法糖があり、-> { 'bar' } procと略記してもProc.new { 'foo' }によりオブジェクトをインスタンス化してもよい.
異なる点
Lambdaはprocよりも厳しく、匿名の方法のようだ.1.戻り値return、lambdaは現在のコードブロックから戻り、procは現在のコードブロックが存在する役割ドメインから戻る.2.コードブロックパラメータ検査、lambdaパラメータ検査はもっと厳格で、少なくなったら間違いを報告しますが、procはしません.Proc#lambda?を呼び出すことで、彼らを区別することができます.
Tips:block/proc/lambdaは、lambdaにおけるnextと同様に、returnのキーワードを使用してコードブロックを終了することができる.奇妙な戻り文法のため、procではreturnの使用をできるだけ避けます.実際にそれをマスターしていない限り、本当にこの特性を使用する必要があります.
def a_method
  puts lambda { return "we just returned from the block" }.call
  puts "we just returned from the calling method"
end
a_method
#---output---
# we just returned from the block
# we just returned from the calling method
def a_method
  puts proc { return "we just returned from the block" }.call
  puts "we just returned from the calling method"
end
result = a_method
puts result
#---output---
# we just returned from the block

クローズドパッケージとは
閉包は関数式プログラミングにおける重要な特性である.私の理解は、彼が役割ドメインを貫通し、コードブロックコンテキストのローカル変数にアクセスできることです.
Wikipedia: ‘Closure is a function or a reference to a function together with a referencing environment. Unlike a plain function, closures allow a function to access non-local variables even when invoked outside of its immediate lexical scope.’
コードブロックのその他の実用的なテクニック
  • Kernel#block_given?:この方法は、呼び出し時に匿名コードブロックが受信されたか否かを判断するために使用される(ここでは、匿名コードブロックが送信されたか否かを判断するためにのみ使用され、非匿名コードブロックが送信されなかった場合、:block_givenfalseに戻る).最適な適用シーン:使用の目的は、通常、yieldを使用する前に匿名のコードブロックが操作できるかどうかを判断することです.匿名コードブロックが追加されず、yieldキーワードが使用されている場合、解釈器はLocalJumpErrorの異常エラーを放出し、エラープロンプトno block is givenを伴う.だから毎回yieldを使う前に:block_givenで判断することを確保して、これで大丈夫です:D
  • 匿名コードブロック構文糖:(1..3).map(&:to_s) #=> ["1", "2", "3"] &は、後続のsymbol :to_sをトリガして、自身のSymbol#to_procメソッドを呼び出し、&を介してprocを匿名のblockに変換してmapメソッドに渡す.

  • Play with Code Block
  • 非匿名コードブロックをパラメータとしてメソッドに入力し、匿名コードブロックに変換して使用する.
  • def filter(array, block)
         array.select(&block)
    end 
    arr = [1, 2, 3 ,4 ,5]
    choose = -> (n) { n < 3}
    filter arr, choose # => [1, 2] 
    # explain: 'select' should receive implicit code block,
    # the second parameter of the 'filter; method is lambda,
    # so use '&block' to convert lambda to implicit block.
    
  • は、匿名コードブロックを非匿名コードブロックに転送し、依然として匿名コードブロックとして使用する.
  • filter_block = lambda do |array, &block|
         array.select(&block)
    end
    filter_block.call([1, 2, 3, 4])     { |number| number.even? }    #=> [2, 4]
    filter_block.call([1, 2.0, 3, 4.0]) { |number| number.integer? } #=> [1, 3]
    
  • は、方法をprocオブジェクトに変換し、匿名コードブロックに変換して方法によって使用する.
  • def say; yield; end
    def hi; "hi"; end
    def hello; "hello"; end
    say &method(:hi).to_proc      # => "hi"
    say &method(:hello).to_proc   # => "hello"
    
  • 方法は、コードブロックが他の方法で使用されるように変換される.
  • def plus a, b
         a + b
    end
    def plusplus a, b, c, d, &plus
         block_given? ? yield(a+b, c+d) : 0
    end
    plusplus 1, 2, 3, 4, &method(:plus).to_proc # => 10
    
  • 計算機ウィジェット
  • # complex example
    def calculation(*operations, calculator_lambda)
         operations.each do |operation|
           num1 = operation[0]
           num2 = operation[1]
           operator = operation[2]
           puts calculator_lambda.call(num1, num2, operator)
         end
    end
    #
    calculator_lambda = -> (a, b, op) do 
         next a + b if op == '+'
         next a - b if op == '-'
         next a * b if op == '*'
         next a / b if op == '/'
         raise 'invalid operator'
    end
    #
    operation1 = [1, 2, '+'] # 1 + 2 = 3
    operation2 = [6, 3, '/'] # 6 / 3 = 2
    calculation(operation1, operation2, calculator_lambda)
    #
    #---output---
    # 3
    # 2