奇怪的Ruby 2+行为与“选择!”

时间:2014-12-03 18:28:45

标签: ruby select

我有一个问题,我似乎无法在任何地方找到记录或解释,所以我希望有人可以帮助我。我已经验证了三个版本的Ruby上的意外行为,所有2.1 +,都证实它不会在早期版本上发生(尽管它通过tryruby。组织,我不知道他们正在使用哪个版本)。无论如何,对于这个问题,我只是发布一些带有结果的代码,希望有人可以帮我调试它。

arr = %w( r a c e c a r )             #=> ["r","a","c","e","c","a","r"]
arr.select { |c| arr.count(c).odd? }  #=> ["e"]
arr.select! { |c| arr.count(c).odd? } #=> ["e","r"] <<<<<<<<<<<<<<< ??????

我认为对我来说这个令人困惑的部分是清楚标记的,如果有人能够解释这是一个错误还是有一些逻辑,我会非常感激。谢谢!

2 个答案:

答案 0 :(得分:6)

您正在重新修改 我不确定结果是定义的行为。在运行对象时,不需要该算法来保持对象处于任何理智的状态。

迭代期间的一些调试打印显示了您的特定结果发生的原因:

irb(main):005:0> x
=> ["r", "a", "c", "e", "c", "a", "r"]
irb(main):006:0> x.select! { |c| p x; x.count(c).odd? }
["r", "a", "c", "e", "c", "a", "r"]
["r", "a", "c", "e", "c", "a", "r"]
["r", "a", "c", "e", "c", "a", "r"]
["r", "a", "c", "e", "c", "a", "r"] # "e" is kept...
["e", "a", "c", "e", "c", "a", "r"] # ... and moved to the start of the array
["e", "a", "c", "e", "c", "a", "r"]
["e", "a", "c", "e", "c", "a", "r"] # now "r" is kept
=> ["e", "r"]

您可以在最后一次迭代中看到, 只有一个r,并且e已移到数组的前面。据推测,算法就地修改了数组,将匹配的元素移动到前面,覆盖已经失败的元素。它跟踪匹配和移动的元素数量,然后将数组截断为多个元素。

所以,请使用select


匹配更多元素的较长示例使问题更加清晰:

irb(main):001:0> nums = (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):002:0> nums.select! { |i| p nums; i.even? }
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 8, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 8, 5, 6, 7, 8, 9, 10]
=> [2, 4, 6, 8, 10]

你可以看到它确实将匹配的元素移动到数组的前面,覆盖了不匹配的元素,然后截断了数组。

答案 1 :(得分:0)

只是为了给你一些其他方法来完成你正在做的事情:

arr = %w( r a c e c a r )
arr.group_by{ |c| arr.count(c).odd? }        
# => {false=>["r", "a", "c", "c", "a", "r"], true=>["e"]}
arr.group_by{ |c| arr.count(c).odd? }.values 
# => [["r", "a", "c", "c", "a", "r"], ["e"]]
arr.partition{ |c| arr.count(c).odd? }       
# => [["e"], ["r", "a", "c", "c", "a", "r"]]

如果你想要更易读的键:

arr.group_by{ |c| arr.count(c).odd? ? :odd : :even } 
# => {:even=>["r", "a", "c", "c", "a", "r"], :odd=>["e"]}

partitiongroup_by是将数组中的元素分成某种分组的基本构建块,因此熟悉它们是很好的。