我看到的大多数示例都是微不足道的,我不确定这会给语言增加什么。我明白为什么你会在 Objective-C 中使用块来处理回调或在 Javascript 中使用闭包作为计时器的回调,但我不确定在 Ruby 中我将使用块来做什么。是否可以使用它来代替具有类似功能的功能inject
?
2 回答
Ruby 到处都使用块,这是该语言最普遍的特性之一。它基本上是函数即值的优化案例。一个真正的函数对象,就像在 JavaScript 中一样,在 Ruby 中是一个Proc
类的实例。块不是对象,因此避免了对象性产生的一些开销。原因是,在几乎所有你想将函数作为参数传递的情况下,你只会传递一个,并且你想当场定义它,所以为什么不优化,让事情成为语法上更好一点?
这是一个块:
[1, 2, 3].each do |i|
puts i
end
这是一个函数:
func = Proc.new do |i|
puts i
end
[1, 2, 3].hypothetical_each(func)
您可以看到,即使定义一个函数值也涉及一个块(作为Proc
构造函数的参数)。
您通常在 中使用块inject
,而不是函数。您几乎在 Ruby 中的每个循环结构中都使用它(while
非常不常用)。
您还可以使用块来构建特定领域的语言,例如 Sinatra:
get 'index.html' do
"Hello World"
end
或 Rspec:
describe "addition" do
it "should add two numbers together" do
(1+2).should equal(3)
end
end
学好积木,因为没有积木你就不能读,也不能写好 Ruby。
ruby 中块的秘密在于 ruby 设计的本质。
Ruby 设计有一个对象模式,其中方法调用作为“消息”发送到对象,返回值是响应。在 ruby 中(与 python 和其他一些语言不同),函数和方法本身不是对象。块是 ruby 在此模式中允许类似函数的消息的方式,并且是 ruby 通常如何处理迭代的关键。
如果我错了,请有人纠正我,但我相信实际上不可能在不创建块或使用 while 语句的情况下在 ruby 中进行任何迭代,实际上该for i in object
符号只是访问对象each
方法的语法简写。
IE。
for i in 1..5
#do stuff with i
end
执行完全一样:
1..5.each do |i|
#do stuff with i
end
通过 ruby VM,其中 Range 对象1..5
(包括 ruby 标准 lib Enumerable 模块)将其元素生成给传递的块
在引擎盖下,这个操作看起来像这样(注意这是一个简化版本,没有考虑到范围的 ... 独占和 .. 包含版本)
class Range
def init(start, end)
@start = start
@end = end
end
def each
i = @start
while i < @end
yield i
i += 1
end
end
end
因此,当调用该 for 循环时,它会创建一个范围对象obj = Range.new(1,5)
,然后向该对象发送一条消息,obj.send(:each, &block)
并在每个方法内部使用一个 while 循环并屈服于块
TL;博士
Ruby 在内部使用块,因为它们对它的设计至关重要,没有它们,语言本身将无法使用它的message => object => response
模态。
所以你不应该问:“我为什么要使用积木?” 因为没有它们就无法编写 ruby 程序。
相反,您应该问:“ruby 中的块与 Objective-C 和 Javascript 中的块有何不同?” 因为他们是不同的。
我不能为 Objective-C 说话,但在 JavaScript 中,块在很大程度上是一个对象。它是代码的容器,其他对象可以获取并使用它来做任何需要的事情,块甚至不必与使用它的对象相关联。
在 ruby 中,一个块更像是一个对象在邮件中收到的一组指令,它在它自己上使用(然后在他下次来的时候传递给邮件人)