Ответ 1
В то время как решение break
работает, я думаю, что более функциональный подход действительно подходит для этой проблемы. Вы хотите, чтобы take
первые 10 элементов и напечатали их, попробуйте
items.take(10).each { |i| puts i.to_s }
код:
c = 0
items.each { |i|
puts i.to_s
# if c > 9 escape the each iteration early - and do not repeat
c++
}
Я хочу захватить первые 10 элементов, а затем оставить цикл "каждый".
Что я могу заменить прокомментированной строкой? есть ли лучший подход? что-то большее, чем Ruby, идиоматично?
В то время как решение break
работает, я думаю, что более функциональный подход действительно подходит для этой проблемы. Вы хотите, чтобы take
первые 10 элементов и напечатали их, попробуйте
items.take(10).each { |i| puts i.to_s }
В Ruby нет оператора ++
. Он также соглашается использовать do
и end
для многострочных блоков. Изменение вашего решения дает:
c = 0
items.each do |i|
puts i.to_s
break if c > 9
c += 1
end
Или также:
items.each_with_index do |i, c|
puts i.to_s
break if c > 9
end
Смотрите each_with_index
, а также Программирование Ruby Break, Redo, и далее.
Обновление: Chuck answer с диапазонами больше Ruby-like и nimrodm answer с использованием take
еще лучше.
break
работает для ускорения работы из цикла, но для него более идиоматично делать items[0..9].each {|i| puts i}
. (И если все, что вы делаете, буквально печатает элементы без каких-либо изменений, вы можете просто сделать puts items[0..9]
.)
Другой вариант:
puts items.first(10)
Обратите внимание, что это отлично работает с массивами менее 10 элементов:
>> nums = (1..5).to_a
=> [1, 2, 3, 4, 5]
>> puts nums.first(10)
1
2
3
4
5
(Еще одно замечание: многие люди предлагают некоторую форму puts i.to_s
, но в таком случае не является .to_s
избыточным? puts
будет автоматически вызывать .to_s
на нестрочном напечатайте его, подумал я. Вам понадобится .to_s
, если вы хотите сказать puts 'A' + i.to_s
или тому подобное.)
Другой вариант:
items.first(10).each do |i|
puts i.to_s
end
Это немного легче для меня, чем взломать итератор, и сначала вернет только столько элементов, сколько доступно, если их недостаточно.
Означает ли это, что вы хотите?
10.times { |i|
puts items[i].to_s
}
items.each_with_index { |i, c| puts i and break if c <= 9 }
Было спрошено:
Я хочу захватить первые 10 элементов, а затем оставить цикл "каждый".
Используйте throw
и catch
, чтобы выполнить это, с небольшими изменениями в примере:
catch(:done) do
c = 0
collected = []
items.each do |item|
collected << item
throw(:done, collected) if c == 9 # started at 0
c += 1
end
collected # if the list is less than 10 long, return what was collected
end
Просто throw
метка :done
с collected
и catch
, которая ждет :done
, вернет collected
.
И немного "рубить":
catch(:done) do
items.inject([]) do |collected, item|
throw(:done, collected) if collected.size == 10
collected << item # collected gets returned here and populates the first argument of this block
end
end
Я не знаю, почему некоторые люди отказываются использовать inject
и вместо этого используют reduce
(они эквивалентны), когда ясно, что пустой массив, присвоенный inject([])
, вводится item
s! Во всяком случае, inject
вернет collected
, если их меньше 10.
Большинство ответов пытаются ответить на вопрос, что может быть целью вопроса вместо того, что было задано, и items.take(10)
имеет смысл в этом случае. Но я могу себе представить, что хочу захватить первые предметы, которые соответствуют моему бюджету в 100 долларов. Тогда вы можете просто:
catch(:done) do
items.inject({items: [], budget: 100}) do |ledger, item|
remainder = ledger[:budget] - item.price
if remainder < 0
throw(:done, ledger)
else
ledger.tap do |this|
this[:items] << item
this[:budget] = remainder
end # tap just returns what is being tapped into, in this case, ledger
end
end
end