在ruby中执行循环,将块中的值设置为nil

在ruby中执行循环,将块中的值设置为nil,ruby,loops,Ruby,Loops,我有以下方法(d方法类似于puts——使用log\u buddy): 输出如下所示: response_array.count = 2 i = 0 return_response = "this is the return response" i = 1 return_response = nil 问题:当第二次循环i=1时,return\u response的值为零。我想不明白为什么。TL;DR块在每次迭代时重置其作用域。您可以在进入循环之前执行return\u response=nil,它

我有以下方法(d方法类似于puts——使用log\u buddy):

输出如下所示:

response_array.count = 2
i = 0
return_response = "this is the return response"
i = 1
return_response = nil

问题:当第二次循环i=1时,return\u response的值为零。我想不明白为什么。

TL;DR块在每次迭代时重置其作用域。您可以在进入循环之前执行
return\u response=nil
,它应该可以解决您的问题,因为它不会在每次迭代中都重置

这是一个非常有趣的问题,因为输出有点误导。我做了更多的研究,因为我不喜欢我的答案,这就是我想到的。(仅供参考,我在Ruby 2.1.1上运行了所有这些)

可变存在 在Ruby中,单个作用域中的变量存在由解析器确定。这里有一个简单的例子:

if false
  fnord = 42
end
fnord # => nil
与此相反(您必须重新加载irb或以其他方式重置范围)

这是一个有趣的行为,我强烈建议阅读上面链接的堆栈溢出答案以了解更多细节

块范围 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
,因为x已被解析(因此您不会得到上面的名称错误),但尚未分配,所以您将连续五次得到nil打印。您可以在块范围之外定义x来保持值(甚至可以让解析器在块范围之外解析x,而不实际执行赋值!)

这将导致零,然后20,20,20,20。在这种情况下,
x
的范围在块范围之外(块可以访问其周围范围内的局部变量)。即使您使用
if false
机制“声明”x而不分配它,它仍然可以工作

因此,在您的情况下,问题不在于return\u响应的作用域是
if/else
语句的
if
部分,而是它在第二次时没有被赋值(但它确实被解析,因此它的值为nil,而不是引发NameError)。让我们稍微简化一下您的情况:

(0..1).each do |i|
  if i == 0
    x = 10
    p x
  else
    p x
  end
end
这会打印10,然后是零,这反映了您遇到的问题。您无法通过在if语句之前“声明”变量来修复它,但仍然在块范围内(或通过使用,这将做同样的事情):

这仍然打印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\u响应。您可以在进入循环之前执行
return\u response=nil
,它应该可以解决您的问题,因为它不会在每次迭代中都重置。一种更像Ruby的方法是在进入循环并追加每条记录之前分配
return\u response=[]
(在这种情况下,不需要检查i==0)。如果您不需要中间日志记录,那么您可能不需要这样做

return_response = response_array.map { |response_line| response_line[:value]['value'] }.join("X-LINE-BREAK.")
也就是说,将响应数组中的每个项目映射到其
[:value][value']
,然后将所有项目连接到字符串中,并在每个项目之间加上
“X-LINE-BREAK.”

PS:这两个答案还提供了关于块作用域、变量和循环的其他特性的一些信息,尽管它们不适用于您的特定情况:和


我之前在这里写的答案不正确,所以我把它替换了。如果这是错误的方法,请让我知道。

对于与
log\u buddy
不相似的读者,文档是。当里面有两条语句时,应该如何操作
d{}
?如果您使用
d{return\u response}
d{response\u array[i][:value]['value']}
,是否会有所不同;如果由
分隔它像单独的行一样运行它们。感谢指向文档的链接,这有助于理解多个语句。我很清楚,return_response在else语句之后是nil,除非它在循环之外声明。我想知道是否还有其他打印出来的东西。在else语句中,我希望返回\u response+“X-LINE-BREAK”。。。抛出一个NoMethodError,因为return_响应为nil。当我键入行IRB时,它会按预期工作。我不知道为什么必须在循环中声明它。有人能解释一下吗?嗯,这很有趣。因此,如果在if语句中声明了某个内容,它仍然是本地的?您的示例肯定显示了相同的行为。是的,使用数组然后将数组映射到单个字符串是您编写的wht中的正确方法,我将尝试它!在短期内,我做了您所做的事情,即将其移出作用域块,但是枚举是错误的,因此需要-1(因为[0]数组已经分配!@Angela很乐意提供帮助。是的,请尝试将响应初始化为空数组,然后重试
(0..1).each do |i|
  if i == 0
    x = 10
    p x
  else
    p x
  end
end
(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
x = nil # outside the block scope
(0..1).each do |i| 
  if i == 0
    x = 10
    p x
  else
    p x
  end
end
return_response = response_array.map { |response_line| response_line[:value]['value'] }.join("X-LINE-BREAK.")