Подтвердить что ты не робот

Как я могу выйти из карты/собрать и вернуть все, что было собрано до этого?

Я переписываю этот вопрос в код:

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 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?
4b9b3361

Ответ 1

Если вы действительно имеете в виду "до разрыва", [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

Сравните с:

r = []
[0,1,2,1,0].each do |i|
  break if i > 1
  r << i
end
puts r.inspect

Рекурсия хвоста не может быть и речи для Ruby, так как делаются в реальных функциональных языках.

Breaking map не работает для меня, результат nil.

Добавлено: Как отметил @dogenpunk, есть take_whiledrop_while), что, вероятно, является лучшей альтернативой, только он всегда создает временный массив, который может или не может быть проблемой.

Ответ 2

Конечно, единственный способ сделать это в Ruby - это метод типа фильтра, а затем передать результаты для отображения. Я не уверен, что это работает в 1.8, но в 1.9 вы могли:

[0,1,2,1,0].take_while {|val| val < 2}.map(&:some_function)

Или в примере times

3.times.take_while {|count| count <= 1 } #=> [0,1]

Ответ 3

Вместо прямого использования 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]

Если бы мы сделали только break (вместо break accumulator), то nil будет неявно возвращено, а result будет только nil.

Это решение имеет то преимущество, что выделяет только один массив накопителей и имеет только один цикл.

Ответ 4

irb(main):011:0> 3.times.select {|count| count <= 1}
=> [0, 1]

или

irb(main):014:0> 3.times.reject {|count| count > 1}
=> [0, 1]

Ответ 5

foo = 3.times.map do |count|
        count > 1 ? nil : rand 
      end.compact

Ответ 6

Как насчет:

odd_index = my_collection.index{|item| odd_condition(item)}
result = odd_index == 0 ? [] : my_collection[0..odd_index.to_i - 1]