شرح الكسلية
احسب القيم عند الحاجة فقط
شرح الكسلية درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Scala for Backend Engineering & Functional Programming، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.
ما المقصود بالتقييم الكسول؟
في Scala، تكون معظم القيم strict، أي تُحسب لحظة تعريفها. أما الكسل فيعكس ذلك؛ فلا تُحسب القيمة الكسولة إلا عند الحاجة إليها للمرة الأولى، وليس قبل ذلك.
يهم هذا عندما تكون الحسابات مكلفة، أو قد تفشل، أو ربما لا تُستخدم مطلقًا. فلماذا ندفع تكلفة عمل لا يطلبه أحد؟
Strict val
يقيّم val العادي الطرف الأيمن فورًا، حتى إن لم تقرأه مرة أخرى.
شغّل هذا المثال ولاحظ أن الكلمة "computing" تُطبع قبل "after". يحدث العمل وقت التعريف.
object Demo extends App {
val x = { println("computing"); 42 }
println("after")
println(x)
}lazy val
أضف lazy قبل val لتأجيل التقييم حتى أول وصول إلى القيمة.
تُطبع هنا "after" أولًا، ثم تُطبع "computing" فقط عند قراءة x. كما تُخزّن النتيجة مؤقتًا، لذلك لا يُنفّذ الجسم أكثر من مرة.
object Demo extends App {
lazy val x = { println("computing"); 42 }
println("after")
println(x)
println(x)
}المعاملات by-name
يُمرَّر المعامل المعلن بصيغة => A by name، أي لا يُقيَّم الوسيط حتى يُستخدم داخل الدالة.
هذا هو الأساس الذي تُبنى عليه الكسولة. تستخدم LazyList معاملات by-name، لذلك لا يُحسب الذيل حتى يُطلب.
def maybe(cond: Boolean, value: => Int): Int =
if (cond) value else 0
val r = maybe(false, { println("side"); 99 })
// "side" never prints; value was unusedتعرّف إلى LazyList
LazyList هي التسلسل الكسول في Scala، وقد حلّت محل Stream الأقدم في الإصدار 2.13 وما بعده. يُقيَّم رأسها بشغف، بينما يكون ذيلها كسولًا.
تُنتج العناصر عند الطلب وتُحفظ مؤقتًا بعد حسابها، لذلك لا تعيد حساب الخلية نفسها مرتين.
val ll = LazyList(1, 2, 3)
println(ll)
// prints LazyList(<not computed>) until forcedفرض التقييم
تبقى LazyList من دون تقييم حتى تفرض تقييمها. يؤدي استدعاء toList أو foreach أو طباعة كل عنصر إلى فرض تقييم الخلايا.
يعرض REPL الرمز <not computed> للذيل الكسول. ويكشف فرض التقييم القيم الفعلية.
object Demo extends App {
val ll = LazyList(1, 2, 3)
println(ll.toList)
}المعامل #::
تضيف عنصرًا في بداية LazyList باستخدام #::. وعلى خلاف :: الخاص بالقائمة، يكون المعامل الأيمن لـ #:: من نوع by-name، لذلك لا يُبنى الذيل حتى تدعو الحاجة إليه.
يتيح لك ذلك وصف تسلسلات يُحسب باقيها بطريقة كسولة.
val ll = 1 #:: 2 #:: 3 #:: LazyList.empty
println(ll.head)التخزين المؤقت
بعد حساب خلية في LazyList، تُخزَّن قيمتها. وتصبح قراءتها مرة أخرى مجانية، ولا يُنفَّذ التعبير الذي أنتجها مرتين.
شغّل هذا المثال: تُطبع "calc" عند الوصول الأول إلى كل عنصر فُرض تقييمه فقط، لا عند القراءات المتكررة.
object Demo extends App {
val ll = LazyList(1, 2).map { n =>
println("calc " + n); n * 10
}
println(ll.head)
println(ll.head)
}الذيل الكسول عمليًا
بما أن الذيل كسول، يمكنك تطبيق map على LazyList، ولن تُحسب إلا العناصر التي تصل إليها.
تُطبع هنا "map" مرة واحدة فقط رغم أن القائمة تضم ثلاثة عناصر، لأن الرأس وحده هو الذي فُرض تقييمه.
object Demo extends App {
val r = LazyList(1, 2, 3).map { n =>
println("map " + n); n + 1
}
println(r.head)
}لماذا تفيد الكسولة
تتيح الكسولة ثلاثة أمور: تجنّب العمل غير المستخدم، وبناء تسلسلات لا نهائية، وفصل طريقة وصف الحساب عن مقدار ما يُنفَّذ منه.
تكتب المسار الكامل مرة واحدة، ثم يحدد المستهلك عدد العناصر التي يريد سحبها.
تنبيه
للكسولة تكاليفها. إذ تخزّن LazyList النتائج مؤقتًا، لذلك يؤدي الاحتفاظ بمرجع إلى رأسها إلى إبقاء كل عنصر فُرض تقييمه في الذاكرة. عند التعامل مع بيانات ضخمة أو لا نهائية، تجنّب الاحتفاظ بالرأس.
كما أن المؤثرات الجانبية في التعليمات البرمجية الكسولة تُنفّذ في أوقات غير متوقعة، مما قد يصعّب تصحيح الأخطاء.
اختبار سريع
اختبر فهمك للتقييم الكسول.
مراجعة
ينفّذ التقييم الصارم العمل فورًا، بينما يؤجله التقييم الكسول حتى الحاجة إليه. يمثل lazy val والمعاملات by-name (=> A) اللبنات الأساسية لذلك.
تبني LazyList على هذه الآليات: رأس يُقيّم بشغف، وذيل كسول مخزّن مؤقتًا، ويُفرض تقييمه عبر toList أو الوصول إلى العناصر. بعد ذلك سنبني LazyLists يدويًا.
الأسئلة الشائعة
هل درس «شرح الكسلية» مجاني؟
نعم — نص درس «شرح الكسلية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Scala for Backend Engineering & Functional Programming، انتقل إلى CoddyKit PRO. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.
ماذا ستتعلم في «شرح الكسلية»؟
احسب القيم عند الحاجة فقط تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Scala for Backend Engineering & Functional Programming؟
لا تُشترط خبرة سابقة. Scala for Backend Engineering & Functional Programming على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «شرح الكسلية»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Scala for Backend Engineering & Functional Programming هذا؟
نعم. كل درس في Scala for Backend Engineering & Functional Programming يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.