Ruby块的最佳解释?

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" 

尽pipe代码是无效的,但它的意图 – 将一些代码传递给方法并让该方法运行代码 – 在Ruby中可能有多种方式。 其中之一是“块”。

Ruby中的Block非常像一个方法:它可以接受一些参数并为这些参数运行代码。 每当你看到foo{ |x,y,z| ... } foo{ |x,y,z| ... }foo do |x,y,z| ... end foo do |x,y,z| ... end ,那些需要三个参数的块,并运行...在他们身上。 (你甚至可以看到上面的upto方法正在通过一个块。)

因为块是Ruby语法的一个特殊部分,所以每个方法都允许通过一个块。 该方法是否使用该块取决于该方法。 例如:

 def say_hi( name ) puts "Hi, #{name}!" end say_hi("Mom") do puts "YOU SUCK!" end #=> Hi, Mom! 

上面的方法传递了一个准备好发布insult的块,但是由于该方法从不调用该块,所以只打印好的消息。 以下是我们如何从一个方法调用块:

 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? 

这只是一个惯例(也是一个好的,你想支持的),以便将刚刚创build的实例传递给块。

这不是一个详尽的答案,因为它不包括捕获块作为参数,它们是如何处理arity,在块参数中是否有溅落等,而是打算用作Blocks-Are-Lambdas的介绍。

Ruby块是创buildProc对象的一种方式,代表可以被其他代码使用的代码。 Proc对象是大括号{}之间的指令(或者do...end多行块的do...end短语,优先级低于大括号),可以select带参数和返回值(例如{|x,y| x+y} ) 。 Procs是一stream的对象 ,可以显式构build,也可以作为方法伪参数隐式获得:

  1. 构造为Proc对象(或使用lambda关键字):

     add1 = Proc.new {|x| x+1} # Returns its argument plus one. add1.call(1) # => 2 
  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. 

第二种forms通常用于访问者模式 ; 数据可以作为callyield方法的parameter passing给特殊的块参数。

从为什么(凄美)指南到ruby :

任何由花括号包围的代码都是块。

2.times { print "Yes, I've used chunky bacon in my examples, but never again!" } 2.times { print "Yes, I've used chunky bacon in my examples, but never again!" }是一个例子。

通过块,您可以将一组指令组合在一起,以便它们可以在程序中传递。 curl的大括号给出了抓取代码的蟹钳的外观,并把它们放在一起。 当你看到这两个钳子时,请记住里面的代码已被压入一个单位。 这就像是在商场里卖的那种小巧的Hello Kitty盒子,里面塞满了小小的铅笔和微型纸,都塞满了闪闪发亮的透明盒子,可以隐藏在你的手掌里面进行隐蔽的固定操作。 除了块不需要太多的眯眼。 花括号也可以交易为单词do和end,如果你的单元比单行长,这个单词很好。

 loop do print "Much better." print "Ah. More space!" print "My back was killin' me in those crab pincers." end 

块参数是由pipe道字符围绕的一组variables,并用逗号分隔。

 |x|, |x,y|, and |up, down, all_around| are examples. 

块参数在块的开始处使用。

 { |x,y| x + y } 

在上面的例子中,| x,y | 是争论。 在争论之后,我们有一些代码。 expression式x + y将两个参数一起添加。 我喜欢把pipe道字符想象成一个隧道。 他们给出了一个滑道,variables滑落的外观。 (一个x向下散布的老鹰,而y整齐地跨过她的双腿。)这个滑道充当了积木和周围世界之间的通道。 variables通过此滑槽(或隧道)传递到块中。

对于从C#背景(或其他语言)来的人来说,这可能会有所帮助:

Ruby块就像在C#中的lambdaexpression式和匿名方法。 它们是C#调用委托(和Ruby调用Proc)的东西,也就是说它们本质上是可以作为值传递的函数。 在Ruby和C#中,它们也可以performance为closures。

Ruby: { |x| x + 1 } { |x| x + 1 }

C#: x => x + 1

Ruby: { |name| puts "Hello there #{name}" } { |name| puts "Hello there #{name}" }

C#: name => { Console.WriteLine("Hello there {0}", name); } name => { Console.WriteLine("Hello there {0}", name); }

C#和Ruby都提供了替代方法来编写上面的例子。

ruby:

 do |name| puts "Hello there #{name}" end 

C#:

 delegate(string name) { Console.WriteLine("Hello there {0}", name); } 

在Ruby和C#中,允许多个语句,在Ruby中,上面的第二个语法是必需的。

这些概念在许多受函数编程背后思想影响的其他语言中都是可用的。

“ Programming Ruby ”这本书有一个很好的解释块和使用它们 。

在1.9+中,传入块的参数列表变得更复杂,可以定义局部variables:

 do |a,b;c,d| some_stuff end 

;c,d在块内部声明两个新的局部variables,它们不会从调用例程的yield语句中获得值。 Ruby 1.9+保证,如果variables存在于块之外,那么它们不会被块内的同名variables所踩踏。 这是新的行为; 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”中被捕获,这将被视为一个数组。 *可以在avariables上:

 do |*a,b| some_stuff end 

会捕获除了最后一个variables传递给variablesb所有variables。 而且,与之前的两个类似:

 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中分组代码的方法。 有两种写块的方法。 一个是使用do..end语句,另一个是使用大括号括住代码:{}。 块被认为是Ruby编程语言中的对象,默认情况下,所有的函数都接受一个隐含的块参数。

下面是两个块的例子,做同样的事情:

 2.times {puts'hi'}
 2次
  放'嗨'
结束

块可以接收竖线中的逗号分隔参数列表||。 例如:

 [1,2] .map {| n |  n + 2}#[3,4]

块(在ruby1.9.2)可以显式地有局部variables:

 x ='你好'
 2.x do | x |
   x =“世界”
  放x
结束

 =>世界
 =>世界

局部variables可以与参数组合在一起:

 [1,2] .map {| n; x |  n + 2}

所有函数都可以接收一个默认的块参数:

 def两次
  产量
  产量
结束

两次{放'你好'}
 =>你好
 =>你好

do..end和{}块之间有什么区别? 按照惯例{}块在一行中,do..end块跨越多行,因为它们每个都更容易阅读。 主要的区别在于:

 array = [1,2]

放入array.map {| n |  n * 10}#puts(array.map {| n | n * 10})
 => 10
 => 20

放入array.map do | n |  n * 10 end#(放入array.map)do | n |  n * 10结束
 => <Enumerator:0x00000100862670>

块是一些烦人的限制匿名头等过程的轻量级文字。 他们在Ruby中的工作方式与几乎所有其他编程语言的工作方式相同,都是以上述的限制为基础的:

  • 块只能出现在参数列表中
  • 最多一个块可以出现在参数列表中(并且它必须是最后一个参数)