2016-01-15 6 views
7

Możemy łatwo zdefiniować metodę i przekształcić ją w blok z unarnym znakiem ampersand.Ruby: dostarczaj argumentu podczas przekształcania procesu na blok

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 

Jak widzimy pierwszy argument jest przekazywany automatycznie, gdy pracujemy z agregatami. Ale co, jeśli musimy przekazać 2 lub nawet więcej argumentów?

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) 

Czy można przekazać dodatkowe argumenty podczas przekształcania procesu w blok?

+0

Chcesz oba argumenty mają być przekazywane z zwaną metodą przez plonowanie, lub chcesz, aby określić jeden argument przy składaniu blok do metody? –

+2

Pamiętaj tylko, że '['foo', 'bar'].each (& my_method) 'nie miałoby sensu, ponieważ' each' zawsze przyniesie tylko jeden element. Nie odpowiadając na twoje pytanie, ale sprawdź '['foo', 3] .each_slice (2, & my_method)'. – ndn

+2

Myślę, że "curry" może być kluczem do tego pytania. – sawa

Odpowiedz

1

chodzi your comment:

Dziwne, ale zamienia argumentów podczas wykonywania

W rzeczywistości, porządek argumentów jest zachowany.

curry zwraca nowy proces, który skutecznie zbiera argumenty, dopóki nie będzie wystarczającej liczby argumentów do wywołania oryginalnej metody/proc (w oparciu o jej aritę). Osiąga się to przez powracających proca pośrednimi:

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} 

Możesz przekazać dowolną liczbę argumentów naraz do curry 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} 

Puste argumenty są ignorowane:

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

jednak oczywiście nie można zmienić kolejności argumentów.


Alternatywą do zmiękczania jest częściowe stosowanie która zwraca nowy proc o niższej Arity przez zamocowanie jednej lub więcej argumentów. W przeciwieństwie curry, nie ma wbudowanej metody częściowego zastosowania, ale można łatwo pisać własne:

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

utrwalacze ostatni argument:

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

oczywiście, nie jesteś ograniczone do ustalania pojedynczych argumentów. Można na przykład zwrócić proc, która pobiera tablicę i konwertuje go na liście argumentów:

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 ma rację. Możesz to zrobić za pomocą curry. Wersja

Proc: wersja

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

Metoda:

def mult(a, b) 
    a*b 
end 

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

Twój ostatni wiersz odwołuje się do zmiennej lokalnej 'mult', która została przypisana w pierwszym wierszu, a nie do metody. – Stefan

+0

@Stefan: Dobry połów. – fylooi

+2

Można użyć '[1, 2] .map (i metody (: mult) .to_proc.curry [2])', chociaż wygląda na nieporęczne. – Stefan

Powiązane problemy