Kiedy używać lambda, kiedy użyć Proc.new?

głosy
316

W Ruby 1.8, istnieją subtelne różnice między proc / lambda z jednej strony, a Proc.newz drugiej strony.

  • Jakie są te różnice?
  • Można dać wskazówki, jak zdecydować, który z nich wybrać?
  • Ruby 1,9 Proc i lambda są różne. O co chodzi?
Utwórz 03/08/2008 o 07:40
źródło użytkownik
W innych językach...                            


15 odpowiedzi

głosy
364

Innym ważnym ale subtelna różnica między proca utworzonych lambdai proca tworzone Proc.newjest, jak radzą sobie na returnstwierdzenie:

  • W lambda-created proc The returninstrukcja zwraca tylko z samego proc
  • W Proc.new-created proc The returnstwierdzenie jest trochę bardziej zaskakujące: zwraca kontrolę nie tylko z proc, ale również od sposobu załączając proc!

Oto lambda-created proc użytkownika returnw akcji. Zachowuje się w sposób, który prawdopodobnie spodziewać:

def whowouldwin

  mylambda = lambda {return "Freddy"}
  mylambda.call

  # mylambda gets called and returns "Freddy", and execution
  # continues on the next line

  return "Jason"

end


whowouldwin
#=> "Jason"

Teraz oto Proc.new-created proc użytkownika returnrobi to samo. Masz zamiar zobaczyć jeden z tych przypadków, w których Ruby łamie wiele-osławionej zasady najmniejszych niespodzianka:

def whowouldwin2

  myproc = Proc.new {return "Freddy"}
  myproc.call

  # myproc gets called and returns "Freddy", 
  # but also returns control from whowhouldwin2!
  # The line below *never* gets executed.

  return "Jason"

end


whowouldwin2         
#=> "Freddy"

Dzięki tej zaskakującej zachowania (jak również mniej pisania), ja faworyzują przy użyciu lambdaponad Proc.newpodczas dokonywania procs.

Odpowiedział 03/08/2008 o 16:21
źródło użytkownik

głosy
93

Dostarczenie dodatkowych wyjaśnień:

Joey mówi, że zachowanie powrót Proc.newjest zaskakujące. Jednak jeśli wziąć pod uwagę, że zachowuje się jak Proc.new bloku nie jest to zaskakujące, jako że jest dokładnie tak, jak zachowują się blokuje. lambas natomiast zachowują się bardziej jak metod.

To wyjaśnia, dlaczego właściwie Procs są elastyczne, jeśli chodzi o liczbę operandów (liczba argumentów), podczas gdy lambda nie są. Bloki nie wymagają wszystkie ich argumenty mają być zapewnione, ale Metody zrobić (chyba że zapewniona jest domyślnie). Zapewniając domyślne argumentów N nie jest to możliwe w Ruby 1.8, jest obecnie obsługiwana w Ruby 1,9 z alternatywnym składni lambda (jak wskazano przez webmat)

concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1)   # => "12"

I Michiel de Mare (PO) jest niepoprawny o PROC i lambda zachowują to samo z Arity Ruby 1.9. I sprawdzeniu, że nadal utrzymują zachowanie od 1,8 jak podano powyżej.

breakSprawozdanie w rzeczywistości nie ma większego sensu w obu Procs lub lambda. W Procs, przerwa będzie powrót z Proc.new która została już zakończona. I to nie ma sensu, aby zerwać z lambda, ponieważ jest to w istocie sposób, i nigdy nie pęknie z najwyższym poziomie metody.

next, redoI raisezachowują się tak samo w obu PROC i lambda. Natomiast retrynie jest dozwolone w jednej i podniesie wyjątek.

I wreszcie, procmetoda nigdy nie powinny być stosowane, ponieważ jest niespójna i ma nieoczekiwane zachowanie. W Ruby 1.8 to faktycznie zwraca lambda! W Ruby 1.9 ten został naprawiony i zwraca Proc. Jeśli chcesz utworzyć Proc, trzymaj się Proc.new.

Aby uzyskać więcej informacji, bardzo polecam O'Reilly Ruby Programming Language , który jest moim źródłem większości informacji.

Odpowiedział 04/10/2009 o 06:23
źródło użytkownik

głosy
41

Znalazłem stronę , która pokazuje, jaka jest różnica między Proc.newi lambdasą. Według strony, jedyną różnicą jest to, że lambda jest ścisła o liczbie argumentów to akceptuje, a Proc.newnawróceni brakuje argumentów nil. Oto przykład ilustrujący IRB sesja różnicę:

IRB (główny): 001: 0> L = lambda {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (IRB): 1>
IRB (główny): 002: 0> P = Proc.new {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (IRB) 2>
IRB (main): 003: 0> l.call "cześć", "świat"
=> "Helloworld"
IRB (main): 004: 0> p.call "cześć", "świat"
=> "Helloworld"
IRB (main): 005: 0> l.call "cześć"
ArgumentError: zła ilość argumentów (1 do 2)
    z (IRB): 1
    od (IRB): 5: w `rozmowy”
    z (IRB): 5
    od: 0
IRB (main): 006: 0> p.call "cześć"
TypeError: nie można przekonwertować na ciąg nil
    z (IRB): 2: w '+”
    z (IRB): 2
    od (IRB) 6: w `rozmowy”
    z (IRB): 6
    od: 0

Strona zaleca również za pomocą lambda chyba że wyraźnie chce Błąd tolerancyjne zachowanie. Zgadzam się z tym uczuciem. Korzystanie z lambda wydaje się odrobinę bardziej zwięzłe i tak nieznacznej różnicy, wydaje się lepszym wyborem w średniej sytuacji.

Jak dla Ruby 1.9, przepraszam, ja nie spojrzał jeszcze 1,9, ale nie wyobrażam sobie, by zmienili go aż tak dużo (nie biorą za to moje słowo, choć wydaje się, słyszeliście o pewnych zmianach, tak Jestem prawdopodobnie źle tam).

Odpowiedział 03/08/2008 o 08:28
źródło użytkownik

głosy
14

Proc jest starszy, ale semantyka zamian są bardzo sprzeczne z intuicją do mnie (przynajmniej kiedy nauka języka), ponieważ:

  1. Jeśli używasz proc, które są najczęściej za pomocą jakiegoś paradygmatu funkcjonalnego.
  2. Proc może powrócić z zakresu obejmującego (patrz poprzednie reakcji), co jest w zasadzie Goto i wysoce niefunkcjonalne w naturze.

Lambda jest funkcjonalnie bezpieczniejsze i łatwiejsze do rozumu o - Zawsze używam go zamiast proc.

Odpowiedział 11/09/2008 o 00:32
źródło użytkownik

głosy
11

Nie mogę wiele powiedzieć o subtelnych różnic. Mogę jednak podkreślić, że Ruby 1.9 pozwala teraz opcjonalne parametry lambda i bloków.

Oto nowa składnia stabby lambdas poniżej 1,9:

stabby = ->(msg='inside the stabby lambda') { puts msg }

Ruby 1.8 nie mają tę składnię. Ani konwencjonalny sposób deklarowania bloki / lambdas obsługują opcjonalne argumenty:

# under 1.8
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }
SyntaxError: compile error
(irb):1: syntax error, unexpected '=', expecting tCOLON2 or '[' or '.'
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }

Ruby 1.9, jednak obsługuje opcjonalne argumenty nawet z starej składni:

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

Jeśli chcesz zbudować Ruby1.9 dla Leoparda lub Linux, sprawdź ten artykuł (bezwstydny autopromocji).

Odpowiedział 19/11/2008 o 22:28
źródło użytkownik

głosy
10

Krótka odpowiedź: Liczy się to, co returnrobi: lambda powraca z siebie i proc powraca z siebie, a funkcja, która nazwie go.

Co to jest mniej jasne jest dlaczego chcesz korzystać każdy. lambda to, czego oczekujemy rzeczy, które należy zrobić w sensie funkcjonalnym programowania. To jest w zasadzie anonimowa metoda z bieżącego zakresu automatycznie związana. Z tych dwóch, lambda jest jednym powinieneś używać.

Proc, z drugiej strony, jest bardzo przydatny do realizacji samego języka. Na przykład można zaimplementować „jeśli” oświadczeń lub pętli „for” z nimi. Wszelkie powrót znaleźć w proc powróci z metody, która nazwie go, a nie tylko „if”. Jest to, jak działają języki, jak „czy” oświadczenia pracy, więc moje przypuszczenie jest Ruby wykorzystuje to pod kołdrą i po prostu narażony, bo wydawało potężny.

Można by tylko naprawdę trzeba to w przypadku tworzenia nowych konstrukcji językowych jak pętle, if-else konstruktów, itp

Odpowiedział 06/10/2011 o 19:33
źródło użytkownik

głosy
9

Dobrym sposobem, aby ją zobaczyć, że lambdas są wykonywane we własnym zakresie (jakby to było wywołanie metody), natomiast Procs może być postrzegany jako wykonywane inline z metody wywołującej, a przynajmniej, że to dobry sposób decydowania Wich jedno użycie w każdej sprawie.

Odpowiedział 09/12/2008 o 15:17
źródło użytkownik

głosy
8

Nie zauważyłem żadnych komentarzy na temat trzeciej metody w Queston „proc”, która jest przestarzała, ale traktowane odmiennie w 1,8 i 1,9.

Oto dość rozwlekły przykład sprawia, że ​​łatwo zobaczyć różnice między trzema podobnych połączeń:

def meth1
  puts "method start"

  pr = lambda { return }
  pr.call

  puts "method end"  
end

def meth2
  puts "method start"

  pr = Proc.new { return }
  pr.call

  puts "method end"  
end

def meth3
  puts "method start"

  pr = proc { return }
  pr.call

  puts "method end"  
