每个循环都无法按预期工作

时间:2015-08-27 13:46:07

标签: ruby loops

我有一个名为@results的数组,它只由数组组成。我想迭代@results并永久删除任何小于给定大小的内部数组:

我的代码:

  def check_results limit
    @results.each_with_index do |result, index|
      @results.delete_at(index) if result.size < limit
    end
  end

不幸的是,这只会删除数组长度小于limit的第一个项目。例如,如果limit = 4@results = [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]],则check_results会返回[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]]

我无法弄清楚为什么会这样。我使用了错误的循环吗?

3 个答案:

答案 0 :(得分:4)

你应该这样做,因为delete_at修改了数组,如果在迭代时删除元素,你会得到意想不到的行为

@results.reject { |i| i.size < limit }

上面的代码将排除所有大小小于limit

的数组元素

答案 1 :(得分:3)

修改@results数组并不是一个好主意,因为这会与外部迭代冲突。

你应该做的是使用select来构建一个新数组。

def check_results(limit)
  @result.select { |result| result.size > limit }
end

答案 2 :(得分:2)

根据文档,#delete_at返回该索引处的元素。

a = ["ant", "bat", "cat", "dog"]
a.delete_at(2)    #=> "cat"
a                 #=> ["ant", "bat", "dog"]
a.delete_at(99)   #=> nil

我添加了一些调试语句来向您展示每个步骤发生了什么,假设限制为4:

@results = [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]]
@results.each_with_index do |r, i|
  puts "RESULT: #{r.to_s}"
  puts "INDEX: #{i}"
  @results.delete_at(i) if r.size < 4
  puts "ARRAY: #{@results.to_s}"
end

RESULT: [1, 1, 1, 1]
INDEX: 0
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]]
RESULT: [1, 1, 1, 1]
INDEX: 1
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]]
RESULT: [1, 1, 1]
INDEX: 2
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]]
# @results == [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]]

如您所见,原始位于索引2的元素已被删除。因为您在迭代它时正在修改@results,所以索引3不再存在,已经分析了索引2。这就是为什么在迭代它时不应该修改对象的原因。

理想情况下,您要使用#delete_if。与以!结尾的方法类似,#delete_if将根据块中的条件(作为参数)修改数组(不返回结果的副本)。以下是如何实现该方法:

def check_results(limit)
  @results.delete_if { |arr| arr.length < limit }
end

@results = [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ]

check_results(2)
# => @results == [ ['foo', 'bar'], ['bizz', 'bazz'] ]

如果您不想修改@results,那么我建议使用类似的方法#reject。同样,@results将不会被修改,而是会返回结果的副本。

def check_results(limit)
  @results.reject { |arr| arr.length < limit }
end

@results = [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ]

check_results(2)
# => [ ['foo', 'bar'], ['bizz', 'bazz'] ]
# => @results == [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ]