我需要迭代数组并在某些条件下添加元素



你好,我需要在ruby中迭代数组并在此迭代期间添加元素。但是我有一个问题:假设数组的长度是6,如果我再添加一个元素,并且它不是迭代的结束,它不会遍历整个数组,因为每次执行条件时长度都会增加1,例如:在数组的所有2之间放置-1。

vet = [1,2,2,2,2,2]
for i in 0...vet.length do
if vet[i]==2
i+=1 #this is because the insert method always replace the element in i 
vet.insert(i,-1)
print "#{vet} n" # [1, 2, -1, 2, -1, 2, -1, 2, 2] there is still a pair without a -1 
# between them 
end
end

如何妥善解决?

作为一般规则,Ruby中从来没有需要使用循环。您应该总是倾向于使用更高级的迭代构造。此外,当您在数据结构上进行迭代时,永远不要改变它。(当然,如果你认识我,你就知道我是函数式编程的粉丝,因此我会说,永远不要改变数据结构)

下面是我解决这个问题的几个想法:

indices = vet.
each_cons(2).
with_index.
map {|pair, index| if pair == [2, 2] then index end }.
compact
indices.reverse_each {|index| vet.insert(index + 1, -1) }

这实际上改变了vet,并且在迭代它时这样做,这两件事都是我上面说过不要做的。然而,它是以一种安全的方式完成的,通过从后面的扩展数组,以便尚未处理的索引不会四处移动。

它还使用了"低水平"Enumerable#reverse_each迭代器,它实际上并不比循环好多少。

这是一个不改变vet并使用更高级迭代器的版本:

([nil] + vet).
each_cons(2).
flat_map {|a, b| if [a, b] == [2, 2] then [-1, b] else b end }

注意,即使没有这个bug,你的问题中的代码仍然是错误的:你说你想在数组的所有2之间放置一个-1。但是,您的代码在每个2之后放置了一个-1

因此,例如,如果输入是[2],您的代码将生成[2, -1],而不是正确的结果[2]

您可以通过slice_when将数组拆分为相邻的2,然后通过inject-1放在它们之间来重新组合块:

vet = [1, 2, 2, 2, 2, 2]
vet.slice_when { |i, j| i == 2 && j == 2 } #=> [[1, 2], [2], [2], [2], [2]]
.inject     { |a, b| a + [-1] + b }     #=> [1, 2, -1, 2, -1, 2, -1, 2, -1, 2]

注意slice_when操作的是vet中的相邻数字,而inject操作的是相邻的块,即子数组。(更准确地说,a最初是第一个块,在随后的调用中是块的前一个结果)

+操作为Array#+。它看起来非常干净和对称,但不幸的是它创建了几个临时数组。使用pushconcat可能会更有效:

.inject { |a, b| a.push(-1).concat(b) }

我会这样做。

arr = [1, 2, 2, 2, 2, 2]

下面返回一个新的数组,并且不会改变arr:

arr.each_with_object([]) do |n, a|
a << -1 if n == 2
a << n
end
#=> [1, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2]

如果你想修改arr:

arr.replace(arr.each_with_object([]) do |n, a|
a << -1 if n == 2
a << n
end)
#=> [1, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2]
arr
#=> [1, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2]

看到#替换数组。

最新更新