如何突破地图/收集并返回到目前为止收集的内容?

Hak*_*ari 22 ruby

我在代码中重写了这个问题:

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)


pdo*_*obb 9

不要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.

这种解决方案的优点是只分配一个累加器数组并且只需要循环一次。


Vic*_*roz 7

如果你的意思是"直到休息",[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实际上),这可能是一个更好的选择,只是它总是创建临时数组,这可能是也可能不是问题.