2010-06-15 10 views
5

Digamos que tengo una funciónentendimiento Rubí Enumerable # mapa (con bloques más complejos)

def odd_or_even n 
    if n%2 == 0 
    return :even 
    else 
    return :odd 
    end 
end 

Y tuve un simple conjunto numerable

simple = [1,2,3,4,5] 

Y yo se lo pasó por mapa, con mi función, usando un bloque do-end:

simple.map do 
    |n| odd_or_even(n) 
end 
# => [:odd,:even,:odd,:even,:odd] 

¿Cómo podría hacer esto sin, digamos, definir la función en primer lugar? Por ejemplo,

# does not work 
simple.map do |n| 
    if n%2 == 0 
    return :even 
    else 
    return :odd 
    end 
end 

# Desired result: 
# => [:odd,:even,:odd,:even,:odd] 

no es ruby ​​válido, y el compilador se enoja conmigo por siquiera pensarlo. ¿Pero cómo implementaría un tipo equivalente de cosas, eso funciona?

edición

En realidad, la solución a mi problema me importa mucho menos que la motivación/razonamiento detrás de él, para ayudar a entender más cómo funciona :) bloques rubí

+2

Por lo que vale la pena, se puede hacer 1.even? o 1.odd? en ruby> = 1.8.7 – steenslag

Respuesta

13

Usted' re tan cerca. Solo elimine el return s y estará dorado.

Esto se debe a que el bloque pasado a map es un proceso (es decir, creado con Proc.new), y no un lambda. Un return dentro de un proceso no solo salta del proceso salta fuera del método que se ejecutó (es decir, llamado call en) el proceso. Un retorno dentro de una lambda, por otro lado, salta solo de la lambda.

El método proc devuelve una lambda en Ruby 1.8 y una Proc en Ruby 1.9. Probablemente sea mejor simplemente no usar este método y ser explícito con la construcción que desea utilizar.

Supongo que estabas en IRB o en un simple script de ruby ​​cuando estabas probando esto.

a = Proc.new { return } 
a.call # fails. Nothing to return from. 

def foobar 
    a = Proc.new { return } 
    a.call 
    puts 'hello' # not reached. The return within the proc causes execution to jump out of the foobar method. 
end 
foobar # succeeds, but does not print 'hello'. The return within the proc jumps out of the foobar method. 

b = lambda { return } 
b.call # succeeds. The return only returns from the lambda itself. 

def bazquux 
    b = lambda { return } 
    b.call 
    puts 'hello' # this is reached. The lambda only returned from itself. 
end 
bazquux # succeeds, and prints 'hello' 

La lección para aprender de esto es usar devoluciones implícitas a menos que no pueda, supongo.

+0

¿Hay alguna razón * por la que * esto suceda, sin embargo? ¿El bloque simplemente saca el último comando ejecutado, como en retornos implícitos? Lo pregunto porque me gustaría poder predecir lo que sucede; tener estos salir parece un poco ... aleatorio. –

+0

La versión corta es "es una de las diferencias entre procs y lambdas". Trabajando en una mejor explicación. Y sí, los bloques simplemente devolverán la expresión evaluada más recientemente en ellos. – x1a4

+0

gracias por la explicación; es muy completo y útil =) solo una última pregunta ... ¿es posible pasar un lambda como bloque? a #map, tal vez? –

9

Sospecho que esto puede ser una pregunta duplicado, sino para dar un valor a partir de un bloque, utilice next

simple.map do |n| 
    if n%2 == 0 
    next :even 
    else 
    next :odd 
    end 
end 
+0

hm, esto parece ser exactamente lo que estaba buscando :) gracias :) –

3

variante más corta usando la respuesta de Andrew:

simple.map { |n| next :even if n % 2 == 0; :odd } 
+1

Dando un paso más:' simple.map {| n | siguiente n% 2 == 0? : incluso:: impar} ';-) –