سؤال

ما هو الخطأ في الكود؟

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

تحرير: يجب أن أعترف أن الحل أرسلت بواسطة العدالة يبدو أكثر منطقية.

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