首先,为什么您的解决方案不起作用? select 接受一个块,它会丢弃该块返回假值的所有元素(nil 或 false),并保留所有块返回真值的元素。
arr.select { |p| p < 5; break if p >= 5 } # = nil
在这里,您调用p < 5,但您只是忽略了它的返回值。在 Ruby 中,块的返回值是块内最后一个表达式的值。此处评估的最后一个表达式是break if p >= 5。由于没有else的情况,它会为小于5的所有元素返回nil(即false,即表示“不保留此元素”),然后它将返回break(表示“中止并返回nil")。
IOW:您是在告诉 select 丢弃所有小于 5 的元素,然后中止。
arr.select { |p| p < 5; next if p >= 5 } # = []
在这种情况下,您再次为小于 5 的所有元素返回 nil,对于所有大于或等于 5 的元素,您将参数返回给 next……但没有参数,所以,您还将为所有大于 5 的元素返回 nil。
IOW:你告诉select 扔掉所有小于 5 的元素,然后你告诉它扔掉所有大于或等于 5 的元素,或者,换句话说,你告诉它扔掉所有元素… 并且你仍在迭代整个数组。
无论如何,这没有多大意义。 next 隐含在块中,就像 return 隐含在方法中一样。 (事实上,next 是块,return 是方法),所以你所做的只是添加一个多余的关键字。
arr.select { |p| p < 5; return if p >= 5 } # LocalJumpError
再一次,和以前一样……你告诉select 扔掉所有小于5 的元素,然后你是return 来自封闭方法。但是没有封闭方法,所以Ruby raises 是LocalJumpError。
您可以通过这种方式“修复”您的第二个解决方案:
arr.select { |p| if p < 5 then true else next end }
即对于小于 5 的元素,您实际上需要返回 true(或一些真实的东西)。但是,就像我说的那样,next 无论如何都是隐含的,所以您可以将其省略,这意味着这相当于
arr.select { |p| if p < 5 then true else nil end }
其中,由于select 对待false 和nil 相同,因此等价于
arr.select { |p| if p < 5 then true else false end }
当然和
一样
arr.select { |p| p < 5 }
其中,由于< 和> 的对称性,所以与
arr.select(&5.method(:>))
无论如何,在排序数组中搜索的正确解决方案是使用二分搜索,它根本不需要迭代数组,并且只需要 O(log(n) ) 比较:
arr.bsearch_index(&5.method(:<=))
这将搜索“小于 5 的最后一个数字的下一个最大索引右侧的元素的索引”。然后你可以使用Array#[] 来提取你想要的元素切片
arr[0...arr.bsearch_index(&5.method(:<=))]
注意Array#bsearch_index就是new in 2.3.0,相信在那之前,你可以使用Range#bsearchinstead:
arr[0...(0...arr.size).bsearch {|i| arr[i] >= 5 }]