سؤال
ما هو الخطأ في الكود؟
def call_block(n)
if n==1
return 0
elsif n== 2
return 1
else
yield
return call_block(n-1) + call_block(n-2)
end
end
puts call_block(10) {puts "Take this"}
أحاول استخدام العائد للطباعة خذ هذا بخلاف رقم فيبوناتشي العاشر.
أحصل على الخطأ: في "Call_block": لم يتم تقديم كتلة (localjumperror)
حتى الكود التالي يلقي خطأ:
def call_block(n)
if n==1
yield
return 0
elsif n== 2
yield
return 1
else
yield
return call_block(n-1) + call_block(n-2)
end
end
puts call_block(10) {puts "Take this"}
المحلول
قد ترغب في استخدام هذا الخط آدم فاندنبرغ تلميحات:
return call_block(n-1) { yield } + call_block(n-2) { yield }
نصائح أخرى
أولاً ، دعنا ننظف ذلك قليلاً حتى يكون من الأسهل رؤية ما يحدث:
def call_block(n)
return 0 if n == 1
return 1 if n == 2
yield
call_block(n-1) + call_block(n-2)
end
puts call_block(10) { puts 'Take this' }
الآن دعنا فقط نتتبعها.
نبدأ بالاتصال
call_block(10) { puts 'Take this' }
لذا، n
هو 10
والكتلة هي {puts 'take this'}. حيث n
هو كلاهم 1
ولا 2
, ، وصلنا إلى yield
, الذي ينقل التحكم إلى الكتلة.
الآن نحن ندعو
call_block(n-1)
الذي
call_block(9)
لاحظ أننا لا نسميها مع كتلة. لذلك ، لهذه المكالمة الجديدة ، n
هو 9
وليس هناك كتلة. مرة أخرى ، نتخطى أول خطين ونأتي إلى yield
.
لكن لا يوجد كتلة yield
إلى ، وهذا هو السبب في أن الكود ينفجر هنا.
الحل واضح ودقيق. الجزء الواضح هو: المشكلة هي أننا لا نمرر كتلة ، وبالتالي فإن الحل هو أننا نحتاج إلى تمرير الكتلة. الجزء الدقيق هو: كيف نفعل ذلك؟
الشيء الذي يجعل روبي كتل خفيفة الوزن بشكل نحلي ، هو أنها مجهولة. ولكن إذا لم يكن للكتلة اسم ، فلا يمكننا الرجوع إليه ، وإذا لم نتمكن من الرجوع إليه ، فلا يمكننا تمريره.
الحل لهذا هو استخدام بنية أخرى في روبي ، وهو في الأساس تجريد الوزن الثقيل لفكرة "جزء من الكود" من كتلة: أ Proc
.
def call_block(n, blk)
return 0 if n == 1
return 1 if n == 2
blk.()
call_block(n-1, blk) + call_block(n-2, blk)
end
puts call_block(10, ->{ puts 'Take this' })
كما ترون ، هذا هو أثقل قليلا ، ولكن يمكننا إعطاء Proc
اسم ، وبالتالي تمريره إلى المكالمات العودية.
ومع ذلك ، فإن هذا النمط شائع في الواقع بما يكفي لوجود دعم خاص في روبي لذلك. إذا وضعت &
Sigil أمام اسم المعلمة في قائمة المعلمات ، سوف "Ruby" يحزم "كتلة يتم تمريرها كوسيطة في أ Proc
كائن وربطه بهذا الاسم. وعلى العكس ، إذا وضعت &
سيجيل أمام تعبير الوسيطة في قائمة وسيطة ، سوف "يفصل" ذلك Proc
في كتلة:
def call_block(n, &blk)
return 0 if n == 1
return 1 if n == 2
yield # or `blk.()`, whichever you prefer
call_block(n-1, &blk) + call_block(n-2, &blk)
end
puts call_block(10) { puts 'Take this' }
هذا بسبب الدعوة العودية للطريقة call_block
دون أن يمر في كتلة. طريقة واحدة للقيام بذلك ستكون:
def call_block(n, &blk)
if n == 1
return 0
elsif n == 2
return 1
else
blk.call()
return call_block(n-1, &blk) + call_block(n-2, &blk)
end
end
puts call_block(4) {puts "Take this"}
تحرير: يجب أن أعترف أن الحل أرسلت بواسطة العدالة يبدو أكثر منطقية.