2016-01-15 12 views
7

Possiamo facilmente definire un metodo e trasformarlo in blocco con la e commerciale unaria.Ruby: fornire un argomento mentre si trasforma proc in un blocco

def my_method(arg) 
    puts arg*2 
end 

['foo', 'bar'].each(&method(:my_method)) 

# foofoo 
# barbar 

# or 
my_method = ->(arg) { puts arg*2 } 
['foo', 'bar'].each(&my_method) 
# same output 

Come si vede il primo argomento viene passato automaticamente quando lavoriamo con gli aggregati. Ma cosa succede se abbiamo bisogno di passare 2 o anche più argomenti?

my_method = ->(arg,num) { puts arg*num } 
['foo', 'bar'].each(&my_method) 
# ArgumentError: wrong number of arguments (1 for 2) 
['foo', 'bar'].each(&my_method(3)) 
# NoMethodError: undefined method `foo' for main:Object 
['foo','bar'].each do |i, &my_method| 
    yield i, 3 
end 
# LocalJumpError: no block given (yield) 

È possibile passare argomenti aggiuntivi durante l'attivazione di proc in un blocco?

+0

Vuoi entrambi gli argomenti da passare dal metodo chiamato cedendo, o si desidera specificare un argomento al momento della presentazione il blocco al metodo? –

+2

Basta notare che '['foo', 'bar'].each (& my_method) 'non avrebbe senso dato che' each' produrrà sempre un solo elemento. Non rispondere alla tua domanda, ma controlla '['pippo', 3] .each_slice (2, & my_method)'. – ndn

+2

Penso che 'curry' potrebbe essere una chiave per questa domanda. – sawa

risposta

1

Per quanto riguarda your comment:

Strano, ma scambia argomenti durante la performance

In realtà, l'ordine degli argomenti è conservato.

curry restituisce un nuovo proc che raccoglie efficacemente gli argomenti finché non ci sono sufficienti argomenti per richiamare il metodo/proc originale (basato sulla sua arità). Ciò si ottiene ritorno proc intermedi:

def foo(a, b, c) 
    { a: a, b: b, c: c } 
end 

curried_proc = foo.curry #=> #<Proc:0x007fd09b84e018 (lambda)> 
curried_proc[1]   #=> #<Proc:0x007fd09b83e320 (lambda)> 
curried_proc[1][2]  #=> #<Proc:0x007fd09b82cfd0 (lambda)> 
curried_proc[1][2][3]  #=> {:a=>1, :b=>2, :c=>3} 

È possibile passare un numero qualsiasi di argomenti in una sola volta per un proc al curry:

curried_proc[1][2][3]  #=> {:a=>1, :b=>2, :c=>3} 
curried_proc[1, 2][3]  #=> {:a=>1, :b=>2, :c=>3} 
curried_proc[1][2, 3]  #=> {:a=>1, :b=>2, :c=>3} 
curried_proc[1, 2, 3]  #=> {:a=>1, :b=>2, :c=>3} 

argomenti vuoti vengono ignorati:

curried_proc[1][][2][][3] #=> {:a=>1, :b=>2, :c=>3} 

Tuttavia, ovviamente non puoi modificare l'ordine degli argomenti.


Un'alternativa currying è applicazione parziale che restituisce una nuova proc con arity inferiore fissando uno o più argomenti. A differenza curry, non c'è alcun metodo incorporato per l'applicazione parziale, ma si può facilmente scrivere il proprio:

my_proc = -> (arg, num) { arg * num } 

def fix_first(proc, arg) 
    -> (*args) { proc[arg, *args] } 
end 

fixed_proc = fix_first(my_proc, 'foo') #=> #<Proc:0x007fa31c2070d0 (lambda)> 
fixed_proc[2] #=> "foofoo" 
fixed_proc[3] #=> "foofoofoo" 

[2, 3].map(&fixed_proc) #=> ["foofoo", "foofoofoo"] 

o il fissaggio l'ultimo argomento:

def fix_last(proc, arg) 
    -> (*args) { proc[*args, arg] } 
end 

fixed_proc = fix_last(my_proc, 2) #=> #<Proc:0x007fa31c2070d0 (lambda)> 
fixed_proc['foo'] #=> "foofoo" 
fixed_proc['bar'] #=> "barbar" 

['foo', 'bar'].map(&fixed_proc) #=> ["foofoo", "barbar"] 

Naturalmente, non si è limitato a fissare singoli argomenti. Si potrebbe ad esempio restituire un proc che prende un array e lo converte in un elenco di argomenti:

def splat_args(proc) 
    -> (array) { proc[*array] } 
end 

splatting_proc = splat_args(my_proc) 
[['foo', 1], ['bar', 2], ['baz', 3]].map(&splatting_proc) 
#=> ["foo", "barbar", "bazbazbaz"] 
6

@sawa ha ragione. Puoi farlo con curry. Versione

Proc: versione

mult = proc {|a, b| a * b} # => #<Proc:[email protected](irb):32> 
[1, 2].map(&mult.curry[2]) # => [2, 4] 

Metodo:

def mult(a, b) 
    a*b 
end 

[1, 2].map(&method(:mult).to_proc.curry[2]) # => [2, 4] 
+2

L'ultima riga fa riferimento alla variabile locale 'mult' che è stata assegnata nella prima riga, non al metodo. – Stefan

+0

@Stefan: buona presa. – fylooi

+2

Si potrebbe usare '[1, 2] .map (& method (: mult) .to_proc.curry [2])', sebbene sembri macchinoso. – Stefan

Problemi correlati