Ruby each 的疑惑

idotjkl · 2012年03月29日 · 最后由 idotjkl 回复于 2012年03月30日 · 3478 次阅读

这是 Ruby Programming 第十一章的一个练习,讲的是如何在不新建数组的情况下,取出 3 的倍数。程序如下,但发现 list.delete 总不能如期删除 非 3 的倍数。

list = []

for i in 0...100
  list[i]=i+1;
end

list.each{|i|
  if ((i % 3) != 0)  then
    list.delete(i)
  end

}

p list


首先,多行 block 不要用{}而要用 do..end。单行才用{}

list = []

for i in 0...100
  list[i]=i+1;
end

list.each do |i|
  if ((i % 3) != 0)  then
    list.delete(i)
  end
end

p list

在枚举迭代的时候删除被迭代对象种的数据,这样做是不是有问题。

然后 list 初始话看着太难受了

list = (1..100).to_a

多看看Array的方法,尤其跟 delete 相关的

然后你在 block 里修改了对象,就相当容易出问题。 这么写你就能看出来:

list = []
for i in 0...10
    list[i]=i+1;
end

list.each do |i|
    if i % 3 != 0
        list.delete(i)
        p "i: #{i}"
        p list
        p list[i]
    end
end

运行的结果是:

➜  rails  ruby tmp.rb
"i: 1"
[2, 3, 4, 5, 6, 7, 8, 9, 10]
3
"i: 4"
[2, 3, 5, 6, 7, 8, 9, 10]
7
"i: 7"
[2, 3, 5, 6, 8, 9, 10]
nil
"i: 10"
[2, 3, 5, 6, 8, 9]
nil

Enumerable的方法也要多熟悉,我怎么感觉这题是要诱导使用 find_all

简单说就是你的 list 在不断缩短 附赠答案

p list.select {|i| i % 3 == 0}

答案见10楼

你写的代码能把人晕死~~

首先,你试图在一个可枚举对象被迭代取出的时候,删除这个可枚举对象的元素, 这种行为常常会导致不可预期的后果。

其次,改用 while 循环,而不是 if.

晕,这代码真晕。

在不创建新的数组的情况下,我怎么觉得应该用 Array#select 呢?

puts (1..100).to_a.select { |i| i % 3 == 0 }

(1..100).to_a.delete_if { |n| n%3 != 0 } 可以用 delete_if 啊

#11 楼 @doitian 这都能被发现啊... 真让人感到 embarrassed

哈哈 终上 一定要多看 api 文档啊

@lgn21st 如果说在不新建数组的情况下,我们给的是不是就不对了?。。

Returns an array containing all elements of enum for which block is not false (see also Enumerable#reject).

If no block is given, an enumerator is returned instead.

#14 楼 @cqpx 恩,严格规定不建立新数组,而在原数组上裁剪的话,10 楼 @tassandar 的方法是对的。

#12 楼 @lgn21st 哈哈,真搞笑 XD。我觉得你这个方法是最简洁,最符合 ruby 风格的了。

谢谢楼上热心的各位:)

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