2009-10-14 10 views
35

código:escapar de la .Cada {} iteración temprana en Ruby

c = 0 
items.each { |i| 
    puts i.to_s  
    # if c > 9 escape the each iteration early - and do not repeat 
    c++ 
} 

Quiero agarrar los primeros 10 elementos entonces salen de la "cada" bucle.

¿Con qué reemplazo la línea comentada? hay un mejor enfoque? algo más Ruby idiomático?

+0

Sugiero respuesta de nimrodm que utiliza tomar: http://stackoverflow.com/questions/1568288/escaping-the-each-iteration-early-in-ruby/1568445#1568445 –

Respuesta

59

Si bien la solución break funciona, creo que un enfoque más funcional realmente se adapta a este problema. ¿Quieres take los primeros 10 elementos e imprimirlos a fin de tratar

items.take(10).each { |i| puts i.to_s } 
+0

Siempre me ha gustado FP, ¡genial! – khelll

+14

Más corto: 'puts items.take (10)' – Telemachus

+0

Probé Google en el 'método Ruby Take' ahora mismo y no pude encontrar una referencia a en qué módulo 'take' está. ¿Dónde está en la API? –

1

¿Esto se parece a lo que quieres?

10.times { |i| 
    puts items[i].to_s 
} 
+0

Eso funcionaría, pero no siempre estaré seguro de que la fuente tenga al menos 10 elementos. – BuddyJoe

+0

Ah. Puedes agregar 'break if items [i] == nil' pero en este punto' each_with_index' se parece a lo que deberías estar usando. –

42

No hay operador ++ en Ruby. También es convencional utilizar do y end para bloques de varias líneas. Modificación de los rendimientos de 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 Programming Ruby Break, Redo, and Next.

Actualización:Chuck's answer con rangos es más Rubí-como, y nimrodm's answer usando take es aún mejor.

+0

Gracias. Respuesta y +1. Guau, estaba muy lejos de la sintaxis inicial. – BuddyJoe

+0

No estaba lejos, en realidad: la única parte inválida de su respuesta fue '++'. Las llaves para bloques funcionarán, simplemente no se prefiere para bloques de varias líneas; ver http://stackoverflow.com/questions/533008/what-is-the-difference-or-value-of-these-block-coding-styles-in-ruby –

+0

Me gusta tu primera solución porque si quieres hacer un loop más de 100 elementos cada vez pero solo saca 10 condicionalmente, puede incrementar el contador de forma independiente. – chrisallick

0
items.each_with_index { |i, c| puts i and break if c <= 9 } 
+0

Eso se romperá después del primer artículo. – Chuck

+0

No realmente, ¿cómo lo has probado? – khelll

+0

@Khelll: ¿esto se debe a la evaluación perezosa de 'y'? Funciona, pero es demasiado inteligente para mí. Mi cerebro sigue queriendo '> =' ya que veo "y romper si" juntos. – Telemachus

7

break obras para escapar temprano de un bucle, pero es más idiomático sólo para hacer items[0..9].each {|i| puts i}. (Y si todo lo que hace es, literalmente, imprimir los elementos sin ningún cambio, puede hacer puts items[0..9]).

+1

Lo habría escrito como: 'pone elementos [0..9] .join (" \ n ")' –

4

Otra variante:

puts items.first(10) 

Tenga en cuenta que esto funciona bien con matrices de menos de 10 unidades:

>> nums = (1..5).to_a 
=> [1, 2, 3, 4, 5] 
>> puts nums.first(10) 
1 
2 
3 
4 
5 

(Otra nota, mucha gente está ofreciendo alguna forma de puts i.to_s, pero en tal caso, ¿no es .to_s redundante? puts automáticamente llame al .to_s en una cadena para imprimirlo, pensé. Usted sólo necesita .to_s si quería decir puts 'A' + i.to_s o similares.)

3

Otra opción sería

items.first(10).each do |i| 
    puts i.to_s 
end 

que lee un poco más fácil para mí que se rompe en un iterador, y el primer devolverá solamente tantos elementos como estén disponibles si no hay suficientes.

-1

Se preguntó:

Quiero agarrar los primeros 10 elementos entonces salen de la "cada" bucle.

Uso throw y catch para lograr esto, con pocos 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á a la espera de :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 utilizar reduce lugar (que son equivalentes) cuando claramente la matriz vacía dado a inject([]) es siendo inyectado con item s! De todos modos, el inject devolverá collected si hay menos de 10 elementos.

La mayoría de las respuestas intentan responder cuál podría ser el objetivo de la pregunta en lugar de lo que se solicitó 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. A continuación, puede:

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 
+0

Respondió una pregunta simple y dio una respuesta realmente complicada. No es necesario usar 'throw' y' catch' aquí o convertir esto en 13 líneas de código profundamente anidado. Mantenlo simple. – NeuroXc

+0

Mi respuesta es de 6 líneas, muestra una forma alternativa de escapar de cada uno de los bucles que se le preguntó, está anidado un nivel más profundo que la mayoría de las respuestas. Mi esperanza al dejar esta respuesta fue mostrar cómo alternativamente salir de un bucle aprovechando este contexto simple. Si realmente hubiera leído mi publicación, mis 13 líneas de código son una respuesta más compleja a un ejemplo más complejo que planteé en mi respuesta. Me disculpo de antemano por tener demasiadas palabras en esta respuesta. – Nothus

Cuestiones relacionadas