在讨论 Ruby 循环时,Niklas B.最近谈到了与每个循环相比的 for 循环“不引入新范围”。我想看看一些例子,说明人们对此有何感受。
好的,我扩展这个问题:在 Ruby 中,我们还能在哪里看到 apears 的作用/结束块分隔符,但实际上里面没有范围?除了 for ... do ... end 还有什么?
好的,问题的另一个扩展,有没有办法用花括号 { block } 编写 for 循环?
让我们通过一个例子来说明这一点:
results = []
(1..3).each do |i|
results << lambda { i }
end
p results.map(&:call) # => [1,2,3]
酷,这是预期的结果。现在检查以下内容:
results = []
for i in 1..3
results << lambda { i }
end
p results.map(&:call) # => [3,3,3]
咦,怎么回事?相信我,这类错误很难追踪。Python 或 JS 开发人员会明白我的意思 :)
仅这一点就是我避免像瘟疫一样的循环的一个原因,尽管有更多好的论据支持这一立场。正如 Ben 正确指出的那样,使用正确的方法 fromEnumerable
几乎总是比使用普通的旧命令式for
循环或更高级的Enumerable#each
. 例如,上面的例子也可以简洁地写成
lambdas = 1.upto(3).map { |i| lambda { i } }
p lambdas.map(&:call)
我扩展了这个问题:在 Ruby 中,我们还能在哪里看到 apears 的作用/结束块分隔符,但实际上里面没有范围?除了 for ... do ... end 还有什么?
每一个循环结构都可以这样使用:
while true do
#...
end
until false do
# ...
end
另一方面,我们可以在没有 的情况下编写每一个do
(这显然是更可取的):
for i in 1..3
end
while true
end
until false
end
问题的另一个扩展,有没有一种方法可以用花括号 { block } 编写 for 循环
不,那里没有。另请注意,术语“块”在 Ruby 中具有特殊含义。
首先,我将解释为什么你不想使用for
,然后解释为什么你可能会。
您不想使用的主要原因for
是它不习惯。如果您使用each
,您可以轻松地将其替换each
为 amap
或 afind
或a ,each_with_index
而无需对代码进行重大更改。但是没有for_map
or for_find
or for_with_index
。
另一个原因是,如果您在 内的块中创建一个变量each
,并且之前没有创建它,那么只要该循环存在,它就会一直存在。一旦你对它们没有用处就摆脱它们是一件好事。
现在我会提到为什么你可能想要使用for
. each
为每个循环创建一个闭包,如果重复该循环太多次,该循环可能会导致性能问题。在https://stackoverflow.com/a/10325493/38765中,我发布了使用while
循环而不是块使其变慢的帖子。
RUN_COUNT = 10_000_000
FIRST_STRING = "Woooooha"
SECOND_STRING = "Woooooha"
def times_double_equal_sign
RUN_COUNT.times do |i|
FIRST_STRING == SECOND_STRING
end
end
def loop_double_equal_sign
i = 0
while i < RUN_COUNT
FIRST_STRING == SECOND_STRING
i += 1
end
end
times_double_equal_sign
始终花费 2.4 秒,而loop_double_equal_sign
始终快 0.2 到 0.3 秒。
在https://stackoverflow.com/a/6475413/38765中,我发现执行一个空循环需要 1.9 秒,而执行一个空块需要 5.7 秒。
知道为什么不想使用for
,知道为什么要使用for
,并且只在需要时使用后者。除非你对其他语言感到怀旧。:)
好吧,在 1.9 之前的 Ruby 中,即使是块也不完美。他们并不总是引入新的范围:
i = 0
results = []
(1..3).each do |i|
results << lambda { i }
end
i = 5
p results.map(&:call) # => [5,5,5]