新手问题 在数组的循环内删除数组元素的问题?

u4crella · 2020年02月22日 · 最后由 spike76 回复于 2020年02月23日 · 2702 次阅读

(在https://v2ex.com/t/646674里得到启发)

对数组内的不同元素执行相同的操作,我用到了

方法一:array.delete(x)

方法二:array.reject!{|y| y == x}

方法三:array = array.reject{|y| y == x}

出现了一些奇怪的结果,直接看代码。有人能解释一下是怎么回事吗?

names = %w(王八 王九 李大 李二 赵四)
puts names.inspect
names.each do |x|
  case x[0]
  when '王', '李'
    # names.delete(x) # 这样变成[王九, 李二, 赵四]
    names.reject!{|y| y == x} # 这样变成[王九, 李二, 赵四]
    # names = names.reject{|y| y == x} # 这样变成[赵四]
  end
end
puts RUBY_VERSION
puts names.inspect

numbers = (1 .. 5).to_a
numbers.each do |x|
  if x.even?
    numbers.delete(x) # 这样变成[1,3,5]
    # numbers.reject!{|y| y == x} # 这样变成[1,3,5]
    # numbers = numbers.reject{|y| y == x} # 这样变成[1,3,5]
  end
end
puts numbers.inspect

在 names.each 内原位移除 names 元素 X 会导致下一个元素 Y 位置发生前移而导致元素 Y 被跳过,ruby 对这种场景不会提示错误。

例一里 王八 被 reject! 后,王九 成了 x[0]李大 成了 x[1],所以 王八 之后的下次循环处理的是 李大王九 被保留下来。

例二里看起来正确是因为 delete 2 后跳过了 3 刚好又处理到 4,所以 2 和 4 都被删除了。如果用 [1, 2, 4, 3, 5] 测试,结果会是 [1,4,3,5]

一个可行的写法是 illegit_surnames = ['王', '李']; names.reject! { |x| illegit_surnames.include?(x[0]) }

还可以用 select 过滤出来需要的

array = [ 3, 4, 7, 12 ]
result = array.select do |elem|
  elem > 5
end

函数式编程的思想是,与其改变变量,不如生成一个新的。以为改变变量太复杂了。

遍历,生成新的这种,除了极特殊情况,不会成为瓶颈。

delete_if

这是个经典问题了。正向遍历的过程中改变了集合的长度

一边从桶里舀水出来,一边把桶高锯掉一些。

这种情况类 for 循环需要倒着遍历,或者用 while … do 这样的循环

同意楼上。不建议在遍历过程中改变原数组的长度

需要 登录 后方可回复, 如果你还没有账号请 注册新账号