解决这个 ruby​​ 迭代器棘手问题的干净解决方案吗?

发布于 2024-09-04 10:15:49 字数 1584 浏览 11 评论 0原文

k = [1,2,3,4,5]
for n in k
  puts n
  if n == 2
    k.delete(n)
  end
end
puts k.join(",")

# Result:
# 1
# 2
# 4
# 5
# [1,3,4,5]

# Desired:
# 1
# 2
# 3
# 4
# 5
# [1,3,4,5]

同样的效果也发生在另一个数组迭代器 k.each: 上,

k = [1,2,3,4,5]
k.each do |n|
  puts n
  if n == 2
    k.delete(n)
  end
end
puts k.join(",")

具有相同的输出。

发生这种情况的原因非常清楚......Ruby 实际上并没有迭代存储在数组中的对象,而是将其变成一个漂亮的数组索引迭代器,从索引 0 开始,每次增加索引直到结束。但是当您删除一个项目时,它仍然会增加索引,因此它不会对同一个索引进行两次评估,而这正是我想要的。

可能不是正在发生的事情,但这是我能想到的最好的情况。

有没有一种干净的方法来做到这一点?是否已经有一个内置迭代器可以做到这一点?或者我是否必须将其弄脏并执行数组索引迭代器,并且在删除项目时不增加? (或迭代数组的克隆,然后从原始数组中删除)


澄清

我不想简单地从数组中删除项目;我想从数组中删除项目。抱歉,如果说清楚的话。我想做的是迭代每个元素,并“处理”它;此过程有时可能会删除它。更准确地说:

class Living_Thing

  def initialize tracker,id
    @tracker = tracker
    @id = id

    @tracker << self
  end

  def process
    do_stuff
    puts @id
    if @id == 2
      die
    end
  end

  def die
    do_stuff_to_die
    @tracker.delete(self)
  end

  def inspect
    @id
  end
end

tracking_array = Array.new()

foo = Living_Thing.new(tracking_array,1)
bar = Living_Thing.new(tracking_array,2)
rab = Living_Thing.new(tracking_array,3)
oof = Living_Thing.new(tracking_array,4)

puts tracking_array.join(",")              # => [1, 2, 3, 4]

for n in tracking_array
  n.process
end

# result: only foo, bar, and oof are processed

理想情况下,我希望处理 track_array 中的所有项目。

当Living_Thing从tracking_array中删除时,必须调用Living_Thing#die; do_stuff_to_die 清理必须清理的东西。

k = [1,2,3,4,5]
for n in k
  puts n
  if n == 2
    k.delete(n)
  end
end
puts k.join(",")

# Result:
# 1
# 2
# 4
# 5
# [1,3,4,5]

# Desired:
# 1
# 2
# 3
# 4
# 5
# [1,3,4,5]

This same effect happens with the other array iterator, k.each:

k = [1,2,3,4,5]
k.each do |n|
  puts n
  if n == 2
    k.delete(n)
  end
end
puts k.join(",")

has the same output.

The reason this is happening is pretty clear...Ruby doesn't actually iterate through the objects stored in the array, but rather just turns it into a pretty array index iterator, starting at index 0 and each time increasing the index until it's over. But when you delete an item, it still increments the index, so it doesn't evaluate the same index twice, which I want it to.

This might not be what's happening, but it's the best I can think of.

Is there a clean way to do this? Is there already a built-in iterator that can do this? Or will I have to dirty it up and do an array index iterator, and not increment when the item is deleted? (or iterate through a clone of the array, and delete from the original array)


Clarification

I don't simply want to delete items from an array; sorry if that was clear. What I'd want to do is iterate through each element, and "process" it; this process might sometimes delete it. To be more accurate:

class Living_Thing

  def initialize tracker,id
    @tracker = tracker
    @id = id

    @tracker << self
  end

  def process
    do_stuff
    puts @id
    if @id == 2
      die
    end
  end

  def die
    do_stuff_to_die
    @tracker.delete(self)
  end

  def inspect
    @id
  end
end

tracking_array = Array.new()

foo = Living_Thing.new(tracking_array,1)
bar = Living_Thing.new(tracking_array,2)
rab = Living_Thing.new(tracking_array,3)
oof = Living_Thing.new(tracking_array,4)

puts tracking_array.join(",")              # => [1, 2, 3, 4]

for n in tracking_array
  n.process
end

# result: only foo, bar, and oof are processed

Ideally, I'd want all items in tracking_array to be processed.

When Living_Thing is removed from tracking_array, Living_Thing#die must be called; do_stuff_to_die cleans up things that have to be claned up.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

表情可笑 2024-09-11 10:15:50

在大多数语言中,在迭代集合时改变集合是错误的。在大多数语言中,解决方案是创建一个副本并对其进行变异,或者构建一个索引列表,并在完成迭代后对这些索引执行操作,但 Ruby 的迭代器为您提供的功能远不止这些。有几个解决方案是显而易见的。最惯用的IMO:

puts k
puts k.reject {|n| n == 2}.join(',')

更直接地从您的示例翻译:(

k.delete_if do |n|
  puts n
  n == 2
end
puts k.join(',')

delete_if基本上是reject的破坏性版本,它返回块未返回true的对象的数组为了。)

It's an error in most languages to mutate a collection while you're iterating it. In most languages the solution is to create a copy and mutate that or build up a list of indexes and perform the operations on those indexes when you're done iterating, but Ruby's iterators give you a bit more than that. A couple of solutions are obvious. The most idiomatic IMO:

puts k
puts k.reject {|n| n == 2}.join(',')

More directly translated from your example:

k.delete_if do |n|
  puts n
  n == 2
end
puts k.join(',')

(delete_if is basically the destructive version of reject, which returns an array of the objects that the block did not return true for.)

硬不硬你别怂 2024-09-11 10:15:50

这可能更适合处理(参考更新的澄清)

k = [1,2,3,4,5] 
k.dup.each do |n| 
  puts n 
  if n == 2
    k.delete(n) 
  end 
end 
puts k.join(",")

它回避了您所遇到的问题(关于通过对象的迭代与通过索引的迭代)

this might be more suited for the processing (Ref. updated clarification)

k = [1,2,3,4,5] 
k.dup.each do |n| 
  puts n 
  if n == 2
    k.delete(n) 
  end 
end 
puts k.join(",")

it side steps the question you had though (about iteration through objects vs iteration through indices)

逐鹿 2024-09-11 10:15:50

好吧,假设您想从数组中消除所有 2:

arr = [1,2,3,4,5]
arr.delete(2)
puts arr.join(", ")
# => "1, 3, 4, 5"
arr = [1,2,3,2,4,2,5,2]
arr.delete(2)
puts arr.join(", ")
# => "1, 3, 4, 5"

但我怀疑您想迭代,所以我会:

arr = [1,2,3,4,5]
arr.each {|x| a[a.index(x)] = nil if x == 2}.compact!

也许这太脏了?对 nil 的赋值使迭代器计数保持正确,并且 compact! 会在事后清除 nils。 Course map 使其变得更短并且更清晰:

arr.map {|x| x if x != 2}.compact!

Okay, so let's say you want to eliminate all 2's from your array:

arr = [1,2,3,4,5]
arr.delete(2)
puts arr.join(", ")
# => "1, 3, 4, 5"
arr = [1,2,3,2,4,2,5,2]
arr.delete(2)
puts arr.join(", ")
# => "1, 3, 4, 5"

But I suspect you want to iterate, so I would:

arr = [1,2,3,4,5]
arr.each {|x| a[a.index(x)] = nil if x == 2}.compact!

Maybe that's too dirty? The assignment to nil keeps the iterator count right, and the compact! wipes out the nils after the fact. Course map keeps it a little shorter and is cleaner:

arr.map {|x| x if x != 2}.compact!
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文