Zobacz także: książka Ruby Programming Language autorstwa Matza i Flanagana, obszernie omówiła ten temat. proc zachowuje się jak semantyka blokowo-wydajna, gdzie jak lambda zachowuje się jak semantyka wywołania metody. Również return, break, i. wszyscy zachowują się inaczej w procs n lambdas
zaakceptowałeś odpowiedź, która mówi tylko, jaka jest różnica między proc a lambda, a tytuł twojego pytania brzmi, kiedy używać tych rzeczy
Shri
Odpowiedzi:
378
Inną ważną, ale subtelną różnicą między procs utworzonymi za pomocą lambdai procs utworzonych za pomocą Proc.newjest sposób obsługi returninstrukcji:
W lambdautworzonym proc, returninstrukcja zwraca tylko od samego proc
W Proc.newutworzonym proc, returninstrukcja jest nieco bardziej zaskakująca: zwraca kontrolę nie tylko z proc, ale także z metody otaczającej proc!
Oto lambdautworzone procy returnw akcji. Zachowuje się w sposób, którego prawdopodobnie się spodziewasz:
def whowouldwin
mylambda = lambda {return"Freddy"}
mylambda.call
# mylambda gets called and returns "Freddy", and execution# continues on the next linereturn"Jason"end
whowouldwin
#=> "Jason"
Teraz tutaj-stworzony Proc.newproc returnrobi to samo. Za chwilę zobaczysz jeden z przypadków, w których Ruby łamie bardzo chwaloną zasadę najmniejszej niespodzianki:
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.
@mattdipasquale W moich testach proczachowuje się jak lambdai nie Proc.neww odniesieniu do instrukcji return. Oznacza to, że rubinowy dokument jest niedokładny.
Kelvin
31
@mattdipasquale Przepraszam, miałem tylko połowę racji. procdziała jak lambdaw 1.8, ale działa jak Proc.neww 1.9. Zobacz odpowiedź Petera Wageneta.
Kelvin
55
Dlaczego to „zaskakujące” zachowanie? A lambdajest anonimową metodą. Ponieważ jest to metoda, zwraca wartość, a metoda, która ją wywołała, może zrobić z nią, co chce, włączając w to zignorowanie jej i zwrócenie innej wartości. A Procjest jak wklejanie fragmentu kodu. To nie działa jak metoda. Więc kiedy zwrot nastąpi w obrębie Proc, to tylko część kodu metody, która go wywołała.
Arcolye
96
Aby udzielić dalszych wyjaśnień:
Joey mówi, że zachowanie powrotu Proc.newjest zaskakujące. Jednak biorąc pod uwagę, że Proc.new zachowuje się jak blok, nie jest to zaskakujące, ponieważ dokładnie tak zachowują się bloki. z drugiej strony lambas zachowują się bardziej jak metody.
To faktycznie tłumaczy, dlaczego Procs są elastyczni, jeśli chodzi o arity (liczba argumentów), podczas gdy lambdas nie. Bloki nie wymagają podania wszystkich argumentów, ale wymagają tego metody (chyba że podano wartość domyślną). Chociaż podanie domyślnego argumentu lambda nie jest opcją w Ruby 1.8, jest ono obecnie obsługiwane w Ruby 1.9 z alternatywną składnią lambda (jak zauważył webmat):
A Michiel de Mare (OP) nie ma racji co do tego, że Procs i lambda zachowują się tak samo z arity w Ruby 1.9. Sprawdziłem, że nadal zachowują zachowanie z wersji 1.8, jak określono powyżej.
breakinstrukcje faktycznie nie mają większego sensu ani w Procs, ani w lambdach. W Procs przerwa spowoduje powrót z Proc.new, który został już ukończony. I nie ma sensu zerwać z lambda, ponieważ jest to w zasadzie metoda i nigdy nie zerwałbyś z najwyższego poziomu metody.
next, redoi raisezachowuj się tak samo w Procs i lambdas. Chociaż retrynie jest dozwolone w żadnym z nich i spowoduje wyjątek.
I wreszcie, procnigdy nie należy stosować tej metody, ponieważ jest niespójna i ma nieoczekiwane zachowanie. W Ruby 1.8 faktycznie zwraca lambda! W Ruby 1.9 zostało to naprawione i zwraca Proc. Jeśli chcesz utworzyć Proc, trzymaj się Proc.new.
Aby uzyskać więcej informacji, gorąco polecam The Ruby Programming Language O'Reilly'ego, który jest moim źródłem większości tych informacji.
„” „Jednak jeśli weźmiesz pod uwagę, że Proc.new zachowuje się jak blok, nie jest to zaskakujące, ponieważ dokładnie tak zachowują się bloki.” „” <- blok jest częścią obiektu, podczas gdy Proc.new tworzy obiekt. Zarówno lambda, jak i Proc.new tworzy obiekt, którego klasą jest Proc, dlaczego diff?
słaby
1
Jako Ruby 2.5, breakod podbicia proca LocalJumpError, natomiast breakod lambdas zachowuje się podobnie return( tzn , return nil).
Masa Sakano
43
Znalazłem tę stronę, która pokazuje, jaka jest różnica między Proc.newi lambda. Według strony jedyną różnicą jest to, że lambda jest ścisła pod względem liczby argumentów, które akceptuje, podczas gdy Proc.newkonwertuje brakujące argumenty na nil. Oto przykładowa sesja IRB ilustrująca różnicę:
irb (główny): 001: 0> l = lambda {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (irb): 1>
irb (main): 002: 0> p = Proc. new {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (irb): 2>
irb (main): 003: 0> l.call "hello", "world"
=> „helloworld”
irb (main): 004: 0> p.call „hello”, „world”
=> „helloworld”
irb (main): 005: 0> l.call "hello"
ArgumentError: zła liczba argumentów (1 na 2)
z (irb): 1
z (irb): 5: w `call '
z (irb): 5
od: 0
irb (main): 006: 0> p.call "hello"
TypeError: nie można przekonwertować wartości zero na String
z (irb): 2: w `+ '
z (irb): 2
z (irb): 6: w `call '
z (irb): 6
od: 0
Strona zaleca także użycie lambda, chyba że chcesz zachowania tolerancji na błędy. Zgadzam się z tym sentymentem. Stosowanie lambda wydaje się nieco bardziej zwięzłe, a przy tak nieznacznej różnicy wydaje się lepszym wyborem w przeciętnej sytuacji.
Jeśli chodzi o Ruby 1.9, przepraszam, nie zagłębiłem się jeszcze w 1.9, ale nie sądzę, że zmieniliby to aż tak bardzo (nie wierz mi na to, wydaje się, że słyszałeś o niektórych zmianach, Prawdopodobnie się tam mylę).
Opcjonalne parametry w lambda były bardzo potrzebne, cieszę się, że dodały to w wersji 1.9. Zakładam, że bloki mogą mieć również opcjonalne parametry (w 1.9)?
MPD
nie wyświetlasz domyślnych parametrów w blokach, tylko lambdas
iconoclast
11
Krótka odpowiedź: ważne jest to, co return robi: lambda wraca z siebie, a proc wraca z siebie ORAZ funkcję, która ją wywołała.
Mniej jasne jest, dlaczego chcesz z nich korzystać. lambda jest tym, czego oczekujemy, że rzeczy powinny robić w sensie programowania funkcjonalnego. Jest to w zasadzie anonimowa metoda z automatycznie powiązanym bieżącym zakresem. Z tych dwóch lambda jest tym, którego prawdopodobnie powinieneś używać.
Z drugiej strony Proc jest naprawdę przydatny do implementacji samego języka. Na przykład możesz zaimplementować z nimi pętle „if” lub „for”. Wszelkie zwroty znalezione w proc zwrócą się z metody, która je wywołała, a nie tylko z instrukcji „if”. Tak działają języki, jak działają zdania „jeśli”, więc przypuszczam, że Ruby używa tego pod przykrywkami i po prostu je ujawniło, ponieważ wydawało się to potężne.
Będzie to naprawdę potrzebne, jeśli tworzysz nowe konstrukcje językowe, takie jak pętle, konstrukcje if-else itp.
„lambda powraca z siebie, a proc powraca z siebie ORAZ funkcja, która ją wywołała” jest po prostu błędna i bardzo powszechnym nieporozumieniem. Proc to zamknięcie i powrót z metody, która go utworzyła. Zobacz moją pełną odpowiedź w innym miejscu na stronie.
ComDubh
10
Dobrym sposobem, aby to zobaczyć, jest to, że lambd są wykonywane we własnym zakresie (jak gdyby było to wywołanie metody), podczas gdy Procs może być postrzegany jako wykonywany zgodnie z metodą wywołującą, przynajmniej to dobry sposób na decyzję, którego użyć w każdej sprawie.
Matz oświadczył, że planuje go wycofać, ponieważ mylące jest, że proc i nowe zwracają różne wyniki. W wersji 1.9 zachowują się tak samo (proc to alias do Proc. New). eigenclass.org/hiki/Changes+in+Ruby+1.9#l47
Dave Rapin
@banister: proczwrócił lambda w 1.8; teraz naprawiono zwracanie proc w wersji 1.9 - jest to jednak przełomowa zmiana; dlatego nie zaleca się już używania
Gishu
Myślę, że kilof mówi gdzieś w przypisie, że proc jest skutecznie osłabiony czy coś. Nie mam dokładnego numeru strony.
dertoni
7
Zamknięcia w Ruby to dobry przegląd tego, jak działają bloki, lambda i proc w Rubim, z Ruby.
Przestałem to czytać po przeczytaniu „funkcja nie może zaakceptować wielu bloków - naruszając zasadę, że zamknięcia mogą być swobodnie przekazywane jako wartości”. Bloki nie są zamknięciami. Procesory są, a funkcja może akceptować wiele procesów.
ComDubh
5
lambda działa zgodnie z oczekiwaniami, podobnie jak w innych językach.
Przewodowe Proc.newjest zaskakujące i mylące.
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 zamykającej, podobnie jak blok. Ale Proc.newtworzy obiekt, podczas gdy blok jest częścią obiektu.
I jest jeszcze jedna różnica między lambda i Proc.new, która polega na ich (błędnych) argumentach. lambda narzeka na to, a Proc.newignoruje dodatkowe argumenty lub uważa brak argumentów za zero.
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 (0for1)
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,2ArgumentError: wrong number of arguments (2for1)
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, podczas gdy w Ruby 1.9+ zachowuje się jak Proc.new, co jest naprawdę mylące.
Zauważ, że Proc.newtworzy proces, przechodząc blok. Uważam, że lambda {...}jest to parsowane raczej jako dosłowność, niż wywołanie metody, które przechodzi przez blok. returning od wewnątrz bloku dołączonego do wywołania metody powróci z metody, a nie z bloku, iProc.new przypadek jest tego przykładem.
(To jest 1.8. Nie wiem, jak to się tłumaczy na 1.9.)
Co ciekawe, robi to samo, co deklarowanie &blockargumentu na def, ale bez konieczności robienia tego na liście def arg.
jrochkind
2
Warto podkreślić, że returnw proc powraca z metody obejmującej leksykalnie, tj. Z metody, w której proc został utworzony , a nie z metody, która wywołała proc. Jest to konsekwencja właściwości zamknięcia procs. Poniższy kod nic nie wyświetla:
Mimo że proces wykonuje się foobar, został utworzony fooi dlatego returnkończy się foo, nie tylko foobar. Jak napisał Charles Caldwell powyżej, ma w sobie GOTO. Moim zdaniem returnjest dobrze w bloku, który jest wykonywany w kontekście leksykalnym, ale jest znacznie mniej intuicyjny, gdy jest używany w proc, który jest wykonywany w innym kontekście.
Aby zaktualizować: procs można teraz tworzyć za pomocą proc {}. Nie jestem pewien, kiedy to zadziałało, ale jest (nieco) łatwiejsze niż wpisywanie Proc. New.
Odpowiedzi:
Inną ważną, ale subtelną różnicą między procs utworzonymi za pomocą
lambda
i procs utworzonych za pomocąProc.new
jest sposób obsługireturn
instrukcji:lambda
utworzonym proc,return
instrukcja zwraca tylko od samego procProc.new
utworzonym proc,return
instrukcja jest nieco bardziej zaskakująca: zwraca kontrolę nie tylko z proc, ale także z metody otaczającej proc!Oto
lambda
utworzone procyreturn
w akcji. Zachowuje się w sposób, którego prawdopodobnie się spodziewasz:Teraz tutaj-stworzony
Proc.new
procreturn
robi to samo. Za chwilę zobaczysz jeden z przypadków, w których Ruby łamie bardzo chwaloną zasadę najmniejszej niespodzianki:Dzięki tej zaskakującej zachowania (jak również mniej pisania), ja faworyzują przy użyciu
lambda
ponadProc.new
podczas dokonywania procs.źródło
proc
metoda. Czy to tylko skrótProc.new
?proc
jest odpowiednikiemProc.new
proc
zachowuje się jaklambda
i nieProc.new
w odniesieniu do instrukcji return. Oznacza to, że rubinowy dokument jest niedokładny.proc
działa jaklambda
w 1.8, ale działa jakProc.new
w 1.9. Zobacz odpowiedź Petera Wageneta.lambda
jest anonimową metodą. Ponieważ jest to metoda, zwraca wartość, a metoda, która ją wywołała, może zrobić z nią, co chce, włączając w to zignorowanie jej i zwrócenie innej wartości. AProc
jest jak wklejanie fragmentu kodu. To nie działa jak metoda. Więc kiedy zwrot nastąpi w obrębieProc
, to tylko część kodu metody, która go wywołała.Aby udzielić dalszych wyjaśnień:
Joey mówi, że zachowanie powrotu
Proc.new
jest zaskakujące. Jednak biorąc pod uwagę, że Proc.new zachowuje się jak blok, nie jest to zaskakujące, ponieważ dokładnie tak zachowują się bloki. z drugiej strony lambas zachowują się bardziej jak metody.To faktycznie tłumaczy, dlaczego Procs są elastyczni, jeśli chodzi o arity (liczba argumentów), podczas gdy lambdas nie. Bloki nie wymagają podania wszystkich argumentów, ale wymagają tego metody (chyba że podano wartość domyślną). Chociaż podanie domyślnego argumentu lambda nie jest opcją w Ruby 1.8, jest ono obecnie obsługiwane w Ruby 1.9 z alternatywną składnią lambda (jak zauważył webmat):
A Michiel de Mare (OP) nie ma racji co do tego, że Procs i lambda zachowują się tak samo z arity w Ruby 1.9. Sprawdziłem, że nadal zachowują zachowanie z wersji 1.8, jak określono powyżej.
break
instrukcje faktycznie nie mają większego sensu ani w Procs, ani w lambdach. W Procs przerwa spowoduje powrót z Proc.new, który został już ukończony. I nie ma sensu zerwać z lambda, ponieważ jest to w zasadzie metoda i nigdy nie zerwałbyś z najwyższego poziomu metody.next
,redo
iraise
zachowuj się tak samo w Procs i lambdas. Chociażretry
nie jest dozwolone w żadnym z nich i spowoduje wyjątek.I wreszcie,
proc
nigdy nie należy stosować tej metody, ponieważ jest niespójna i ma nieoczekiwane zachowanie. W Ruby 1.8 faktycznie zwraca lambda! W Ruby 1.9 zostało to naprawione i zwraca Proc. Jeśli chcesz utworzyć Proc, trzymaj sięProc.new
.Aby uzyskać więcej informacji, gorąco polecam The Ruby Programming Language O'Reilly'ego, który jest moim źródłem większości tych informacji.
źródło
break
od podbicia procaLocalJumpError
, natomiastbreak
od lambdas zachowuje się podobniereturn
( tzn ,return nil
).Znalazłem tę stronę, która pokazuje, jaka jest różnica między
Proc.new
ilambda
. Według strony jedyną różnicą jest to, że lambda jest ścisła pod względem liczby argumentów, które akceptuje, podczas gdyProc.new
konwertuje brakujące argumenty nanil
. Oto przykładowa sesja IRB ilustrująca różnicę:Strona zaleca także użycie lambda, chyba że chcesz zachowania tolerancji na błędy. Zgadzam się z tym sentymentem. Stosowanie lambda wydaje się nieco bardziej zwięzłe, a przy tak nieznacznej różnicy wydaje się lepszym wyborem w przeciętnej sytuacji.
Jeśli chodzi o Ruby 1.9, przepraszam, nie zagłębiłem się jeszcze w 1.9, ale nie sądzę, że zmieniliby to aż tak bardzo (nie wierz mi na to, wydaje się, że słyszałeś o niektórych zmianach, Prawdopodobnie się tam mylę).
źródło
Proc jest starszy, ale semantyka powrotu jest dla mnie wysoce sprzeczna z intuicją (przynajmniej kiedy uczyłam się języka), ponieważ:
Lambda jest funkcjonalnie bezpieczniejsza i łatwiejsza do uzasadnienia - zawsze używam jej zamiast proc.
źródło
Nie mogę wiele powiedzieć o subtelnych różnicach. Jednak mogę zauważyć, że Ruby 1.9 pozwala teraz na opcjonalne parametry lambd i bloków.
Oto nowa składnia stabby lambdas poniżej 1.9:
Ruby 1.8 nie miał tej składni. Tradycyjny sposób deklarowania bloków / lambdas nie wspierał również opcjonalnych argumentów:
Ruby 1.9 obsługuje jednak opcjonalne argumenty nawet przy starej składni:
Jeśli chcesz zbudować Ruby1.9 dla Leoparda lub Linuksa, sprawdź ten artykuł (bezwstydna autopromocja).
źródło
Krótka odpowiedź: ważne jest to, co
return
robi: lambda wraca z siebie, a proc wraca z siebie ORAZ funkcję, która ją wywołała.Mniej jasne jest, dlaczego chcesz z nich korzystać. lambda jest tym, czego oczekujemy, że rzeczy powinny robić w sensie programowania funkcjonalnego. Jest to w zasadzie anonimowa metoda z automatycznie powiązanym bieżącym zakresem. Z tych dwóch lambda jest tym, którego prawdopodobnie powinieneś używać.
Z drugiej strony Proc jest naprawdę przydatny do implementacji samego języka. Na przykład możesz zaimplementować z nimi pętle „if” lub „for”. Wszelkie zwroty znalezione w proc zwrócą się z metody, która je wywołała, a nie tylko z instrukcji „if”. Tak działają języki, jak działają zdania „jeśli”, więc przypuszczam, że Ruby używa tego pod przykrywkami i po prostu je ujawniło, ponieważ wydawało się to potężne.
Będzie to naprawdę potrzebne, jeśli tworzysz nowe konstrukcje językowe, takie jak pętle, konstrukcje if-else itp.
źródło
Dobrym sposobem, aby to zobaczyć, jest to, że lambd są wykonywane we własnym zakresie (jak gdyby było to wywołanie metody), podczas gdy Procs może być postrzegany jako wykonywany zgodnie z metodą wywołującą, przynajmniej to dobry sposób na decyzję, którego użyć w każdej sprawie.
źródło
Nie zauważyłem żadnych komentarzy na temat trzeciej metody w queston, „proc”, która jest przestarzała, ale inaczej potraktowano ją w 1.8 i 1.9.
Oto dość szczegółowy przykład, który ułatwia dostrzeżenie różnic między trzema podobnymi wywołaniami:
źródło
proc
zwrócił lambda w 1.8; teraz naprawiono zwracanie proc w wersji 1.9 - jest to jednak przełomowa zmiana; dlatego nie zaleca się już używaniaZamknięcia w Ruby to dobry przegląd tego, jak działają bloki, lambda i proc w Rubim, z Ruby.
źródło
lambda działa zgodnie z oczekiwaniami, podobnie jak w innych językach.
Przewodowe
Proc.new
jest zaskakujące i mylące.return
Oświadczenie w proc utworzonego przezProc.new
nie tylko zwrócić sterowanie tylko od siebie, ale także od sposobu zamykania go .Można argumentować, że
Proc.new
wstawia kod do metody zamykającej, podobnie jak blok. AleProc.new
tworzy obiekt, podczas gdy blok jest częścią obiektu.I jest jeszcze jedna różnica między lambda i
Proc.new
, która polega na ich (błędnych) argumentach. lambda narzeka na to, aProc.new
ignoruje dodatkowe argumenty lub uważa brak argumentów za zero.BTW,
proc
w Ruby 1.8 tworzy lambda, podczas gdy w Ruby 1.9+ zachowuje się jakProc.new
, co jest naprawdę mylące.źródło
Aby rozwinąć odpowiedź na temat Accordion Guy:
Zauważ, że
Proc.new
tworzy proces, przechodząc blok. Uważam, żelambda {...}
jest to parsowane raczej jako dosłowność, niż wywołanie metody, które przechodzi przez blok.return
ing od wewnątrz bloku dołączonego do wywołania metody powróci z metody, a nie z bloku, iProc.new
przypadek jest tego przykładem.(To jest 1.8. Nie wiem, jak to się tłumaczy na 1.9.)
źródło
Trochę się spóźniam, ale jest jedna wspaniała, ale mało znana rzecz, o której w
Proc.new
ogóle nie wspomniano w komentarzach. Zgodnie z dokumentacją :Powiedziawszy to,
Proc.new
pozwólmy łańcuchowi metod uzyskiwania:źródło
&block
argumentu nadef
, ale bez konieczności robienia tego na liście def arg.Warto podkreślić, że
return
w proc powraca z metody obejmującej leksykalnie, tj. Z metody, w której proc został utworzony , a nie z metody, która wywołała proc. Jest to konsekwencja właściwości zamknięcia procs. Poniższy kod nic nie wyświetla:Mimo że proces wykonuje się
foobar
, został utworzonyfoo
i dlategoreturn
kończy sięfoo
, nie tylkofoobar
. Jak napisał Charles Caldwell powyżej, ma w sobie GOTO. Moim zdaniemreturn
jest dobrze w bloku, który jest wykonywany w kontekście leksykalnym, ale jest znacznie mniej intuicyjny, gdy jest używany w proc, który jest wykonywany w innym kontekście.źródło
Różnica w zachowaniu z
return
IMHO jest najważniejszą różnicą między 2. Ja też wolę lambda, ponieważ jest mniej pisania niż Proc. Nowe :-)źródło
proc {}
. Nie jestem pewien, kiedy to zadziałało, ale jest (nieco) łatwiejsze niż wpisywanie Proc. New.