A kiedy użyłbyś jednego zamiast drugiego?
A kiedy użyłbyś jednego zamiast drugiego?
Odpowiedzi:
Jedna różnica dotyczy sposobu, w jaki radzą sobie z argumentami. Tworzenie proc przy użyciu proc {}i Proc.new {}są równoważne. Jednak użycie lambda {}daje ci proces, który sprawdza liczbę argumentów przekazanych do niego. Od ri Kernel#lambda:
Równoważne z Proc.new , z tym wyjątkiem, że wynikowe obiekty Proc sprawdzają liczbę parametrów przekazanych podczas wywołania.
Przykład:
p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)
Ponadto, jak podkreśla Ken, użycie returnwewnątrz lambdy zwraca wartość tej lambdy, ale użycie returnw proc zwraca z otaczającego bloku.
lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return
Więc dla większości szybkich zastosowań są takie same, ale jeśli chcesz automatycznie, ścisłe sprawdzanie argumentów (co czasami może pomóc w debugowaniu) lub jeśli potrzebujesz użyć returninstrukcji do zwrócenia wartości proc, użyj lambda.
Prawdziwa różnica między procesami a lambdami ma wszystko wspólnego ze słowami kluczowymi przepływu sterowania. Mówię return, raise, break, redo, retryitd. - tymi słowami kontrolnymi. Powiedzmy, że masz instrukcję return w proc. Gdy wywołasz swój proc, nie tylko wyrzuci Cię z niego, ale także wróci z otaczającej metody, np .:
def my_method
puts "before proc"
my_proc = Proc.new do
puts "inside proc"
return
end
my_proc.call
puts "after proc"
end
my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
Finał putsw metodzie nigdy nie został wykonany, ponieważ kiedy wywołaliśmy nasz proces, returnjego wnętrze wyrzuciło nas z metody. Jeśli jednak zamienimy nasz proc na lambdę, otrzymamy:
def my_method
puts "before proc"
my_proc = lambda do
puts "inside proc"
return
end
my_proc.call
puts "after proc"
end
my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
after proc
Zwrot w lambdzie powoduje tylko wyrzucenie nas z samej lambdy, a otaczająca metoda kontynuuje wykonywanie. Sposób, w jaki słowa kluczowe przepływu sterowania są traktowane w procedurach i lambdach, jest główną różnicą między nimi
Istnieją tylko dwie główne różnice.
lambdasprawdza liczbę przekazanych do niego argumentów, a procnie. Oznacza to, że a lambdazgłosi błąd, jeśli przekażesz mu niewłaściwą liczbę argumentów, podczas gdy a proczignoruje nieoczekiwane argumenty i przypisze nildo tych, których brakuje.lambdapowraca, przekazuje kontrolę z powrotem do metody wywołującej; kiedy a proczwraca, robi to natychmiast, bez wracania do metody wywołującej.Aby zobaczyć, jak to działa, spójrz na poniższy kod. Nasza pierwsza metoda wywołuje a proc; druga nazywa a lambda.
def batman_ironman_proc
victor = Proc.new { return "Batman will win!" }
victor.call
"Iron Man will win!"
end
puts batman_ironman_proc # prints "Batman will win!"
def batman_ironman_lambda
victor = lambda { return "Batman will win!" }
victor.call
"Iron Man will win!"
end
puts batman_ironman_lambda # prints "Iron Man will win!"
Zobacz, jak procmówi „Batman wygra!”, Ponieważ wraca natychmiast, bez powrotu do metody batman_ironman_proc.
Nasz lambdajednak wraca do metody po wywołaniu, więc metoda zwraca ostatni kod, który ocenia: „Iron Man wygra!”
# Przykłady procesów
p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p) # The '&' tells ruby to turn the proc into a block
proc = Proc.new { puts "Hello World" }
proc.call
# Przykłady lambda
lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)
lam = lambda { puts "Hello World" }
lam.call
Różnice między procesami a lambdami
Zanim przejdę do różnic między procesami a lambdami, ważne jest, aby wspomnieć, że oba są obiektami Proc.
proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }
proc.class # returns 'Proc'
lam.class # returns 'Proc'
Jednak lambdy to inny „smak” procesów. Ta niewielka różnica jest widoczna podczas zwracania obiektów.
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
1. Lambdy sprawdzają liczbę argumentów, podczas gdy procesy nie
lam = lambda { |x| puts x } # creates a lambda that takes 1 argument
lam.call(2) # prints out 2
lam.call # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3) # ArgumentError: wrong number of arguments (3 for 1)
W przeciwieństwie do tego, procy nie przejmują się, jeśli przekazano im złą liczbę argumentów.
proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2) # prints out 2
proc.call # returns nil
proc.call(1,2,3) # prints out 1 and forgets about the extra arguments
2. Lambdy i procesy różnie traktują słowo kluczowe „return”
„return” wewnątrz wyrażenia lambda wyzwala kod bezpośrednio poza kodem lambda
def lambda_test
lam = lambda { return }
lam.call
puts "Hello world"
end
lambda_test # calling lambda_test prints 'Hello World'
'return' wewnątrz proca wyzwala kod poza metodą, w której proc jest wykonywany
def proc_test
proc = Proc.new { return }
proc.call
puts "Hello world"
end
proc_test # calling proc_test prints nothing
A żeby odpowiedzieć na Twoje inne pytanie, którego użyć i kiedy? Będę śledził @jtbandes, jak wspomniał
Więc dla większości szybkich zastosowań są takie same, ale jeśli chcesz automatycznie, ścisłe sprawdzanie argumentów (co czasami może pomóc w debugowaniu) lub jeśli musisz użyć instrukcji return, aby zwrócić wartość proc, użyj lambda.
Pierwotnie opublikowane tutaj
Mówiąc ogólnie, lambdy są bardziej intuicyjne niż procesy, ponieważ są bardziej podobne do metod. Są dość surowe, jeśli chodzi o arity, i po prostu wychodzą, gdy wywołujesz zwrot. Z tego powodu wielu Rubyistów używa lambd jako pierwszego wyboru, chyba że potrzebują specyficznych cech procs.
Procesy: Obiekty klasy Proc. Podobnie jak bloki, są oceniane w zakresie, w którym zostały zdefiniowane.
Lambdy: Również obiekty klasy, Procale nieznacznie różnią się od zwykłych procesów. Są domknięciami, takimi jak bloki i procesy, i jako takie są oceniane w zakresie, w którym zostały zdefiniowane.
Tworzenie proc
a = Proc.new { |x| x 2 }
Tworzenie lambdy
b = lambda { |x| x 2 }
a = proc { |x| x 2 }jest taki sam jaka = Proc.new { |x| x 2 }
Oto inny sposób, aby to zrozumieć.
Blok to fragment kodu dołączony do wywołania metody na obiekcie. W poniższym przykładzie self jest instancją anonimowej klasy dziedziczącej po ActionView :: Base we frameworku Rails (który sam zawiera wiele modułów pomocniczych). karta to metoda, którą nazywamy siebie. Przekazujemy argument do metody, a następnie zawsze dołączamy blok na końcu wywołania metody:
self.card :contacts do |c|
// a chunk of valid ruby code
end
Ok, więc przekazujemy fragment kodu do metody. Ale jak wykorzystujemy ten blok? Jedną z opcji jest przekonwertowanie fragmentu kodu na obiekt. Ruby oferuje trzy sposoby na przekształcenie fragmentu kodu w obiekt
# lambda
> l = lambda { |a| a + 1 }
> l.call(1)
=> 2
# Proc.new
> l2= Proc.new { |a| a + 1 }
> l2.call(1)
=> 2
# & as the last method argument with a local variable name
def add(&block)
end
W powyższej metodzie & konwertuje blok przekazany do metody na obiekt i zapisuje ten obiekt w bloku zmiennej lokalnej. W rzeczywistości możemy pokazać, że zachowuje się tak samo jak lambda i Proc.new:
def add(&block)
block
end
l3 = add { |a| a + 1 }
l3.call(1)
=> 2
To jest ważne. Kiedy przekazujesz blok do metody i konwertujesz ją za pomocą &, tworzony przez niego obiekt używa Proc.new do wykonania konwersji.
Zauważ, że unikałem użycia "proc" jako opcji. To dlatego, że jest to Ruby 1.8, jest taki sam jak lambda, aw Ruby 1.9 to to samo, co Proc.new i we wszystkich wersjach Rubiego należy tego unikać.
Więc pytasz jaka jest różnica między lambda i Proc.new?
Po pierwsze, jeśli chodzi o przekazywanie parametrów, lambda zachowuje się jak wywołanie metody. Zgłosi wyjątek, jeśli podasz niewłaściwą liczbę argumentów. Natomiast Proc.new zachowuje się jak przypisanie równoległe. Wszystkie nieużywane argumenty są zamieniane na zero:
> l = lambda {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb47e40@(irb):19 (lambda)>
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)
> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb261a0@(irb):21>
> l2.call(1)
1 +
Po drugie, lambda i Proc.new inaczej obsługują słowo kluczowe return. Kiedy wykonujesz powrót wewnątrz Proc.new, w rzeczywistości wraca on z metody otaczającej, czyli z otaczającego kontekstu. Kiedy wracasz z bloku lambda, po prostu wraca z bloku, a nie z otaczającej metody. Zasadniczo wychodzi z wywołania do bloku i kontynuuje wykonywanie z resztą otaczającej metody.
> def add(a,b)
l = Proc.new { return a + b}
l.call
puts "now exiting method"
end
> add(1,1)
=> 2 # NOTICE it never prints the message "now exiting method"
> def add(a,b)
l = lambda { return a + b }
l.call
puts "now exiting method"
end
> add(1,1)
=> now exiting method # NOTICE this time it prints the message "now exiting method"
Skąd więc ta różnica w zachowaniu? Powodem jest to, że dzięki Proc.new możemy używać iteratorów w kontekście otaczających metod i wyciągać logiczne wnioski. Spójrz na ten przykład:
> def print(max)
[1,2,3,4,5].each do |val|
puts val
return if val > max
end
end
> print(3)
1
2
3
4
Oczekujemy, że gdy wywołasz return wewnątrz iteratora, zwróci on z otaczającej metody. Pamiętaj, że bloki przekazywane do iteratorów są konwertowane na obiekty przy użyciu Proc.new i dlatego kiedy używamy return, zakończy on otaczającą metodę.
Możesz myśleć o lambdach jako o metodach anonimowych, izolują one poszczególne bloki kodu w obiekt, który można traktować jak metodę. Ostatecznie pomyśl, że lambda zachowuje się jak anomiczna metoda, a Proc.new zachowuje się jak kod wbudowany.
Pomocny post o przewodnikach po Ruby: blokach, procesach i lambdach
Procenty zwracają się z bieżącej metody, podczas gdy lambdy wracają z samej lambdy.
Procesy nie przejmują się poprawną liczbą argumentów, podczas gdy lambdy będą zgłaszać wyjątek.
returnpowraca oświadczenie wprocporównaniulambda.