2016-11-09 74 views
0

对于以下代码段。
我想从数组中删除等于3的元素。 但代码只删除了第一个元素。当我调试代码,
我发现迭代器只循环一次而不是两次数组。
我不确定是什么导致了这个问题。任何帮助将不胜感激。Ruby无法删除阵列中的元素

nums = [3,3] 
def remove_element(nums, val) 
    nums.each_with_index do |num,index| 
     if num == val 
      nums.slice!(index) 
     end 
    end 
    nums.length 
end 

remove_element(nums,3) 
+6

千万不要尝试在迭代它来修改阵列。调查员疯了。 – mudasobwa

+2

不要修改您正在迭代的相同数组。 –

+0

@mudasobwa谢谢,能否让我知道原因 –

回答

3

正如您所评论的那样,您在迭代数组的同时修改了数组,从而删除了第一个元素,并结束了迭代。如果您在nums.each_with_index循环内放置打印语句,您将看到它只打印一次。

一种更好的方式来删除一个元素,可以使用拒绝像下面的方法:

nums.reject!{|item| item == 3} 
+0

谢谢,请你让我知道为什么迭代器在删除第一个元素后停止 –

+0

@MarcoSong,我认为在内部迭代器继续寻找下一个项目来迭代,当你删除第一个元素,并且迭代器在第二个元素上,但它没有找到任何东西,然后返回。在这里检查:http://apidock.com/ruby/Enumerator – 2016-11-09 08:20:48

3

由于@steenslag已经指出的那样,delete方法做你想要什么:

n = [1,2,3,3,4,5,6,3,4,5,3,2,1,8] 
n.delete(3) 
n 

回报:[1, 2, 4, 5, 6, 4, 5, 2, 1, 8]

这是值得看的这个替代代码:

nums = [3,3] 
def remove_element(nums, val) 
    nums.each_with_index do |num,index| 
     nums_before_slice = nums.clone 
     if num == val 
      sliced = nums.slice!(index) 
     end 
     puts "nums: #{nums_before_slice}, index: #{index}, sliced: #{sliced.inspect}" 
    end 
end 

remove_element(nums,3) 

puts "Result: #{nums.inspect}" 

输出结果为:

​​

正如您所看到的,迭代只发生一次,因为第二个元素在进行第二次迭代之前已被删除。

这个结果比较这个版本的代码:

nums = [3,3] 
def remove_element(nums, val) 
    nums.clone.each_with_index do |num,index| 
     nums_before_slice = nums.clone 
     if num == val 
      sliced = nums.slice!(index) 
     end 
     puts "nums: #{nums_before_slice}, index: #{index}, sliced: #{sliced.inspect}" 
    end 
end 

remove_element(nums,3) 

puts "Result: #{nums.inspect}" 

导致:

nums: [3, 3], index: 0, sliced: 3 
nums: [3], index: 1, sliced: nil 
Result: [3] 

这就是现在运行在原有nums副本的迭代,但结果是与第二次迭代相同 - 没有第二个要移除的元素。

+0

感谢您的帮助。很容易理解。 –

+0

'n.delete(3)'。 – steenslag

+0

@steenslag是的 - 删除是另一个(也许更好)选项。 – ReggieB

2

什么方法delete

nums = [3,3] 
def remove_element(nums, val) 
    nums.delete(val) 
    nums.length 
end 
remove_element(nums, 3) 
#=> 0 

delete_if

nums = [3,3] 
def remove_element(nums, val) 
    nums.delete_if { |element| element == val } 
    nums.length 
end 
remove_element(nums, 3) 
#=> 0 

UPD

require 'benchmark' 

array = Array.new(100000) { rand(5) } 

Benchmark.bm do |x| 
    x.report("delete: ") { array.delete(5) } 
    x.report("delete_if: ") { array.delete_if { |e| e == 5 } } 
    x.report("reject: ") { array.reject! { |e| e == 5 } } 
end 

#   user  system  total  real 
# delete: 0.000000 0.000000 0.000000 ( 0.004230) 
# delete_if: 0.010000 0.000000 0.010000 ( 0.006387) 
# reject: 0.010000 0.000000 0.010000 ( 0.007543) 
+0

是的,我从leetcode看到这个解决方案。它非常强大和干净。但代码运行速度非常快,我认为删除方法的工作方式可能不是很有效 –

+0

@MarcoSong你可以检查更新:) –

+0

真棒,根本不慢 –