تثمين كسول

لمحة تاريخيّة

قُدِّمَ التثمين الكسول من أجل حسابات اللامدا من قِبَل كريستوفر وادز-ورث وَباستقلالٍ من أجل لُغات البرمجة من قِبَل بيتر هيندرسن وَجيمس حيرم موريس وَدانيال بول فريدمان وَديفيد س. وايز.

التطبيقات

يُستخدم التثمين المؤجَّل(Delayed evaluation) تحديدًا في لُغات البرمجة الوظيفيَّة. فعند استخدام التثمين المؤجَّل، لا يتم تثمين التعبير عند إسناده إلى مُتَغَيِّر بل يُثَمَّن عند الحاجة لإنتاج قيمة التعبير. هذه إفادة كمثال،  x:=expression; (إسناد نتيجة التعبير إلى مُتَغَيِّر). بوضوحٍ، تَمَّ استدعاء التعبير بُغية تثمينه وَوُضِعَت النتيجة في x، لكن يتم تعليق ما بداخل x لِحين احتياج قيمته وذلك باستخدام فهرس يقود إلى x في تعبيرٍ ما لاحِقٍ وقد يكون هذا التعبير أيضًا مؤجّلًا، وأخيرًا تُشَذَّب شجرة الاعتماديَّات النامية هذه لإنتاج بعض الرموز الّتي يُفَضَّل رؤيتها.
من فوائد التثمين المؤجَّل قدرته على إنشاء قوائم لانهائيَّة قابلة للحِساب بدون تداخل الحلقات اللَّانهائيَّة أو الحجم في الحِساب. كمثال، يُمكن إنشاء دالَّة تُنشِئ قائمة لانهائيَّة (تُدعى غالبًا «جريان») من أعداد فيبوناتشي. إنَّ حِساب عدد فيبوناتشيّ ما -وليكن «ن»- سيكون بكلّ بساطة استخلاصًا لهذا العنصر -الّذي هو العدد «ن»- من القائمة اللَّانهائيَّة، مُجبرًا بذلك على تثمين أوَّل ن عنصر من القائمة.
كمثال، يُمكن في لغة البرمجة هاسكل كتابة قائمة بكلّ أعداد فيبوناتشي كالتالي:
 fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
في نحويَّة هاسكل، «:» تُضيف عنصرًا إلى بداية القائمة وَ«tail» تعود بالقائمة بدون عنصرها الأوَّل وَ«zipWith» تَستَخدِم دالَّة مُعَيَّنة (في حالتنا هذه هي الجمع «+») لجمع العناصر المُتوافقة لِكلا القائمتين بهدف إنتاج قائمة ثالثة.
يجب أن يكون المبرمج حذرًا وَدقيقًا؛ لأنّه يتم فقط تثمين القيم اللّازمة لإنتاج نتيجة معيّنة. ومع ذلك، قد تؤدّي بعض الحسابات إلى محاولة البرنامج لتثمين عدد لا حصر له من العناصر؛ على سبيل المثال، طلب طول القائمة أو محاولة جمع عناصر القائمة بعملية الطيّ  يؤدي بالبرنامج إمَّا إلى فشل الإنهاء أو نفاد الذاكرة.

بُنى التحكّم

في كلّ اللّغات «اللّهوفة» تقريبًا، يتمّ تثمين الإفادة «if» بأسلوب كسولٍ.
 if a then b else c
سيتمّ تثمين (a)، وَإذا وَفقط إذا ثُمِّنَت (a) لتكون صح «true» سيتمّ تثمين (b) وَإلّا سيتمّ تثمين (c). وبذلك لن يتمّ تثمين واحد من (b) وَ (c). بالمُقابِل، سيكون السلوك المُتَوَقَّع هو هذا:
 define f(x, y) = 2 * x
 set k = f(d, e)
سيتمّ تثمين (e) عند حِساب قيمة f(d, e) وعلى الرُّغم من ذلك فقيمة (e) غير مُستخدمة في الدالَّة f. وبالتالي تعتمد بُنى التحكّم المُعرَّفة من قِبَل المُستَخدِم على النحويَّة المضبوطة، كمثال:
define g(a, b, c) = if a then b else c
l = g(h, i, j)
سيتمّ تثمين كُلٍّ من (i) وَ (j) في اللُّغة اللَّهوفة. بينما في اللُّغة الكسولة،
l' = if h then i else j
سيتمّ تثمين إمَّا (i) أو (j)، ولن يتمّ تثمين كليهما معًا أبدًا.
يسمَح التثمين الكسول بتعريف بُنى التحكّم طبيعيًّا، وَلكن لا يسمح لها أن تكون مُدمَجة أو كتقنيّات وقت-التجميع. فإذا كان لِـ (i) أو (j) آثارًا جانبيّةً أو أثارت أخطاءً في وقت التشغيل، عنها ستكون الاختلافات غير الملحوظة بين (l) وَ (l') مُعَقَّدَةً. وَمن المُمكن عادةً إنشاء بُنى تحكّمٍ كسولة يُعرّفها المُستخدِم كَدوالٍّ في اللُّغات اللَّهوفة، لذلك قد تُزاح تلك الدَّوال من نحويَّة اللُّغة عند التثمين اللَّهوف: غالبًا ما يُحتَاج ادّثار جسد الشِفرة (مثل: (i) وَ (j)) ليصبح قيمة الدالَّة، ولذلك يتمّ فقط تنفيذها عند الاستدعاء.
يُدعى التثمين قصير الدوران (وَيُسمّى أيضًا: تثمين دُونيّ أو تثمين مَكارثي) لِبُنى التحكّم البوليانيّة(المنطقيّة) تثمينًا «كسولًا».

العمل مع بُنى بيانات أزليّة

توفّر العديد من اللُّغات مفهوم «بُنى-البيانات الأزليّة (لا محدودة)» (بالإنجليزية: infinite data-structures). ويسمح ذلك بتعريف البيانات كمجالات أزليّة أو كتعاوديّة غير منتهية (unending recursion)، لكن يتمّ حساب القيم الفعليّة عندما نحتاجها فحسب. تفقّد هذا المِثال المتواضع؛ وهو برنامج في لغة هاسكل:
numberFromInfiniteList :: Int -> IntnumberFromInfiniteList n =  infinity !! n - 1
    where infinity = [1..]main = print $ numberFromInfiniteList 4
في الدالَّة numberFromInfiniteList، إنّ قيمة infinity هي مجال لا محدود، ولحين احتياجه يتمّ تثمين المجال وصولًا إلى المؤشِّر (index) المطلوب.