サーチ…


構文

  • Proc.new( ブロック
  • ラムダ{| args |コード}
  • - >(arg1、arg2){code}
  • オブジェクト.to_proc
  • {| single_arg |コード}
  • do | arg、(key、value)| コード終了

備考

以下のように、複数のメソッドがチェーンされている場合、演算子の優先順位に注意してください。

str = "abcdefg"
puts str.gsub(/./) do |match|
  rand(2).zero? ? match.upcase : match.downcase
end

代わりに何か印刷するabCDeFg 、あなたが期待するように、それは何か印刷します#<Enumerator:0x00000000af42b28> -ので、これはされdo ... end意味の方法よりも低い優先順位、持っているgsub唯一見/./引数をブロック引数ではありません。列挙子を返します。ブロックはputsに渡され、 putsは無視され、 gsub(/./)結果が表示されます。

これを修正するには、 gsub呼び出しをかっこでgsubか、代わりに{ ... }使用します。

Proc

def call_the_block(&calling); calling.call; end

its_a = proc do |*args|
  puts "It's a..." unless args.empty?
  "beautiful day"
end

puts its_a       #=> "beautiful day"
puts its_a.call  #=> "beautiful day"
puts its_a[1, 2] #=> "It's a..." "beautiful day"

最後の例のcall_the_blockメソッドをコピーしました。ここでは、ブロックでprocメソッドを呼び出すことによってprocが作成されていることがわかります。メソッドのようなブロックも暗黙のリターンを持つことがわかります。つまり、procs(とlambdas)も同様です。 its_aの定義では、ブロックは通常のものと同様にスプラット引数を取ることができます。彼らはデフォルトの引数を取ることもできますが、私はその方法を考えることができませんでした。最後に、複数の構文を使用してメソッドを呼び出すことができます。 callメソッドか[]演算子。

ラムダ

# lambda using the arrow syntax
hello_world = -> { 'Hello World!' }
hello_world[]
# 'Hello World!'

# lambda using the arrow syntax accepting 1 argument
hello_world = ->(name) { "Hello #{name}!" }
hello_world['Sven']
# "Hello Sven!"

the_thing = lambda do |magic, ohai, dere|
  puts "magic! #{magic}"
  puts "ohai #{dere}"
  puts "#{ohai} means hello"
end

the_thing.call(1, 2, 3)
# magic! 1
# ohai 3
# 2 means hello

the_thing.call(1, 2)
# ArgumentError: wrong number of arguments (2 for 3)

the_thing[1, 2, 3, 4]
# ArgumentError: wrong number of arguments (4 for 3)

また、 ->を使ってcreateと.()を使ってlambda

the_thing = ->(magic, ohai, dere) {
  puts "magic! #{magic}"
  puts "ohai #{dere}"
  puts "#{ohai} means hello"
}

the_thing.(1, 2, 3)
# => magic! 1
# => ohai 3
# => 2 means hello

ここでは、ラムダがprocとほとんど同じであることが分かります。ただし、いくつかの注意点があります。

  • ラムダの引数のアリティは強制されます。間違った数の引数をラムダに渡すと、 ArgumentErrorます。彼らはまだデフォルトのパラメータ、スプラットパラメータなどを持つことができます

  • returnながら、ラムダからラムダ戻る内からINGのreturn囲む範囲外PROCリターンからINGの:

    def try_proc
      x = Proc.new {
        return # Return from try_proc
      }
      x.call
      puts "After x.call" # this line is never reached
    end
    
    def try_lambda
      y = -> {
        return # return from y
      }
      y.call
      puts "After y.call" # this line is not skipped
    end
    
    try_proc # No output
    try_lambda # Outputs "After y.call"
    

メソッドのブロック引数としてのオブジェクト

引数の前に& (アンパサンド)を置くと、それがメソッドのブロックとして渡されます。オブジェクトは、 to_procメソッドを使用してProc変換されます。

class Greeter
  def to_proc
    Proc.new do |item|
      puts "Hello, #{item}"
    end
  end
end

greet = Greeter.new

%w(world life).each(&greet)

これはRubyの共通パターンであり、多くの標準クラスがそれを提供します。

たとえば、 Symbolto_procを引数に送信して実装します。

# Example implementation
class Symbol
  def to_proc
    Proc.new do |receiver|
      receiver.send self
    end
  end
end

これは便利な&:symbolイディオムを可能にします。これはEnumerableオブジェクトでよく使われEnumerable

letter_counts = %w(just some words).map(&:length)  # [4, 4, 5]

ブロック

ブロックは、中かっこ{} (通常は単一行ブロック用)またはdo..end (複数行ブロック用)で囲まれたコードのチャンクです。

5.times { puts "Hello world" } # recommended style for single line blocks

5.times do
    print "Hello "
    puts "world"
end   # recommended style for multi-line blocks

5.times {
    print "hello "
    puts "world" } # does not throw an error but is not recommended

注意:中括弧はdo..endより優先度が高い

収穫

ブロックは、ワードの使用方法及び機能内で使用することができるyield

def block_caller
    puts "some code"
    yield
    puts "other code"
end
block_caller { puts "My own block" } # the block is passed as an argument to the method.
#some code
#My own block
#other code

yieldブロックなしで呼び出すと、 LocalJumpErrorます。この目的のために、rubyはblock_given?という別のメソッドを提供していますblock_given? yieldを呼び出す前にブロックが渡されたかどうかを確認することができます

def block_caller
  puts "some code" 
  if block_given? 
    yield
  else
    puts "default"
  end
  puts "other code"
end
block_caller 
# some code
# default
# other code
block_caller { puts "not defaulted"}
# some code
# not defaulted
# other code

yieldはブロックにも引数をyieldことができます

def yield_n(n)
  p = yield n if block_given?
  p || n 
end
yield_n(12) {|n| n + 7 } 
#=> 19 
yield_n(4) 
#=> 4

これは簡単な例ですが、 yieldは他のオブジェクトのコンテキスト内でインスタンス変数や評価への直接アクセスを許可するのに非常に便利です。例えば:

class Application
  def configuration
    @configuration ||= Configuration.new
    block_given? ? yield(@configuration) : @configuration
  end
end
class Configuration; end

app = Application.new 
app.configuration do |config| 
  puts config.class.name
end
# Configuration
#=> nil 
app.configuration
#=> #<Configuration:0x2bf1d30>

この方法でyieldを使用するとわかるように、 app.configuration.#method_nameapp.configuration.#method_name継続的に呼び出すよりも読みやすくなります。代わりに、コードを含むブロック内のすべての設定を実行することができます。

変数

ブロックの変数はブロックのローカル変数(関数の変数に似ています)で、ブロックが実行されると終了します。

my_variable = 8
3.times do |x|
    my_variable = x 
    puts my_variable
end
puts my_variable
#=> 0
# 1
# 2
# 8

ブロックは保存できません。実行されるとブロックされます。ブロックを保存するには、 procslambdasを使用するprocsありlambdas

Procに変換する

to_proc応答するオブジェクトは、 &演算子を使用してprocsに変換できます(これもブロックとして渡すことができます)。

Symbolクラスは、 #to_proc定義して、受け取ったオブジェクトの対応するメソッドをパラメータとして呼び出そうとします。

p [ 'rabbit', 'grass' ].map( &:upcase ) # => ["RABBIT", "GRASS"]

メソッドオブジェクトは#to_procも定義します。

output = method( :p )

[ 'rabbit', 'grass' ].map( &output ) # => "rabbit\ngrass"

部分的な塗布とカッシング

技術的には、Rubyには機能はありませんが、メソッドはありません。ただし、Rubyメソッドは他の言語の関数とほぼ同じように動作します。

def double(n)
  n * 2
end

この通常のメソッド/関数は、パラメータnとり、それを倍にして値を返します。次に、より高次の関数(またはメソッド)を定義しましょう:

def triple(n)
  lambda {3 * n}
end

数値を返す代わりに、 tripleはメソッドを返します。 対話式Rubyシェルを使用してテストすることができます。

$ irb --simple-prompt
>> def double(n)
>>   n * 2
>> end
=> :double
>> def triple(n)
>>   lambda {3 * n}
>> end
=> :triple
>> double(2)
=> 4
>> triple(2)
=> #<Proc:0x007fd07f07bdc0@(irb):7 (lambda)>

実際に3倍の数を取得したい場合は、ラムダを呼び出す(または「減らす」)必要があります:

triple_two = triple(2)
triple_two.call # => 6

またはより簡潔に:

triple(2).call

カリングおよび部分的なアプリケーション

これは非常に基本的な機能を定義する上では有用ではありませんが、瞬時に呼び出されたり減らされたりしないメソッド/関数を使用したい場合に便利です。たとえば、番号を特定の数で追加するメソッドを定義するとします( add_one(2) = 3 )。あなたがこれらのトンを定義しなければならない場合、あなたはできます:

def add_one(n)
  n + 1
end 

def add_two(n)
  n + 2
end

ただし、これを行うこともできます:

add = -> (a, b) { a + b }
add_one = add.curry.(1)
add_two = add.curry.(2)

ラムダ計算を使用して、 add(λa.(λb.(a+b)))と言うことができます。 curryingは、 部分的に add 適用 add方法です。そうadd.curry.(1)である(λa.(λb.(a+b)))(1)に低減することができる(λb.(1+b)) 。部分的なアプリケーションは、 add引数を1つ渡したが、後で提供するもう1つの引数を残したことを意味します。出力は特別な方法です。

より便利なカリングの例

特定の引数を指定すると、そこから特定の式を得ることができる、本当に大きな一般式があるとしましょう。この数式を考えてみましょう。

f(x, y, z) = sin(x\*y)*sin(y\*z)*sin(z\*x)

この式は3次元で作業するために作成されていますが、この式をyとzに関してのみ必要とします。また、xを無視するために、その値をpi / 2に設定したいとしましょう。最初に一般式を作ってみましょう:

f = ->(x, y, z) {Math.sin(x*y) * Math.sin(y*z) * Math.sin(z*x)}

さて、 yz式を得るためにカリングを使ってみましょう:

f_yz = f.curry.(Math::PI/2)

次に、 f_yz格納されているラムダを呼び出す:

f_xy.call(some_value_x, some_value_y)

これはかなり簡単ですが、 xzの式を取得したいとしましょう。最後の引数でない場合、 yMath::PI/2に設定するにはどうすればよいですか?まあ、もう少し複雑です:

f_xz = -> (x,z) {f.curry.(x, Math::PI/2, z)}

この場合、プレフィックスではないパラメータのプレースホルダを用意する必要があります。一貫性をf_xyため、 f_xyように書くことができます:

f_xy = -> (x,y) {f.curry.(x, y, Math::PI/2)}

f_yzラムダ計算がどのように機能するかはf_yzです。

f = (λx.(λy.(λz.(sin(x*y) * sin(y*z) * sin(z*x))))
f_yz = (λx.(λy.(λz.(sin(x*y) * sin(y*z) * sin(z*x)))) (π/2) # Reduce => 
f_yz = (λy.(λz.(sin((π/2)*y) * sin(y*z) * sin(z*(π/2))))

今度はf_xz見てみましょう

f = (λx.(λy.(λz.(sin(x*y) * sin(y*z) * sin(z*x))))
f_xz = (λx.(λy.(λz.(sin(x*y) * sin(y*z) * sin(z*x)))) (λt.t) (π/2)  # Reduce =>
f_xz = (λt.(λz.(sin(t*(π/2)) * sin((π/2)*z) * sin(z*t))))

ラムダの計算についての詳細は、 これを試してみてください。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow