2016-01-15 3 views
7

Wir können einfach eine Methode definieren und sie in einen Block mit einem unformalen Ampersand umwandeln.Ruby: Geben Sie ein Argument an, während Sie proc in einen Block umwandeln

Wie wir sehen, wird das erste Argument automatisch übergeben, wenn wir mit Aggregaten arbeiten. Was aber, wenn wir 2 oder noch mehr Argumente übergeben müssen?

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) 

Ist es möglich, zusätzliche Argumente übergeben während Proc zu einem Block zu drehen?

+0

Wollen Sie beiden Argumente von der aufgerufenen Methode übergeben werden durch Nachgeben oder haben Sie ein Argument angeben mögen bei der Einreichung der Block zur Methode? –

+2

Nur um das zu bemerken "['foo', 'bar'].Jede (& my_method) 'würde keinen Sinn ergeben, da' each' immer nur ein Element ergibt. Beantworte deine Frage nicht, aber überprüfe '['foo', 3] .each_slice (2, & my_method)'. – ndn

+2

Ich denke, dass "Curry" ein Schlüssel zu dieser Frage sein könnte. – sawa

Antwort

1

In Bezug auf your comment:

Seltsam, aber es tauscht Argumente während der Performance

Tatsächlich ist die Argumentreihenfolge erhalten.

curry gibt einen neuen Proc zurück, der effektiv Argumente sammelt, bis genügend Argumente vorhanden sind, um die ursprüngliche Methode/proc (basierend auf ihrer Arity) aufzurufen. Dies wird durch die Rückkehr Zwischen Procs erreicht:

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} 

Sie eine beliebige Anzahl von Argumenten übergeben kann auf einmal zu einer curried proc:

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} 

Leere Argumente werden ignoriert:

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

jedoch Sie können die Reihenfolge der Argumente natürlich nicht ändern.


Eine Alternative zu currying ist Teil Anwendung, die durch Fixieren einer oder mehrerer Argumente eine neue proc mit niedrigeren arity zurückgibt. Im Gegensatz zu curry, gibt es keine integrierte Methode für die partielle Anwendung, aber Sie können leicht schreiben Sie Ihre eigene:

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"] 

Oder das letzte Argument Festsetzung:

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"] 

Natürlich sind Sie nicht beschränkt auf das Reparieren einzelner Argumente. Sie könnten zum Beispiel eine proc zurückgeben, die ein Array nimmt und wandelt es in eine Argumentliste:

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 ist richtig. Sie können das mit curry tun.

Proc Version:

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

Methode Version:

def mult(a, b) 
    a*b 
end 

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

Ihre letzte Zeile verweist auf die lokale Variable 'mult', die in der ersten Zeile zugewiesen wurde, nicht auf die Methode. – Stefan

+0

@Stefan: Guter Fang. – fylooi

+2

Sie könnten '[1, 2] .map (& method (: mult) .to_proc.curry [2])' 'verwenden, obwohl es umständlich aussieht. – Stefan

Verwandte Themen