2009-06-26 4 views
9

Ho un metodo arbitrario in Ruby che produce valori multipli in modo che possa essere consegnato ad un blocco:Un metodo Ruby può produrre come iteratore o restituire un array in base al contesto?

def arbitrary 
    yield 1 
    yield 2 
    yield 3 
    yield 4 
end 

arbitrary { |x| puts x } 

mi piacerebbe modificare questo metodo in modo che, se non v'è alcun blocco, restituisce solo il valori come una matrice. Quindi anche questo costrutto funzionerebbe:

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

E 'possibile in Ruby?

risposta

13

C'è una sintassi per questo:

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

Nota:

yield v 

può essere sostituito con:

block.call v 
+0

Perfetto, grazie. –

+10

Se sostituisci "if block" con "if block_given?", Non devi nemmeno rendere esplicito l'argomento "& block", e puoi bastare con "def arbitrary". Questa è una pratica comune di Ruby. – molf

+0

@Molf: hai assolutamente ragione. – 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

Funziona anche, ha scelto blue.tuxedo dal momento che era il primo. –

+1

+1: più pulito, più nitido e più facile da seguire –

12

In ruby ​​1.9+ è possibile utilizzare Enumerator per implementarlo.

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

Ha il vantaggio che funziona per i flussi infiniti troppo:

# 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 

Ma il modo più idiomatico per farlo è quello di custodire il tuo metodo con to_enum(your_method_name, your_args) in questo modo:

def arbitrary 
    return to_enum(:arbitrary) unless block_given? 

    yield 1 
    yield 2 
    yield 3 
    yield 4 
end 

Questo è un idioma che le librerie di base ruby ​​usano in più punti.

+0

+1 per Enumerator! <3 – grilix