Ruby 块的最佳解释? [关闭]
Posted
技术标签:
【中文标题】Ruby 块的最佳解释? [关闭]【英文标题】:Best explanation of Ruby blocks? [closed] 【发布时间】:2011-06-22 03:11:48 【问题描述】:您可以分享的关于 Ruby 块的最佳解释是什么?
使用和编写可以占用块的代码?
【问题讨论】:
您是在寻找有关块概念的介绍,还是对它们的详尽参考? 或者您只是通过提出您不需要答案、不打算接受、甚至不打算参与讨论的问题来获取代表?我们会看看你是否回复。 这是一个有用的线程:reactive.io/tips/2008/12/21/… 【参考方案1】:Ruby 块是一种创建Proc
objects 的方法,它表示可以被其他代码使用的代码。 Proc 对象是大括号 (或多行块的
do...end
短语,其优先级低于大括号)之间的指令,可以选择接受参数和返回值(例如 |x,y| x+y
)。 Procs 是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.
第二种形式通常用于Visitor patterns;数据可以作为call
或yield
方法的参数传递给特殊的块参数。
【讨论】:
大括号优先级高;do
的优先级较低。如果方法调用具有未包含在括号中的参数,则块的大括号形式将绑定到最后一个参数,而不是整个调用。 do
表单将绑定到调用。
请用英文! ......“Ruby 块是 Proc 对象的语法文字......” - 如果人们不知道块是什么,我猜他们也不知道“Proc 对象的语法文字”是什么意思.试着把读者当成 5 岁来解释。
什么是语法字面量?
@DerrickMar:我所说的“语法文字”本质上是指“最基本意义上的语言标记的排列”。例如,在 javascript 中,字符序列/\d+/
是一个语法字面量,意思是“匹配一个或多个数字的正则表达式”。类似地,块是在 Ruby 中定义过程的一种直接方式,它可以作为一等对象(来自 Ruby 中的其他方法和过程的参数和返回值)传递。【参考方案2】:
对于任何从 C# 背景(或其他语言)来回答这个问题的人,这可能会有所帮助:
Ruby 块类似于 C# 中的 lambda 表达式和匿名方法。它们是 C# 所谓的委托(而 Ruby 称为 Procs),也就是说,它们本质上是可以作为值传递的函数。在 Ruby 和 C# 中,它们也可以表现为闭包。
鲁比: |x| x + 1
C#:x => x + 1
鲁比: |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 中,需要上面的第二种语法。
这些概念在受函数式编程背后的思想影响的许多其他语言中可用。
【讨论】:
【参考方案3】:我提供了来自this answer的自己的解释,稍作修改:
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 中可以通过多种方式实现。其中一种方式是“块”。
Ruby 中的 Block 非常非常像一个方法:它可以接受一些参数并为这些参数运行代码。每当您看到foo |x,y,z| ...
或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!
上面的方法传递了一个准备发出侮辱的块,但是由于该方法从不调用该块,因此只打印了 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?
对于某些类来说,将刚刚创建的实例传递给块只是一种约定(也是一个很好的约定,也是您想要支持的约定)。
这不是一个详尽的答案,因为它不包括将块捕获为参数、它们如何处理 arity、块参数中的不溅射等,但打算作为 Blocks-Are-Lambdas 的介绍。
【讨论】:
惊讶地看到,在 7 年多的时间里,只有 29 个(包括我的)对答案的支持。这个概念可以知道。但是你解释的方式,“KUDOS!”。绝对推荐给初学者。【参考方案4】:《Programming Ruby》这本书有一个很棒的explanation of blocks and using them。
在 1.9+ 中,传递到块中的参数列表变得更加复杂,允许定义局部变量:
do |a,b;c,d|
some_stuff
end
;c,d
在块内声明两个新的局部变量,它们不接收来自被调用例程的yield
语句的值。 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
。而且,和前两个类似:
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
【讨论】:
【参考方案5】:块是用于匿名一流过程的轻量级文字,具有一些令人讨厌的限制。它们在 Ruby 中的工作方式与在几乎所有其他编程语言中的工作方式相同,以上述限制为模:
块只能出现在参数列表中 参数列表中最多可以出现一个块(并且必须是最后一个参数)【讨论】:
很好的答案,但与 Proc 对象的关系似乎很重要,不是吗? @maerics 对 Blocks 的详尽资源必不可少?是的。对块的解释必不可少(我将其解释为对新手的介绍)?绝对不是,IMO。 谢谢。你的答案是帮助我理解为什么puts "hello"
不起作用的唯一答案。根本不允许?这很奇怪。【参考方案6】:
块是 Ruby 中对代码进行分组的一种方式。有两种写块的方法。一种是使用 do..end 语句,另一种是用花括号括起来的代码:。在 Ruby 编程语言中,块被视为对象,默认情况下,所有函数都接受隐式块参数。
这里有两个做同样事情的块的例子:
2.times 放'嗨' 2.次做 打“嗨” 结尾块可以在竖线 || 内接收逗号分隔的参数列表。例如:
[1,2].map |n| n+2 # [3, 4]块(在 ruby 1.9.2 中)可以显式地具有局部变量:
x = '你好' 2.次做|;x| x = '世界' 放 x 结尾 => 世界 => 世界局部变量可以与参数组合:
[1,2].map |n;x| n+2所有函数都可以接收默认块参数:
定义两次 屈服 屈服 结尾 两次放'你好' => 你好 => 你好do..end 和 块有什么区别?按照惯例 块在一行上,而 do..end 块跨越多行,因为这样更容易阅读。主要区别在于优先级:
数组 = [1,2] 放数组.map |n| n*10 # 放置 (array.map |n| n*10 ) => 10 => 20 puts array.map 做 |n| n*10 end # (puts array.map) do |n| n*10 结束 =>【讨论】:
【参考方案7】:来自Why's (poignant) guide to ruby:
任何用大括号括起来的代码都是 一个块。
2.times print "Yes, I've used chunky bacon in my examples, but never again!"
就是一个例子。使用块,您可以将一组 一起指示,以便他们可以 在你的程序中传递。这 花括号给出的外观 抓过的蟹钳 代码并将其保持在一起。什么时候 你看到这两个钳子,记住 里面的代码已经被按下 成一个单元。这就像其中之一 那些Hello Kitty的小盒子 在塞满了的商场卖 小铅笔和显微纸, 都挤进一个闪闪发光的 可隐藏的透明外壳 在您的手掌中进行隐蔽固定 操作。除了块不 需要这么多的眯眼。卷曲的 牙套也可以换成 词做和结束,如果 你的块长于一行。
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 将两个参数加在一起。一世 喜欢想到管道字符 作为代表隧道。他们给 滑槽的外观 变量正在下滑。 (一个 x 去 向下展开鹰,而y整齐 交叉双腿。)这个溜槽充当 街区之间的通道 他们周围的世界。变量是 通过这个溜槽(或隧道) 进入区块。
【讨论】:
“任何被花括号包围的代码都是一个块”,除非它是一个散列。 您没有解释这些示例返回的内容。我不明白。 请做我的导师!感谢您以如此简单明了的方式解释它。以上是关于Ruby 块的最佳解释? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
在 foreach 循环中使用 try-catch 块的最佳做法是啥? [关闭]
iOS ViewController 生命周期最佳实践 [关闭]
用于读取图像内文本的最佳 Python/Ruby 库 [关闭]