简体   繁体   English

Ruby for循环陷阱?

[英]Ruby for loop a trap?

In a discussion of Ruby loops, Niklas B. recently talked about for loop 'not introducing a new scope', as compared to each loop. 在对Ruby循环的讨论中,与每个循环相比, Niklas B.最近谈到了循环“不引入新范围”。 I'd like to see some examples of how does one feel this. 我想看一些人们如何看待这个问题的例子。

OK, I expand the question: Where else in Ruby do we see what apears do/end block delimiters, but there is actually no scope inside? 好吧,我扩展了一个问题:在Ruby的其他地方我们看到了什么apears做/结束块分隔符,但实际上里面没有范围? Anything else apart from for ... do ... end? 还有什么别的...做...结束?

OK, One more expansion of the question, is there a way to write for loop with curly braces { block } ? 好的,问题的另一个扩展,是否有办法用花括号{block}编写循环?

Let's illustrate the point by an example: 让我们通过一个例子说明这一点:

results = []
(1..3).each do |i|
  results << lambda { i }
end
p results.map(&:call)  # => [1,2,3]

Cool, this is what was expected. 很酷,这是预期的。 Now check the following: 现在检查以下内容:

results = []
for i in 1..3
  results << lambda { i }
end
p results.map(&:call)  # => [3,3,3]

Huh, what's going on? 嗯,发生了什么事? Believe me, these kinds of bugs are nasty to track down. 相信我,这些类型的错误是令人讨厌的追踪。 Python or JS developers will know what I mean :) Python或JS开发人员会知道我的意思:)

That alone is a reason for me to avoid these loops like the plague, although there are more good arguments in favor of this position. 仅仅这一点是我避免像瘟疫这样的循环的原因,尽管有更多有利于这一立场的好论据。 As Ben pointed out correctly, using the proper method from Enumerable almost always leads to better code than using plain old, imperative for loops or the fancier Enumerable#each . 正如Ben指出的那样,使用Enumerable的正确方法几乎总是会导致更好的代码,而不是使用普通的旧的,命令式的for循环或者有趣的Enumerable#each For instance, the above example could also be concisely written as 例如,上面的例子也可以简洁地写成

lambdas = 1.upto(3).map { |i| lambda { i } }
p lambdas.map(&:call)

I expand the question: Where else in Ruby do we see what apears do/end block delimiters, but there is actually no scope inside? 我扩展了一个问题:在Ruby的其他地方我们看到了什么apears做/结束块分隔符,但实际上里面没有范围? Anything else apart from for ... do ... end? 还有什么别的...做...结束?

Every single one of the looping constructs can be used that way: 每个循环结构都可以这样使用:

while true do
  #...
end

until false do
  # ...
end

On the other hand, we can write every one of these without the do (which is obviously preferrable): 另一方面,我们可以在没有do情况下编写其中的每一个(这显然是可取的):

for i in 1..3
end

while true
end

until false
end

One more expansion of the question, is there a way to write for loop with curly braces { block } 问题的另一个扩展是,有一种方法可以用花括号{block}来编写循环

No, there is not. 不,那里没有。 Also note that the term "block" has a special meaning in Ruby. 另请注意,术语“块”在Ruby中具有特殊含义。

First, I'll explain why you wouldn't want to use for , and then explain why you might. 首先,我会解释为什么你不希望使用for ,然后解释为什么你可能。

The main reason you wouldn't want to use for is that it's un-idiomatic. 你不希望使用的主要原因for是它的非习惯。 If you use each , you can easily replace that each with a map or a find or an each_with_index without a major change of your code. 如果您使用each ,您可以轻松地将each替换为mapfindeach_with_index而无需对代码进行重大更改。 But there's no for_map or for_find or for_with_index . 但是没有for_mapfor_findfor_with_index

Another reason is that if you create a variable within a block within each , and it hasn't been created before-hand, it'll only stay in existance for as long as that loop exists. 另一个原因是,如果你在each块中的块内创建一个变量,并且它之前没有创建过,那么只要该循环存在,它就会保持存在。 Getting rid of variables once you have no use for them is a good thing. 一旦你没有使用它们就摆脱变量是一件好事。

Now I'll mention why you might want to use for . 现在我会提到你可能想要使用for each creates a closure for each loop, and if you repeat that loop too many times, that loop can cause performance problems. each循环each创建一个闭包,如果重复该循环次数过多,那么该循环可能会导致性能问题。 In https://stackoverflow.com/a/10325493/38765 , I posted that using a while loop rather than a block made it slower. 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 consistently took 2.4 seconds, while loop_double_equal_sign was consistently 0.2 to 0.3 seconds faster. times_double_equal_sign始终持续2.4秒,而loop_double_equal_sign持续0.2至0.3秒。

In https://stackoverflow.com/a/6475413/38765 , I found that executing an empty loop took 1.9 seconds, whereas executing an empty block took 5.7 seconds. https://stackoverflow.com/a/6475413/38765中 ,我发现执行空循环需要1.9秒,而执行空块需要5.7秒。

Know why you wouldn't want to use for , know why you would want to use for , and only use the latter when you need to. 知道你为什么会不希望使用for ,知道你为什么会想用for ,只有当你需要使用后者。 Unless you feel nostalgic for other languages. 除非你对其他语言怀有怀旧情绪。 :) :)

Well, even blocks are not perfect in Ruby prior to 1.9. 好吧,在1.9之前的Ruby中,偶数块也不完美。 They don't always introduce new scope: 他们并不总是引入新的范围:

i = 0
results = []
(1..3).each do |i|
  results << lambda { i }
end
i = 5
p results.map(&:call)  # => [5,5,5]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM