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

huangapple go评论53阅读模式
英文:

Can one declare block-level variables in Ruby?

问题

以下是您要翻译的内容:

"I am new to Ruby and trying to understand Ruby Blocks.

Is there a way to include i in the following block or does it have to be declared outside the block like I did below?

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

I tried initializing i as a block variable but it didn't work.

  nums.each_with_index do |num, j; i = 0|
英文:

I am new to Ruby and trying to understand Ruby Blocks.

Is there a way to include i in the following block or does it have to be declared outside the block like I did below?

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

I tried initializing i as a block variable but it didn't work.

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

答案1

得分: 1

有一个技巧可以使 `i` 局部于这个块内,而不会“泄漏”到迭代之外:

```ruby
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 将返回一个枚举器,你可以在其上调用 inject(也称为 reduce),它接受一个初始值和一个块,并将所谓的 累加器 值(在你的情况下基本上就是 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 # 你可以将块中的最后一行视为 `return` 
      # 并且该值将成为下一次迭代中的 `i`,或者在最后一次迭代时作为调用 `inject` 方法的结果返回
    end
    i # 这将是块返回的内容,当前的 `i` 值将被传递到下一个块迭代中(如果它是最后一个迭代,也将被返回)
  end

<details>
<summary>英文:</summary>

There&#39;s a trick to have `i` local to the block, and not &quot;leak&quot; outside of the iteration: 

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

This contrived example defines your i (starting from 42 just for example purposes, but you'd probably prefer 0). And iterates over an array of items with their indexes, and sums it up.

each_with_index will return an Enumerator, on which you can call inject (which is also known as reduce) which takes an initial value and a block, and passes what is called accumulator value (basically i in your case, a variable you can use to accumulate results) as a first argument of this block, which you can use to reduce a collection of items to a single value.

I don't think there's any other way to do it. Variables defined inside of a block are visible only to that invocation of a block (so for each iteration, the whole block is executed so if you do i = 0 it will be 0 for each item.

I believe what I suggested with inject would be an idiomatic solution if you don't want to declare a variable outside of the block.

So, your example would look something like this (please note that I could not test it, because your example is incomplete, i.e. you don't specify what nums is)

  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

答案2

得分: 1

你想要在修改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]
英文:

You want to keep the value of i away from prying eyes after nums is modified.

An easy (but absurd) solution is to enclose the code in a block that is executed once.

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


Rather than resorting to contortions to hide the value of i you should simply enclose your code in a method.

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]

huangapple
  • 本文由 发表于 2023年5月24日 21:22:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76324023.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定