英文:
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's a trick to have `i` local to the block, and not "leak" 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]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论