Ruby 1.8'de bir yandan proc / lambda ve Proc.new
diğer yandan ince farklılıklar vardır .
- Bu farklar neler?
- Hangisini seçeceğine nasıl karar vereceğine dair rehberlik verebilir misin?
- Ruby 1.9'da proc ve lambda farklıdır. Anlaşma ne?
Ruby 1.8'de bir yandan proc / lambda ve Proc.new
diğer yandan ince farklılıklar vardır .
Yanıtlar:
İle oluşturulan procs arasındaki bir diğer önemli ancak ince bir fark lambda
ile oluşturulan ve procs Proc.new
Halledemeyecekleri nasıl return
deyimi:
lambda
işlemde, return
ifade yalnızca işlemin kendisinden dönerProc.new
proc'ta, return
ifade biraz daha şaşırtıcıdır: sadece proc'tan değil, aynı zamanda proc'u kapsayan yöntemden de kontrol döndürür !İşte - oluşturulan lambda
proclar iş başında return
. Muhtemelen beklediğiniz şekilde davranır:
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"
Şimdi, burada Proc.new
yaratılmış bir proc return
aynı şeyi yapıyor. Ruby'nin çok şaşkın En Az Sürpriz İlkesini ihlal ettiği durumlardan birini görmek üzeresiniz:
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"
Bu şaşırtıcı davranış (yanı sıra daha az yazarak) sayesinde kullanıyorum lehine eğilimindedir lambda
üzerinde Proc.new
procs yaparken.
proc
yöntem de var . Sadece bir stenografi Proc.new
mi?
proc
eşdeğerProc.new
proc
davranıyor . Bu, yakut dokümanın yanlış olduğu anlamına gelir. lambda
Proc.new
proc
gibi davranır lambda
, ancak Proc.new
1.9 gibi davranır . Peter Wagenet'in cevabına bakınız.
lambda
anonim bir yöntemdir. Bir yöntem olduğundan, bir değer döndürür ve onu çağıran yöntem, onu görmezden gelmek ve farklı bir değer döndürmek de dahil olmak üzere, ne isterse yapabilir. A Proc
, bir kod pasajına yapıştırma gibidir. Bir yöntem gibi davranmaz. Yani içinde bir dönüş gerçekleştiğinde Proc
, bu, onu çağıran yöntemin kodunun sadece bir parçasıdır.
Daha fazla açıklama yapmak için:
Joey, geri dönüş davranışının Proc.new
şaşırtıcı olduğunu söylüyor . Ancak Proc.new'in bir blok gibi davrandığını düşündüğünüzde, bu tam olarak blokların nasıl davrandığı gibi şaşırtıcı değildir. Diğer taraftan lambalar daha çok benzer yöntemlere sahiptir.
Bu aslında Procs'un arity (argüman sayısı) konusunda neden esnek olduğunu, ancak lambdaların olmadığını açıklar. Bloklar, tüm argümanlarının verilmesini gerektirmez, ancak yöntemler gerektirir (bir varsayılan belirtilmediği sürece). Lambda argümanı varsayılanı Ruby 1.8'de bir seçenek olmasa da, şimdi Ruby 1.9'da alternatif lambda sözdizimi ile desteklenmektedir (webmat tarafından belirtildiği gibi):
concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1) # => "12"
Ve Michiel de Mare (OP) Procs ve lambda'nın Ruby 1.9'da aynı şekilde davranması konusunda yanlıştır. Yukarıda belirtildiği gibi 1.8'den hala davranışlarını koruduklarını doğruladım.
break
Procs veya lambdalarda ifadelerin pek bir anlamı yoktur. Procs'ta, mola sizi zaten tamamlanmış olan Proc.new'ten döndürür. Ve esasen bir yöntem olduğu için lambdadan kopmak mantıklı değil ve asla bir yöntemin en üst seviyesinden asla kopmayacaksınız.
next
, redo
ve raise
Procs ve lambdalarda da aynı şekilde davranır. Oysa retry
ikisinde de izin verilmez ve istisna oluşturur.
Ve son olarak, proc
yöntem asla tutarsız olduğu ve beklenmedik davranışları olduğu için kullanılmamalıdır. Ruby 1.8'de aslında bir lambda döndürür! Ruby 1.9'da bu düzeltildi ve bir Proc döndürüyor. Bir Proc oluşturmak istiyorsanız, ile devam edin Proc.new
.
Daha fazla bilgi için, bu bilgilerin çoğunda kaynağım olan O'Reilly'nin Ruby Programlama Dili'ni şiddetle tavsiye ederim .
break
yordamlara yükseltme gelen LocalJumpError
, oysa break
lambda'lar boğulan bir mesafede gibi return
( yani , return nil
).
Bulduğum bu sayfayı arasındaki gösterileri ne fark Proc.new
ve lambda
vardır. Sayfaya göre, tek fark bir lambda'nın kabul ettiği argüman sayısı konusunda katı olması, Proc.new
eksik argümanların dönüştürülmesidir nil
. İşte farkı gösteren örnek bir IRB oturumu:
irb (ana): 001: 0> l = lambda {| x, y | x + y} => # <Proc: 0x00007fc605ec0748 @ (irb): 1> irb (ana): 002: 0> p = Proc.new {| x, y | x + y} => # <Proc: 0x00007fc605ea8698 @ (irb): 2> irb (main): 003: 0> l.call "merhaba", "dünya" => "helloworld" irb (main): 004: 0> p.call "merhaba", "dünya" => "helloworld" irb (main): 005: 0> l.call "merhaba" ArgumentError: yanlış sayıda argüman (2 için 1) itibaren (irb): 1 from (irb): 5: `` çağrıda '' itibaren (irb): 5 from: 0 irb (main): 006: 0> p.call "merhaba" TypeError: nil'i String'e dönüştüremiyor from (irb): 2: `` + '' da itibaren (irb): 2 from (irb): 6: `` çağrıda '' itibaren (irb): 6 from: 0
Hataya dayanıklı davranışı özellikle istemediğiniz sürece, sayfa lambda kullanmanızı da önerir. Bu düşünceye katılıyorum. Bir lambda kullanmak biraz daha özlü görünüyor ve bu kadar önemsiz bir farkla, ortalama durumda daha iyi bir seçim gibi görünüyor.
Ruby 1.9'a gelince, özür dilerim, henüz 1.9'a bakmadım, ama bunu çok fazla değiştireceklerini düşünmüyorum (bunun için sözümü almayın, bazı değişiklikler duymuşsunuz gibi görünüyor, bu yüzden Muhtemelen orada yanılıyorum).
Proc daha eskidir, ancak dönüş semantiği bana karşı oldukça mantıklı değildir (en azından dili öğrenirken):
Lambda işlevsel olarak daha güvenli ve akıl yürütmesi daha kolay - her zaman proc yerine kullanıyorum.
İnce farklar hakkında fazla bir şey söyleyemem. Ancak, Ruby 1.9'un artık lambdalar ve bloklar için isteğe bağlı parametrelere izin verdiğini belirtebilirim.
İşte 1.9'ın altındaki stabby lambdas için yeni sözdizimi:
stabby = ->(msg='inside the stabby lambda') { puts msg }
Ruby 1.8'in sözdizimi yoktu. Blokları / lambdaları bildirmenin geleneksel yolu da isteğe bağlı argümanları desteklemedi:
# 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 }
Ancak Ruby 1.9, eski sözdiziminde bile isteğe bağlı bağımsız değişkenleri destekler:
l = lambda { |msg = 'inside the regular lambda'| puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez
Leopard veya Linux için Ruby1.9 oluşturmak istiyorsanız, bu makaleye göz atın (utanmaz öz tanıtım).
Kısa cevap: Önemli olan nedir return
: lambda kendi dışına döner ve proc kendi dışına döner ve onu çağıran işlev.
Daha az net olan, neden her birini kullanmak istediğinizdir. lambda, işlevsel bir programlama anlamında işlerin yapmasını beklediğimiz şeydir. Temelde mevcut kapsam otomatik olarak bağlanan anonim bir yöntemdir. İkisinden lambda muhtemelen kullanmalısınız.
Öte yandan Proc, dilin kendisini uygulamak için gerçekten yararlıdır. Örneğin, "if" ifadelerini veya "for" döngülerini onlarla birlikte uygulayabilirsiniz. Proc'ta bulunan herhangi bir dönüş, yalnızca "if" ifadesini değil, onu çağıran yöntemden geri döner. Diller bu şekilde çalışır, "if" ifadeleri böyle çalışır, bu yüzden tahminim Ruby bunu kapakların altında kullanıyor ve güçlü görünüyordu.
Buna sadece döngüler, if-else yapıları gibi yeni dil yapıları oluşturuyorsanız gerçekten ihtiyacınız olacak.
Queston'da "proc" un kullanımdan kaldırıldığı ancak 1.8 ve 1.9'da farklı şekilde ele alındığı üçüncü yöntemle ilgili herhangi bir yorum fark etmedim.
Üç benzer çağrı arasındaki farkları görmeyi kolaylaştıran oldukça ayrıntılı bir örnek:
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
proc
bir lambda döndürdü; şimdi bir proc'u 1.9'da iade etmek için düzeltildi - ancak bu bir kırılma değişikliği; bu nedenle artık kullanılması tavsiye edilmez
Ruby'deki kapanışlar Ruby'de bloklar, lambda ve proc'un Ruby ile nasıl çalıştığı hakkında iyi bir genel bakış.
lambda diğer dillerde olduğu gibi beklendiği gibi çalışır.
Kablolu Proc.new
şaşırtıcı ve kafa karıştırıcı.
return
Yarattığı proc deyimi Proc.new
sadece kendisinden kontrolünü dönmek olmaz, ama bunu çevreleyen yönteminden de .
def some_method
myproc = Proc.new {return "End."}
myproc.call
# Any code below will not get executed!
# ...
end
Proc.new
Kodun, tıpkı blok gibi, kapatma yöntemine eklendiğini iddia edebilirsiniz . Ama Proc.new
olan blok ise, bir nesneyi yaratır parçası bir nesnenin .
Ve lambda ile Proc.new
, (yanlış) argümanları ele almaları arasında başka bir fark var . lambda bundan şikayetçi olurken, Proc.new
ekstra argümanları göz ardı eder veya argümanların yokluğunu sıfır olarak kabul eder.
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, proc
Ruby 1.8'de bir lambda yaratırken, Ruby 1.9 + 'da böyle davranır Proc.new
ve bu gerçekten kafa karıştırıcıdır.
Akordeon Guy'ın cevabını detaylandırmak için:
Proc.new
Bir blok geçirilerek bir proc çıkışı oluşturan dikkat edin . Ben lambda {...}
bir blok geçen bir yöntem çağrısı yerine, bir tür değişmez olarak ayrıştırıldığını düşünüyorum . return
bir yöntem çağrısına eklenmiş bir bloğun içinden girilmesi, bloktan değil, yöntemden dönerProc.new
durum, buna örnek olarak gösterilebilir.
(Bu 1.8. Bunun 1.9'a nasıl dönüştüğünü bilmiyorum.)
Bu konuda biraz geç kaldım, ama Proc.new
yorumlarda hiç bahsedilmeyen büyük ama az bilinen bir şey var . Tarafından gibi belgeler :
Proc::new
yalnızca ekli bloğa sahip bir yöntem içinde bir blok olmadan çağrılabilir, bu durumda bu blokProc
nesneye dönüştürülür .
Bununla birlikte, Proc.new
verim yöntemlerini zincirleyelim:
def m1
yield 'Finally!' if block_given?
end
def m2
m1 &Proc.new
end
m2 { |e| puts e }
#⇒ Finally!
&block
argümanında bir argüman bildirmekle aynı şeyi yapar def
, ancak def arg listesinde bunu yapmak zorunda kalmadan.
Vurgulayan It değerinde olduğunu return
lexically çevreleyen yönteminden bir proc iadeler, yani içinde proc oluşturulduğu yönteme , değil proc denilen yöntem. Bu, prokilerin kapanma özelliğinin bir sonucudur. Yani aşağıdaki kod hiçbir şey çıktı:
def foo
proc = Proc.new{return}
foobar(proc)
puts 'foo'
end
def foobar(proc)
proc.call
puts 'foobar'
end
foo
Proc yürütülürken , sadece değil, aynı zamanda çıkışlar foobar
da yaratıldı . Charles Caldwell'in yukarıda yazdığı gibi, bir GOTO hissi var. Bence, sözcüksel bağlamında yürütülen bir blokta iyidir, ancak farklı bir bağlamda yürütülen bir proc'ta kullanıldığında çok daha az sezgiseldir.foo
return
foo
foobar
return
return
İle davranış farkı IMHO 2 arasında en önemli fark. Ben de lambda tercih ederim çünkü Proc.new daha az yazarak :-)
proc {}
. Bunun ne zaman yürürlüğe girdiğinden emin değilim, ancak Proc.new yazmak zorunda kalmaktan (biraz) daha kolay.