在Ruby中可以声明块级变量吗?

gz5pxeao  于 2023-05-28  发布在  Ruby
关注(0)|答案(2)|浏览(132)

我是Ruby的新手,正在尝试理解Ruby Blocks。
有没有办法在下面的代码块中包含i,或者像下面这样在代码块之外声明它?

i = 0
  nums.each_with_index do |num, j|
    if num != 0
      nums[i], nums[j] = num, nums[i]
      i += 1
    end
  end

我尝试将i初始化为一个块变量,但它不起作用。

nums.each_with_index do |num, j; i = 0|
yhxst69z

yhxst69z1#

有一个技巧可以让i本地化到块,而不是在迭代之外“泄漏”:

result  = [6, 7, 8].each_with_index.inject(42) do |i, (item, index)|
  i + item + index
end
puts result
# 66

这个人为的例子定义了您的i(从42开始只是为了举例,但您可能更喜欢0)。并迭代一个包含索引的项数组,然后求和。
each_with_index将返回一个Enumerator,您可以在其上调用inject(也称为reduce),它接受一个初始值和一个块,并传递称为accumulator的值(在本例中基本上是i,可以使用此变量来累积结果)作为此块的第一个参数,你可以用它把一个集合中的项转换成一个值。
我不认为有任何其他的方法来做到这一点。在一个块内部定义的变量只对一个块的那个 * 调用 * 可见(所以对于每个迭代,整个块都被执行,所以如果你执行i = 0,那么每个项目都是0
我相信,如果您不想在块外声明变量,我对inject的建议将是一个惯用的解决方案。
所以,你的例子看起来像这样(请注意,我不能测试它,因为你的例子是不完整的,即。你不指定nums是什么)

nums.each_with_index.inject(0) do |i, (num, j)|
    if num != 0
      nums[i], nums[j] = num, nums[i]
      i + 1 # you can think of the last line in the block as `return` 
      # and the value becomes `i` in the next iteration, or is 
      # returned as a result of calling `inject` method if it was the 
      # last iteration
    end
    i # This will be what block returns, and current `i` value will be carried over to the next block iteration (or returned if it was the last one)  
  end
fiei3ece

fiei3ece2#

您希望在修改nums后,保持i的值不被窥探。
一个简单(但荒谬)的解决方案是将代码封装在一个只执行一次的块中。

nums = [1, 2, 0, 3]
["hey, diddle, diddle,"].each do
  i = 0
  nums.each_with_index do |num,j|
    if num != 0
      nums[i], nums[j] = num, nums[i]
      i += 1
    end
  end
end
  #=> 3
nums
  #=> [1, 2, 3, 0]
i #=> NameError: undefined local variable or method `i' for main:Object

您应该简单地将代码封装在一个方法中,而不是采用扭曲的方法来隐藏i的值。

def doit(nums)
  i = 0
  nums.each_with_index do |num,j|
    if num != 0
      nums[i], nums[j] = num, nums[i]
      i += 1
    end
  end
end
doit [1, 2, 0, 3]
  #=> [1, 2, 3, 0]
nums
  #=> [1, 2, 3, 0]

相关问题