2009-06-26 13 views
9

que tienen un método arbitrario en Ruby que produce múltiples valores para que pueda ser entregado a un bloque:¿Puede un método Ruby rendir como un iterador o devolver una matriz según el contexto?

def arbitrary 
    yield 1 
    yield 2 
    yield 3 
    yield 4 
end 

arbitrary { |x| puts x } 

me gustaría modificar este método de manera que, si no hay un bloque, simplemente devuelve el valores como una matriz. Entonces esta construcción también funcionaría:

myarray = arbitrary 
p a -----> [1, 2, 3, 4, 5] 

¿Es esto posible en Ruby?

Respuesta

13

Hay una sintaxis para que:

def arbitrary(&block) 
    values = [1, 2, 3, 4] 
    if block 
    values.each do |v| 
     yield v 
    end 
    else 
    values 
    end 
end 

Nota:

yield v 

se puede sustituir por:

block.call v 
+0

Perfecto, gracias. –

+10

Si reemplaza "if block" con "if block_given?", Ni siquiera tiene que hacer que el argumento "& block" sea explícito, y puede ser suficiente con "def arbitrary". Esta es la práctica común de Ruby. – molf

+0

@Molf: Tienes toda la razón. – bltxd

19
def arbitrary 
    values = [1,2,3,4] 
    return values unless block_given? 
    values.each { |val| yield(val) } 
end 
arbitrary { |x| puts x } 
arbitrary 
+0

También funciona, eligió blue.tuxedo desde que era el primero. –

+1

+1: más limpio, más fresco y más fácil de seguir –

12

En Ruby 1.9+ puede utilizar Enumerator para implementar eso.

def arbitrary(&block) 
    Enumerator.new do |y| 
    values = [1,2,3,4] 
    values.each { |val| y.yield(val) } 
    end.each(&block) 
end 

Tiene la ventaja de que funciona para los flujos infinitos también:

# block-only version 
# 
def natural_numbers 
    0.upto(1/0.0) { |x| yield x } 
end 

# returning an enumerator when no block is given 
# 
def natural_numbers(&block) 
    Enumerator.new do |y| 
    0.upto(1/0.0) { |x| y.yield(x) } 
    end.each(&block) 
end 

Pero la forma más idiomática de hacerlo es para proteger su método con to_enum(your_method_name, your_args) así:

def arbitrary 
    return to_enum(:arbitrary) unless block_given? 

    yield 1 
    yield 2 
    yield 3 
    yield 4 
end 

Este es un modismo que las bibliotecas de ruby ​​core usan en múltiples lugares.

+0

+1 para Enumerator! <3 – grilix

Cuestiones relacionadas