我在代码中重写了这个问题:
many = 1000
# An expensive method.
#
# It returns some data or nil if no result is available.
expensive_method = lambda do
rand(5) == 0 ? nil : "foo"
end
# Now, let's collect some data and stop collecting when no more data is
# available.
# This is concise but doesn't work.
collection = many.times.map do
expensive_method.call || break
end
puts collection.is_a? Array # false
# This is less concise but works.
collection = []
many.times do
collection << (expensive_method.call || break)
end
puts collection.is_a? Array # true
# My inner Rubyist ponders: Is it possible to accomplish this more concisely
# using map?
Run Code Online (Sandbox Code Playgroud)
dog*_*unk 15
当然,似乎在Ruby中执行此操作的唯一方法是过滤器类型方法,然后将结果传递给map.我不确定这是否适用于1.8,但在1.9中您可以:
[0,1,2,1,0].take_while {|val| val < 2}.map(&:some_function)
Run Code Online (Sandbox Code Playgroud)
或者在times
示例中
3.times.take_while {|count| count <= 1 } #=> [0,1]
Run Code Online (Sandbox Code Playgroud)
不要map
直接使用,而是建立自己的集合,然后使用break
返回值的事实提前中止:
result =
[0, 1, 2, 1, 0].each_with_object([]) { |val, accumulator|
if val < 2
accumulator << val
else
break accumulator
end
}
result # => [0, 1]
Run Code Online (Sandbox Code Playgroud)
如果我们只是break
(而不是break accumulator
)那么nil
将被隐式返回并且result
将被设置为nil
.
这种解决方案的优点是只分配一个累加器数组并且只需要循环一次。
如果你的意思是"直到休息",[0,1,2,1,0]
应该导致[0,1]
,而不是[0,1,1,0]
.我知道的Ruby中唯一的方法就是break
循环.功能方法可能会慢很多,因为你实际上没有破坏:
r =
[0,1,2,1,0].inject([true, []]) do |(f, a), i|
if f
if i > 1
[false, a]
else
[f, a << i]
end
else
[f, a]
end
end
puts r.last.inspect
Run Code Online (Sandbox Code Playgroud)
与之比较:
r = []
[0,1,2,1,0].each do |i|
break if i > 1
r << i
end
puts r.inspect
Run Code Online (Sandbox Code Playgroud)
对于Ruby来说,尾递归是不可能的,这就是用真正的函数式语言完成的.
打破map
对我不起作用,结果是nil
.
补充:正如@dogenpunk指出的,有take_while
(drop_while
实际上),这可能是一个更好的选择,只是它总是创建临时数组,这可能是也可能不是问题.
归档时间: |
|
查看次数: |
11600 次 |
最近记录: |