Как я могу выйти из карты/собрать и вернуть все, что было собрано до этого?
Я переписываю этот вопрос в код:
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?
Ответы
Ответ 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_while
(и drop_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]