سؤال

في "روبي" 1.8 ، وهناك الفروق الدقيقة بين proc/امدا من جهة ، Proc.new من جهة أخرى.

  • ما هي هذه الاختلافات ؟
  • يمكنك إعطاء مبادئ توجيهية بشأن كيفية تقرر أي واحدة تختار ؟
  • في روبي 1.9, proc و امدا مختلفة.ما هي الصفقة ؟
هل كانت مفيدة؟

المحلول

آخر مهم ولكن فرق دقيق بين procs التي تم إنشاؤها مع lambda و procs التي تم إنشاؤها مع Proc.new هو كيفية التعامل مع return بيان:

  • في lambda-إنشاء مجلد ، return بيان العوائد فقط من بروك نفسها
  • في Proc.new-إنشاء مجلد ، return البيان هو قليلا أكثر إثارة للدهشة:فإنه يعود التحكم ليس فقط من بروك ، ولكن أيضا من طريقة أرفق بروك!

هنا lambda-إنشاء مجلد ما return في العمل.فإنه يتصرف بطريقة ربما نتوقع:

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"

الآن هنا Proc.new-إنشاء مجلد ما return تفعل الشيء نفسه.كنت على وشك أن نرى واحدة من تلك الحالات حيث روبي يكسر كثر مبدأ الأقل مفاجأة:

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"

وبفضل هذا السلوك المستغرب (وكذلك أقل في الكتابة), أنا أميل إلى تفضيل استخدام lambda أكثر Proc.new عند اتخاذ procs.

نصائح أخرى

إلى تقديم مزيد من التوضيح:

جوي يقول أن عودة سلوك Proc.new هو المستغرب.ولكن عندما تفكر في أن Proc.جديدة يتصرف مثل كتلة وهذا ليس مستغربا لأن ذلك هو بالضبط كيف كتل التصرف.lambas من ناحية أخرى تتصرف مثل الأساليب.

هذا الواقع يفسر لماذا Procs تتسم بالمرونة عندما يتعلق الأمر arity (عدد الحجج) في حين lambdas لا.كتل لا تتطلب كل الحجج المقدمة ولكن الطرق (إلا الافتراضي هو المقدمة).مع توفير امدا حجة الافتراضي ليس خيارا في "روبي" 1.8 ، هو الآن معتمد في روبي 1.9 مع بديل امدا جملة (كما أشار webmat):

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

و ميشيل دي مار (OP) غير صحيحة عن Procs و امدا يتصرف نفس مع arity في روبي 1.9.يجب التحقق من أنها لا تزال تحافظ على السلوك من 1.8 كما هو محدد أعلاه.

break البيانات في الواقع لا معنى لها إما Procs أو lambdas.في Procs ، كسر سيعود من Proc.الجديدة التي تم بالفعل الانتهاء.و هذا لا يجعل من أي معنى كسر من امدا لأنها في الأساس وسيلة و لن تكسر من أعلى مستوى الأسلوب.

next, redo, ، raise التصرف نفسه في كل Procs و lambdas.في حين retry لا يسمح في أي وسيتم رفع استثناء.

وأخيرا ، proc طريقة ينبغي أبدا أن تستخدم كما هو غير متناسقة وقد سلوك غير متوقع.في روبي 1.8 في الحقيقة ترجع امدا!في روبي 1.9 وهذا ثابت و ترجع Proc.إذا كنت ترغب في إنشاء مجلد, العصا مع Proc.new.

لمزيد من المعلومات, أنا أوصي أورايلي روبي لغة البرمجة الذي هو مصدر معظم هذه المعلومات.

وجدت هذه الصفحة الذي يظهر ما الفرق بين Proc.new و lambda هي.وفقا الصفحة ، والفرق الوحيد هو أن امدا صارمة حول عدد من الحجج فإنه يقبل ، بينما Proc.new تحويل عداد المفقودين الحجج nil.هنا هو مثال على IRB الدورة توضح الفرق:

