我有以下内容(' d'方法就像' puts' - 使用log_buddy):
response_array.count.times do |i|
d {i}
if i == 0
return_response = response_array[i][:value]['value']
d {return_response}
else
d {return_response; response_array[i][:value]['value']}
return_response = return_response + "X-LINE-BREAK." +
response_array[i][:value]['value']
d {return_response}
end
end
输出如下:
response_array.count = 2
i = 0
return_response = "this is the return response"
i = 1
return_response = nil
问题:循环第二次i = 1时,return_response的值为nil。我无法思考为什么。
答案 0 :(得分:2)
TL; DR块在每次迭代时重置其范围。您可以在进入循环之前执行return_response = nil
,它应该可以解决您的问题,因为它不会在每次迭代时重置。
这是一个非常有趣的问题,因为输出有些误导。我做了一些更多的研究,因为我不喜欢我的答案,这就是我想出的。 (仅供参考,我在Ruby 2.1.1上运行了所有这些)
在Ruby中,解析器确定单个作用域中的变量存在。有一个really good explanation here,但举个简单的例子:
if false
fnord = 42
end
fnord # => nil
与此相反(您必须重新加载irb或重置范围)
fnord # => NameError: undefined local variable or method `fnord' for main:Object
if false
fnord = 42
end
这是一个有趣的行为,我强烈建议您阅读上面链接的堆栈溢出答案以获取更多详细信息。
Ruby中的块在每次迭代时重置其本地范围。
5.times do
# This "if false" is here because otherwise the scope reset would cause x
# to be a NameError, as above. If you delete the following 3 lines it
# will raise that error when running "p x".
if false
x = 10
end
p x
x = 20
end
永远不会保留x = 20
- 你连续五次打印nil,因为x已被解析(所以你不会得到上面的NameError)但它没有&# 39;被分配。您可以在块作用域之外定义x以保留该值(或者甚至让解析器在块作用域之外解析x,而不实际进行赋值!)
x = nil
5.times do
p x
x = 20
end
这将导致nil,然后是20,20,20,20。在这种情况下x
的范围超出了块范围(并且块可以访问它们周围范围内的局部变量)。即使您使用if false
机制来声明"声明" x没有分配它,它仍然可以工作。
因此,对于您的情况,问题不在于return_response的范围限定为if
语句的if/else
部分,它不是't}所有第二次分配(但它确实被解析,因此它的值为nil,而不是引发NameError)。让我们稍微简化一下你的情况:
(0..1).each do |i|
if i == 0
x = 10
p x
else
p x
end
end
这打印10,然后是nil,这反映了你遇到的问题。你无法通过"声明"来解决问题。 if语句之前的变量,但仍然在块作用域内(或使用block-local variables,这将做同样的事情):
(0..1).each do |i|
x ||= nil # note: this is not something you'd probably do in Ruby
if i == 0
x = 10
p x
else
p x
end
end
这仍然打印10,然后是零。您必须将变量移动到块范围之外,以使其不会每次都重置。
x = nil # outside the block scope
(0..1).each do |i|
if i == 0
x = 10
p x
else
p x
end
end
这将打印10,10。
从您的代码看起来,您希望在迭代时从每一行构建return_response。您可以在进入循环之前执行return_response = nil
,它应该可以解决您的问题,因为它不会在每次迭代时重置。更类似Ruby的方法是在进入循环之前分配return_response = []
并附加每条记录(在这种情况下你不需要检查i == 0)。如果你不需要中间日志记录,你可能会放弃这个
return_response = response_array.map { |response_line| response_line[:value]['value'] }.join("X-LINE-BREAK.")
即。将响应数组中的每个项目映射到其[:value]['value']
,然后将所有这些项目连接到每个项目之间的"X-LINE-BREAK."
字符串。
PS:这两个答案还提供了一些关于块作用域,变量和循环的其他特性的信息,尽管它们不适用于您的特定情况:https://stackoverflow.com/a/1654665/4280232和https://stackoverflow.com/a/21296598/4280232。< / p>
我在这里写的上一个答案是不正确的,所以我更换了它。如果这是错误的方法,请告诉我。