end

puts "Using lambda"
meth1
puts "--------"
puts "using Proc.new"
meth2
puts "--------"
puts "using proc"
meth3
Odpowiedział 25/06/2009 o 12:22
źródło użytkownik

głosy
7

Zamknięcia w Ruby jest dobry przegląd na jak bloki, lambda i praca proc w Ruby, Ruby.

Odpowiedział 28/08/2008 o 14:50
źródło użytkownik

głosy
6

Zrozumienie Ruby bloki, proca i lambdas Robert Sosiński jasno wyjaśnia te koncepcje programowe i wzmacnia wyjaśnień z przykładowym kodem. Metoda obiekty są związane i objęte również.

Odpowiedział 23/08/2013 o 18:07
źródło użytkownik

głosy
5

lambda działa zgodnie z oczekiwaniami, podobnie jak w innych językach.

Przewodowy Proc.newjest zaskakujące i kłopotliwe.

returnOświadczenie w proc utworzonego przez Proc.newnie tylko zwrócić sterowanie tylko od siebie, ale także od sposobu zamykania go .

def some_method
  myproc = Proc.new {return "End."}
  myproc.call

  # Any code below will not get executed!
  # ...
end

Można argumentować, że Proc.newwstawia kod do metody okalającego, podobnie jak blok. Ale Proc.newtworzy obiekt, natomiast blok są częścią obiektu.

I jest jeszcze jedna różnica między lambda i Proc.new, co jest ich obsługa (źle) argumentów. lambda narzeka na niego, podczas gdy Proc.newignoruje dodatkowe argumenty czy uważa brak argumentów za zerowy.

irb(main):021:0> l = -> (x) { x.to_s }
=> #<Proc:0x8b63750@(irb):21 (lambda)>
irb(main):022:0> p = Proc.new { |x| x.to_s}
=> #<Proc:0x8b59494@(irb):22>
irb(main):025:0> l.call
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):21:in `block in irb_binding'
        from (irb):25:in `call'
        from (irb):25
        from /usr/bin/irb:11:in `<main>'
irb(main):026:0> p.call
=> ""
irb(main):049:0> l.call 1, 2
ArgumentError: wrong number of arguments (2 for 1)
        from (irb):47:in `block in irb_binding'
        from (irb):49:in `call'
        from (irb):49
        from /usr/bin/irb:11:in `<main>'
irb(main):050:0> p.call 1, 2
=> "1"

BTW, procw Ruby 1.8 tworzy lambda, natomiast w Ruby 1.9+ zachowuje się podobnie Proc.new, co jest bardzo mylące.

Odpowiedział 29/10/2014 o 16:22
źródło użytkownik

głosy
3

Aby rozwinąć odpowiedzi akordeon Guy:

Zauważ, że Proc.newtworzy proc przez były przekazywane do bloku. Wierzę, że lambda {...}jest analizowany jako rodzaj dosłowne, a nie wywołanie metody, która przechodzi do bloku. returning od wewnątrz bloku dołączonym do wywołania metody powróci ze sposobu, a nie bloku, a Proc.newsprawa jest przykładem tego w grze.

(Jest to 1,8. Nie wiem, jak to przekłada się na 1,9).

Odpowiedział 07/09/2008 o 03:31
źródło użytkownik

głosy
2

Jestem trochę późno na to, ale jest jeden wielki, ale mało znany rzeczą Proc.newnie wspomniano w ogóle w komentarzach. Jak przez dokumentacją :

Proc::newmoże być wywołany bez bloku tylko w sposobie z przyłączonym bloku, w tym przypadku, że blok jest przetwarzany naProc obiekcie.

Aby ten Proc.newpozwala sposobów łańcucha otrzymując:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
Odpowiedział 28/04/2015 o 13:15
źródło użytkownik

głosy
1

Warto podkreślić, że returnw ciągu zwrotów proc z metody leksykalnie zamykającego, czyli sposobu, w którym został utworzony proc , nie metoda, która nazywana proc. Wynika to z właściwości zamknięcia proca. Więc następujący kod wyprowadza nic:

def foo
  proc = Proc.new{return}
  foobar(proc)
  puts 'foo'
end

def foobar(proc)
  proc.call
  puts 'foobar'
end

foo

Chociaż proc wykonuje się foobar, że został stworzony w fooi tak returnwychodzi foo, nie tylko foobar. Jak Charles Caldwell napisał powyżej, posiada GOTO czują do niego. Moim zdaniem returnjest w porządku w bloku, który jest wykonywany w kontekście leksykalnym, ale jest znacznie mniej intuicyjne użyte w proc, który jest wykonywany w innym kontekście.

Odpowiedział 15/11/2018 o 10:00
źródło użytkownik

głosy
1

Różnica w zachowaniu z returnjest IMHO najważniejsza różnica między 2. Ja też wolę lambda bo to mniej niż wpisywanie Proc.new :-)

Odpowiedział 11/08/2008 o 03:09
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more