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,也可以作为方法伪参数隐式获得:
-
构造为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.
第二种forms通常用于访问者模式 ; 数据可以作为call
或yield
方法的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”中被捕获,这将被视为一个数组。 *
可以在a
variables上:
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中的工作方式与几乎所有其他编程语言的工作方式相同,都是以上述的限制为基础的:
- 块只能出现在参数列表中
- 最多一个块可以出现在参数列表中(并且它必须是最后一个参数)