signo - ruby tutorial
escapando la iteraciĆ³n de.each{} temprano en Ruby (8)
código:
c = 0
items.each { |i|
puts i.to_s
# if c > 9 escape the each iteration early - and do not repeat
c++
}
Quiero tomar los primeros 10 elementos y luego abandonar el ciclo "cada".
¿Con qué reemplazo la línea comentada? hay un mejor enfoque? algo más Ruby idiomático?
¿Esto se ve como lo que quieres?
10.times { |i|
puts items[i].to_s
}
Fue preguntado:
Quiero tomar los primeros 10 elementos y luego abandonar el ciclo "cada".
Utilice throw
and catch
para lograr esto, con algunos cambios en el ejemplo:
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
Simplemente throw
la etiqueta :done
con collected
y la catch
que está esperando :done
volverá collected
.
Y para "ruby" esto un poco:
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
No sé por qué algunas personas se niegan a usar inject
y usar reduce
(son equivalentes) cuando claramente el conjunto vacío dado a la inject([])
se está inyectando con el item
s. De todos modos, la inject
devolverá collected
si hay menos de 10 elementos.
La mayoría de las respuestas intentan responder a lo que podría ser la intención de la pregunta en lugar de lo que se preguntó y items.take(10)
tiene perfecto sentido en ese caso. Pero me imagino que quiero tomar los primeros artículos que encajan dentro de mi presupuesto de $ 100. Entonces puedes simplemente:
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
No hay operador ++
en Ruby. También es una convención usar do
y end
para bloques multilínea. Modificar los rendimientos de su solución:
c = 0
items.each do |i|
puts i.to_s
break if c > 9
c += 1
end
O también:
items.each_with_index do |i, c|
puts i.to_s
break if c > 9
end
Ver each_with_index
y también programar Ruby Break, Rehacer y Siguiente .
Actualización: la respuesta de Chuck con rangos es más similar a Ruby, y la respuesta de nimrodm con take
es aún mejor.
Otra opción sería
items.first(10).each do |i|
puts i.to_s
end
Eso me resulta un poco más fácil de leer que romper un iterador, y primero solo devolverá tantos elementos como estén disponibles si no son suficientes.
Otra variante:
puts items.first(10)
Tenga en cuenta que esto funciona bien con matrices de menos de 10 elementos:
>> nums = (1..5).to_a
=> [1, 2, 3, 4, 5]
>> puts nums.first(10)
1
2
3
4
5
(Una nota más, mucha gente está ofreciendo alguna forma de puts i.to_s
, pero en tal caso, no es .to_s
redundante? .to_s
llamará automáticamente .to_s
en una cadena para imprimirlo, pensé .to_s
si quisiera decir puts ''A'' + i.to_s
o similares.)
Si bien la solución de break
funciona, creo que un enfoque más funcional realmente se adapta a este problema. Desea take
los primeros 10 elementos e imprimirlos, así que intente
items.take(10).each { |i| puts i.to_s }
break
funciona para escaparse temprano de un ciclo, pero es más idiomático simplemente hacer items[0..9].each {|i| puts i}
items[0..9].each {|i| puts i}
. (Y si todo lo que hace es, literalmente, imprimir los elementos sin ningún cambio, puede simplemente puts items[0..9]
).
items.each_with_index { |i, c| puts i and break if c <= 9 }