irb(main):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: wrong number of arguments (1 for 2)
    from (irb):1
    from (irb):5:in `call'
    from (irb):5
    from :0
irb(main):006:0> p.call "hello"
TypeError: can't convert nil into String
    from (irb):2:in `+'
    from (irb):2
    from (irb):6:in `call'
    from (irb):6
    from :0

الصفحة كما توصي باستخدام امدا إلا إذا كنت تريد على وجه التحديد الخطأ السلوك المتسامح.وأنا أتفق مع هذا الرأي.باستخدام امدا يبدو صبي أكثر إيجازا ، و مع هذا يستهان بها الفرق, يبدو الخيار الأفضل في متوسط الحالة.

أما بالنسبة روبي 1.9, آسف, أنا لم ينظر إلى 1.9 بعد لكن لا أتصور أنها سوف تغير كل هذا بكثير (لا تأخذ في كلمة بلدي لذلك على الرغم من أنه يبدو كنت قد سمعت من بعض التغييرات لذلك أنا ربما هناك خطأ).

بروك هو الأكبر سنا ، ولكن دلالات عودة للغاية متوقع بالنسبة لي (على الأقل عندما كنت أتعلم اللغة) لأن:

  1. إذا كنت تستخدم proc, كنت على الأرجح باستخدام نوع من وظيفية النموذج.
  2. بروك يمكن العودة من أرفق نطاق (انظر الردود السابقة) ، وهو غوتو في الأساس للغاية غير وظيفية في الطبيعة.

امدا وظيفيا أكثر أمانا وأسهل سبب عن - أنا دائما استخدام بدلا من proc.

لا أستطيع أن أقول الكثير عن الفروق الدقيقة.ومع ذلك ، يمكن أن نشير إلى أن روبي 1.9 الآن يسمح معلمات اختيارية بالنسبة lambdas و كتل.

وهنا جملة جديدة من أجل بالغضب lambdas تحت 1.9:

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

روبي 1.8 لم يكن بناء الجملة.ولا بالطريقة التقليدية من إعلان كتل/lambdas دعم اختياري وسائط:

# 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 }

روبي 1.9 ، ومع ذلك ، يدعم وسيطات اختيارية حتى مع البناء القديمة:

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

إذا كنت تريد بناء Ruby1.9 ليوبارد أو لينكس ، تحقق هذه المادة (وقح تعزيز الذات).

الجواب القصير:ما يهم هو ما return لا:امدا ترجع من نفسها ، proc ترجع من نفسها وظيفة أن يطلق عليه.

ما هو أقل وضوحا هو السبب كنت ترغب في استخدام كل منها.امدا هو ما كنا نتوقع أشياء يجب القيام به في البرمجة الوظيفية معنى.بل هو في الأساس أسلوب المجهول مع النطاق الحالي تلقائيا ملزمة.من اثنين ، لامدا هو واحد ربما يجب أن تستخدم.

Proc, من ناحية أخرى, هو حقا مفيدة لتنفيذ اللغة نفسها.على سبيل المثال يمكنك تنفيذ "إذا" أو بيانات أو "ل" الحلقات معهم.أي العودة وجدت في proc سيعود من الأسلوب الذي يطلق عليه ، وليس مجرد "إذا" بيان.هذا هو كيفية عمل اللغات كيف "إذا" التصريحات العمل ، لذا أعتقد روبي يستخدم هذا تحت الأغطية أنها تتعرض فقط لأنه بدا قوية.

كنت حقا بحاجة فقط هذا إذا كنت خلق لغة جديدة يبني مثل الحلقات ، إذا كان آخر يبني ، إلخ.

وسيلة جيدة لمعرفة ذلك هو أن lambdas يتم تنفيذها في نطاقها الخاص (كما لو كانت استدعاء الأسلوب) ، في حين Procs يمكن اعتباره أعدم مضمنة مع استدعاء أسلوب على الأقل هذا هو وسيلة جيدة البت اللي تبونه واحد للاستخدام في كل حالة.

لم ألاحظ أي تعليقات على الأسلوب الثالث في queston, "بروك" الذي هو مستنكر ، ولكن التعامل معها بشكل مختلف في 1.8 و 1.9.

هنا هو إلى حد ما مطول سبيل المثال أن يجعل من السهل أن نرى الاختلافات بين ثلاث دعوات مماثلة:

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.new مفاجئة ومربكة.

على return بيان في proc إنشاؤه من قبل Proc.new لا يعود فقط التحكم فقط من نفسها ، ولكن أيضا من طريقة أرفق ذلك.

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

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

يمكنك القول أن Proc.new إدراج رمز في أرفق الأسلوب ، مثل الكتلة.ولكن Proc.new يخلق كائن ، في حين أن كتلة جزء من كائن.

وهناك فرق آخر بين امدا ، Proc.new, هو التعامل مع (خطأ) الحجج.امدا يشكو ذلك ، في حين Proc.new يتجاهل إضافية الحجج أو ترى غياب الحجج النيل.

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"

راجع للشغل ، proc في روبي 1.8 يخلق امدا ، بينما في روبي 1.9+ يتصرف مثل Proc.new, التي هي مربكة حقا.

وضع على الأكورديون الرجل الاستجابة:

لاحظ أن Proc.new يخلق بروك من قبل تمريرها كتلة.وأعتقد أن lambda {...} تحليل كنوع من حرفية بدلا من استدعاء الأسلوب الذي يمر كتلة. returnجي من داخل كتلة تعلق على استدعاء الأسلوب سوف يعود من طريقة ، وليس الكتلة ، Proc.new القضية هي مثال على هذا في اللعب.

(هذا هو 1.8.أنا لا أعرف كيف أن هذا يترجم إلى 1.9.)

أنا متأخر قليلا على هذا ، ولكن هناك واحدة كبيرة ولكن القليل من يعرف شيئا عن Proc.new لم يرد ذكرها في التعليقات على الإطلاق.كما الوثائق:

Proc::new يمكن أن يسمى من دون كتلة فقط في طريقة متصل مع كتلة, في هذه الحالة أن كتلة يتم تحويلها إلى Proc الكائن.

قال ذلك ، Proc.new يتيح إلى سلسلة الغلة طرق:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!

الفرق في السلوك مع return هو IMHO الفرق الأهم بين 2.أنا أيضا يفضلون امدا لأنه أقل الكتابة من Proc.جديد :-)

إنه من الجدير بالذكر أن return في proc عوائد بشكل معجمي أرفق الطريقة ، أي طريقة حيث تم إنشاء proc, لا الأسلوب الذي يسمى proc.هذا هو نتيجة إغلاق المنشأة من procs.لذا المخرجات التعليمة البرمجية التالية لا شيء:

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

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

foo

على الرغم من أن بروك ينفذ في foobar, تم إنشاؤه في foo وهكذا return مخارج foo, ليس فقط foobar.كما تشارلز كالدويل كتب أعلاه ، فقد غوتو يشعر.في رأيي ، return على ما يرام في كتلة التي يتم تنفيذها في المعجمية السياق ، ولكن أقل بكثير بديهية عند استخدامها في proc التي يتم تنفيذها في سياق مختلف.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top