ruby - 在这个 for/each 语句中,为什么我需要在我正在迭代的对象的类中使用 &block 指定一个 each 方法?
问题描述
这个标题不是最好的,所以我将在这里进一步解释。
我有一个 Card 类和一个 Deck 类,在 Main 中,我正在创建一个 Deck of Cards,然后打印出 Deck 中的所有卡片。我为 Card 制作了一个 to_string 方法(如下):
def to_string
puts "#{self.rank} of #{self.suit}"
end
然后使用它和 Main 中的 for/each 语句打印 Deck 中的所有卡片:
for card in deck
puts card.to_string
end
但是我收到一个错误,说#Deck 有一个“未定义的方法'each':(NoMethodError)。我做了一些搜索,发现解决方案是将这个方法添加到我的Deck 类中:
def each(&block)
@deck.each(&block)
end
我确实理解(或者,我想我知道)它是如何.each
工作的,因为我用它来为我的 Deck 创建所有 Card 对象——它将遍历一个数组并依次获取每个数组项。所以for card in deck
基本上是有道理的deck.each
。但我不确定&block
这里在做什么。我对什么是块进行了一些研究(据我了解,基本上是“匿名代码”——例如,array.each
语句中的指令。一种不是正式书面方法的方法)但我仍然不知道是什么&block
本身就可以。
解决方案
Ruby 中的每个方法都可以(但不是必须)采用可选的块参数,&
在参数名称前用 & 符号表示。当方法被调用时,这个参数被赋予一个显式块的值。
我们可以通过编写一个简单地返回其块的函数来明确地看到这种行为。
def foo(&block)
p block
end
那么如果我们运行
> foo() # Note: No block argument
nil
> foo { 1 } # Note: Block provided
#<Proc:0x...>
因此,如果您将显式块传递给方法,它将作为Proc
对象传递给方法。这是如何.each
工作的。线
[1, 2, 3].each { |x| puts x }
调用each
,[1, 2, 3]
传递其call
方法运行的块参数puts x
。你可以认为它类似于
[1, 2, 3].each(->(x) { puts x })
在这里,我们传递了一个恰好是 lambda 的常规参数。这不等同于上述(块参数被视为特殊),但理论上我们可以实现each
任何一种方式;块语法更方便。
正如您正确推测的那样,for
循环脱糖到类似.each
. 粗略地说,以下两个是等价的。
for i in [1, 2, 3]
foo i
end
i = nil
[1, 2, 3].each do |i|
foo i
end
请注意,for
循环实际上会付出额外的努力来确保变量确实逃脱了循环范围,而.each
直接使用会产生一个不会转义的仅限本地变量。出于这个原因,.each
无论如何,它通常被认为在 Ruby 中更惯用,并且大多数 Ruby 代码通常会避开显式for
循环。(IMO.each
也更漂亮,更符合 Ruby 语法的所有其他方面)
推荐阅读
- sas - SAS调用宏时内存不足
- php - 您可以在不创建新属性的情况下过滤检索到的关系上的数据吗?
- r - 如何在 R 中创建函数以从全局环境中删除所有对象,除了默认值和作为参数传递的对象
- sql - 在单个查询中选择多个列作为 JSON
- wolfram-mathematica - 如何定义符号时间序列/序列?
- c# - 通过 RESTFUL API 传输大型数据集?
- javascript - 在 Firestore 的文本字段中存储文本文件会删除换行符
- javascript - setTimeout 与 0ms 与在我的扩展程序的 popup.html 中不使用 setTimeout 之间的区别
- python - 向应用程序工厂注册烧瓶管理视图的正确方法
- android - Android for Intent 上的路径