共有できるRubyブロックの最良の説明は何ですか?
ブロックを使用できるコードの使用と記述の両方?
私は この答え から自分自身の説明を提供します、わずかに変更:
Rubyの「ブロック」は、一般的なプログラミング用語の「コードブロック」または「コードのブロック」と同じではありません。
次の(無効な)Rubyコードが実際に機能したことを少し考えてみましょう。
def add10( n )
puts "#{n} + 10 = #{n+10}"
end
def do_something_with_digits( method )
1.upto(9) do |i|
method(i)
end
end
do_something_with_digits( add10 )
#=> "1 + 10 = 11"
#=> "2 + 10 = 12"
...
#=> "9 + 10 = 19"
そのコードは無効ですが、その意図(いくつかのコードをメソッドに渡し、そのメソッドにコードを実行させること)は、さまざまな方法でRuby)で可能です。その1つが「ブロック」です。 。
Ruby内のブロックは、メソッドに非常によく似ています。いくつかの引数を取り、それらのコードを実行できます。foo{ |x,y,z| ... }
またはfoo do |x,y,z| ... end
、これらは3つのパラメータを取り、...
それらの上に。 (上記のupto
メソッドがブロックに渡されているのを見ることさえあるかもしれません。)
ブロックはRuby構文の特別な部分であるため、すべてのメソッドにブロックを渡すことができます。メソッドusesブロックはメソッド次第です。例:
def say_hi( name )
puts "Hi, #{name}!"
end
say_hi("Mom") do
puts "YOU SUCK!"
end
#=> Hi, Mom!
上記のメソッドには、侮辱を発行する準備ができているブロックが渡されますが、このメソッドはブロックを呼び出さないため、Niceメッセージのみが出力されます。メソッドからブロックを呼び出す方法は次のとおりです。
def say_hi( name )
puts "Hi, #{name}!"
if block_given?
yield( name )
end
end
say_hi("Mridang") do |str|
puts "Your name has #{str.length} letters."
end
#=> Hi, Mridang!
#=> Your name has 7 letters.
を使用しております block_given?
ブロックが渡されたかどうかを確認します。この場合、ブロックに引数を返しました。ブロックに何を渡すかを決めるのはあなたのメソッド次第です。例えば:
def say_hi( name )
puts "Hi, #{name}!"
yield( name, name.reverse ) if block_given?
end
say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" }
#=> Hi, Mridang!
#=> Is your name Mridang or gnadirM?
これは、一部のクラスが作成したインスタンスをブロックに渡すための単なる慣例(および適切なものであり、サポートしたいもの)です。
これは、ブロックを引数としてキャプチャすること、ブロックがアリティを処理する方法、ブロックパラメータのスプラッティング解除などをカバーしないため、完全な答えではありませんが、ブロック-再ラムダスの紹介
Rubyブロックは、他のコードで使用できるコードを表す Proc
objects を作成する方法です。 procオブジェクトは、中括弧{}
(または複数行ブロックの場合はdo...end
フレーズの間の命令で、中括弧よりも優先順位が高くなります)で、引数を取り、値を返すことができます(例:{|x,y| x+y}
)。プロシージャは first-class objects であり、メソッドの疑似引数として明示的に構築することも、暗黙的に取得することもできます。
Proc オブジェクトとしての構築(またはlambda
キーワードを使用):
add1 = Proc.new {|x| x+1} # Returns its argument plus one.
add1.call(1) # => 2
特別な&
最終引数構文シュガー演算子を明示的に使用するか、またはblock_given?
/yield
ペアを暗黙的に使用して、メソッドの疑似引数として渡されます。
def twice_do(&proc) # "proc" is the block given to a call of this method.
2.times { proc.call() } if proc
end
twice_do { puts "OK" } # Prints "OK" twice on separate lines.
def thrice_do() # if a block is given it can be called with "yield".
3.times { yield } if block_given?
end
thrice_do { puts "OK" } # Prints "OK" thrice on separate lines.
2番目の形式は通常 ビジターパターン ;に使用されます。データは、call
またはyield
メソッドへの引数として特別なブロック引数に渡すことができます。
Rubyの(感動的)ガイド から:
中括弧で囲まれたコードはすべてブロックです。
2.times { print "Yes, I've used chunky bacon in my examples, but never again!" }
は一例です。ブロックを使用すると、一連の命令をグループ化して、プログラム内で受け渡すことができます。中かっこは、コードをひったくり、一緒に保持しているカニのはさみのような外観を与えます。これら2つのペンチが表示されたら、内部のコードが1つのユニットに押し込まれていることを思い出してください。これは、モールで販売している小さなハローキティの箱の1つで、小さな鉛筆と顕微鏡の紙を詰めたものです。すべてがきらめく透明なケースに詰め込まれており、Palmに隠して静止操作を行うことができます。ただし、ブロックはそれほど目を細める必要はありません。中括弧は、doおよびendという単語と交換することもできます。これは、ブロックが1行より長い場合に便利です。
loop do
print "Much better."
print "Ah. More space!"
print "My back was killin' me in those crab pincers."
end
ブロック引数は、パイプ文字で囲まれ、コンマで区切られた変数のセットです。
|x|, |x,y|, and |up, down, all_around| are examples.
ブロック引数は、ブロックの先頭で使用されます。
{ |x,y| x + y }
上記の例では、| x、y |引数です。引数の後に、少しコードがあります。式x + yは2つの引数を加算します。私はパイプ文字をトンネルを表すものとして考えるのが好きです。それらは変数が滑り落ちるシュートのような外観を与えます。 (xはイーグルを広げ、yはきちんと足を交差させます。)このシュートは、ブロックとその周りの世界の間の通路として機能します。変数は、このシュート(またはトンネル)を介してブロックに渡されます。
C#の背景(または実際には他の言語)からこの質問に来た人にとって、これは役立つでしょう:
Rubyブロックは、ラムダ式やC#の無名メソッドに似ています。これらは、C#がデリゲートを呼び出す(およびRubyがProcを呼び出す)ものです。つまり、これらは本質的に値として渡すことができる関数です。Ruby C#では、クロージャーとしても動作します。
Ruby:_{ |x| x + 1 }
_
C#:_x => x + 1
_
Ruby:_{ |name| puts "Hello there #{name}" }
_
C#:name => { Console.WriteLine("Hello there {0}", name); }
C#とRubyはどちらも、上記の例を作成する別の方法を提供します。
ルビー:
_do |name|
puts "Hello there #{name}"
end
_
C#:
_delegate(string name)
{
Console.WriteLine("Hello there {0}", name);
}
_
RubyとC#の両方で、複数のステートメントを使用できます。Rubyでは、これには上記の2番目の構文が必要です。
これらの概念は、関数型プログラミングの背後にあるアイデアに影響を受けた他の多くの言語で利用できます。
本「 Programming Ruby 」には、すばらしい ブロックの説明と使用 があります。
1.9以降では、ブロックに渡されるパラメーターリストがさらに洗練され、ローカル変数を定義できるようになりました。
do |a,b;c,d|
some_stuff
end
;c,d
は、呼び出されたルーチンのyield
ステートメントから値を受け取らない、ブロック内の2つの新しいローカル変数を宣言します。 Ruby 1.9+は、変数がブロックの外側に存在する場合、ブロック内の同じ名前の変数によって踏みつけられないことを保証します。これは新しい動作です。1.8はそれらを踏みつけます。
def blah
yield 1,2,3,4
end
c = 'foo'
d = 'bar'
blah { |a, *b; c,d|
c = 'hello'
d = 'world'
puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}", "d: #{d}"
}
puts c, d
# >> a: 1
# >> b: 2,3,4
# >> c: hello
# >> d: world
# >> foo
# >> bar
パラメータのリストで機能する「splat」演算子*
もあります。
do |a,*b|
some_stuff
end
複数の値の最初の値を "a"に割り当て、残りはすべて "b"にキャプチャされ、配列のように扱われます。 *
はa
変数にある可能性があります。
do |*a,b|
some_stuff
end
b
に渡される最後の変数を除いて、渡された変数をすべてキャプチャします。そして、前の2つと同様に:
do |a,*b,c|
some_stuff
end
最初の値をa
に割り当て、最後の値をc
に割り当て、すべての/間にある値をb
に割り当てます。
それはかなり強力で滑らかだと思います。
例えば:
def blah
yield 1,2,3,4
end
blah { |a, *b| puts "a: #{a}", "b: #{b.join(',')}" }
# >> a: 1
# >> b: 2,3,4
blah { |*a, b| puts "a: #{a.join(',')}", "b: #{b}" }
# >> a: 1,2,3
# >> b: 4
blah { |a, *b, c| puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}" }
# >> a: 1
# >> b: 2,3
# >> c: 4
ブロックは、Rubyでコードをグループ化する方法の1つです。ブロックを書き込む方法は2つあります。 1つはdo..endステートメントを使用し、もう1つはコードを中括弧{}で囲んでいます。ブロックはRubyプログラミング言語のオブジェクトと見なされ、デフォルトではすべての関数が暗黙のブロック引数を受け入れます。
同じことを行うブロックの2つの例を次に示します。
2.times {puts 'hi'} 2.times do puts 'hi' end
ブロックは、縦棒||内のコンマ区切りの引数のリストを受け取ることができます。例えば:
[1,2] .map {| n | n + 2}#[3、4]
ブロック(Ruby 1.9.2内)は明示的にローカル変数を持つことができます:
x = 'hello' 2.times do |; x | x = 'world' puts x end =>ワールド =>ワールド
ローカル変数はパラメーターと組み合わせることができます:
[1,2] .map {| n; x | n + 2}
すべての関数はデフォルトのブロック引数を受け取ることができます:
def 2回 yield yield end twice {puts 'hello'} => hello =>こんにちは
Do..endブロックと{}ブロックの違いは何ですか?慣例により、{}ブロックは1行にあり、do..endブロックは複数の行にまたがっています。これは、このように読みやすいためです。ただし、主な違いは優先順位と関係があります。
array = [1,2] puts array.map {| n | n * 10}#puts(array.map {| n | n * 10}) => 10 => 20 puts array.map do | n | n * 10 end#(put array.map)do | n | n * 10 end => <列挙子:0x00000100862670>
ブロックは、いくつかの迷惑な制限がある匿名のファーストクラスプロシージャ用の軽量リテラルです。 Rubyでも同じように動作しますが、他のほとんどすべてのプログラミング言語で動作するため、前述の制限が適用されます。