0Pricing
Scala for Backend Engineering & Functional Programming · درس

التدفقات اللانهائية

نمذج البيانات غير المنتهية بأمان

التدفقات اللانهائية درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Scala for Backend Engineering & Functional Programming، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.

اللانهاية بأمان

يمكن لـ LazyList وصف تسلسل لا نهائي، لأن ذيلها لا يُحسب حتى يُطلب. ولا تُنشئ فعليًا إلا المقدمة التي تستهلكها.

يتيح لك ذلك نمذجة الأعداد الطبيعية أو الأعداد الأولية أو قراءات المستشعرات من دون تحديد حد مسبق.

جميع الأعداد الطبيعية

‏LazyList.from(1) هو التسلسل اللانهائي 1، 2، 3، ... ويمكنك أخذ أي مقدمة منتهية منه.

أما فرض تقييمه بالكامل فلن ينتهي أبدًا، لذا قطّعه دائمًا باستخدام take أو أوقفه باستخدام مسند.

object Demo extends App {
  val nats = LazyList.from(1)
  println(nats.take(5).toList)
}

‏from مع خطوة

يعدّ LazyList.from(start, step) بفواصل محددة. استخدمه للأعداد الزوجية أو النبضات أو أي متتالية حسابية.

التسلسل لا نهائي، لكن كل استدعاء لـ take يفرض تقييم ما طلبته فقط.

object Demo extends App {
  val evens = LazyList.from(0, 2)
  println(evens.take(5).toList)
}

التدفقات التي تشير إلى نفسها

هناك حيلة شهيرة تتمثل في تعريف LazyList بدلالة نفسها. يمكن كتابة متتالية فيبوناتشي عبر دمج التدفق مع ذيله باستخدام zip.

ينجح ذلك فقط لأن الذيل يبقى من دون تقييم حتى تُسحب كل خلية.

lazy val fibs: LazyList[Int] =
  0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }
// fibs(0)=0, fibs(1)=1, fibs(2)=1 ...

تشغيل فيبوناتشي

لنفرض تقييم مقدمة من تدفق فيبوناتشي الذي يشير إلى نفسه.

يُحسب كل عنصر جديد انطلاقًا من عناصر سابقة حُفظت مؤقتًا، لذلك يبقى كل شيء فعالًا أثناء سحب المزيد من القيم.

object Demo extends App {
  lazy val fibs: LazyList[Int] =
    0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }
  println(fibs.take(10).toList)
}

‏iterate للتسلسلات

ينشئ LazyList.iterate تسلسلًا لا نهائيًا باستخدام دالة خطوة. وتناسبه القوى والنمو الهندسي وآلات الحالات بصورة طبيعية.

كل عنصر هنا يساوي ثلاثة أضعاف العنصر السابق، إلى ما لا نهاية.

object Demo extends App {
  val triples = LazyList.iterate(1)(_ * 3)
  println(triples.take(6).toList)
}

غربال الأعداد الأولية

تتألق التدفقات اللانهائية في غربال إراتوستينس. خذ عددًا أوليًا من الرأس، وصفِّ مضاعفاته من بقية التسلسل، ثم كرّر العملية عوديًا.

المرشح كسول، لذلك تُنتج الأعداد الأولية واحدًا تلو الآخر أثناء استهلاكها.

def sieve(s: LazyList[Int]): LazyList[Int] =
  s.head #:: sieve(s.tail.filter(_ % s.head != 0))

val primes = sieve(LazyList.from(2))

تشغيل الغربال

اسحب الآن أول عشرة أعداد أولية من ذلك الغربال اللانهائي.

لا يُفرض تقييم إلا على القدر الكافي من تدفق الأعداد الأساسي لإنتاج عشرة أعداد أولية، مما يوضّح الحساب القائم على الطلب.

object Demo extends App {
  def sieve(s: LazyList[Int]): LazyList[Int] =
    s.head #:: sieve(s.tail.filter(_ % s.head != 0))
  val primes = sieve(LazyList.from(2))
  println(primes.take(10).toList)
}

لا تفرض تقييم كل شيء أبدًا

ستتوقف الأساليب التي تحتاج إلى التسلسل كاملًا، مثل length أو toList على تدفق غير محدود أو foreach من دون شرط توقف، عند استخدامها مع LazyList لا نهائية.

قيّدها دائمًا أولًا باستخدام take أو takeWhile أو find.

// DON'T: LazyList.from(1).toList    // hangs forever
val ok = LazyList.from(1).take(3).toList

فخ الاحتفاظ بالرأس

إذا احتفظ متغير val برأس LazyList لا نهائية تخزّن النتائج مؤقتًا، ثم استهلكت جزءًا بعيدًا منها، فستظل كل خلية فُرض تقييمها حية، مما يؤدي إلى تسرّب الذاكرة.

في عمليات الاجتياز الطويلة، استهلك البيانات عبر دالة من دون ربط الرأس، أو استخدم Iterator.

لماذا تهم التدفقات اللانهائية

تتيح التدفقات اللانهائية فصل التوليد عن الاستهلاك. يصف المنتج قاعدة لا نهائية، بينما يحدد المستهلك مقدار ما سيُنشأ فعليًا.

هذا نمط وظيفي قوي للمسارات وخ simulations ومصادر البيانات الكسولة.

اختبار سريع

اختبر فهمك لـ LazyLists اللانهائية.

مراجعة

تعمل LazyLists اللانهائية لأن الذيل يُنشأ عند الطلب: إذ تُولّد from وiterate وfibs ذاتية الإشارة ومصفاة الأعداد الأولية عناصر بلا نهاية، لكنها لا تُنشئ فعليًا إلا ما تستهلكه.

تجنّب العمليات التي تتعامل مع التسلسل كاملًا، وتجنّب الاحتفاظ بالرأس. بعد ذلك، سنتقن أخذ العناصر وتصفيتها بشكل كسول.

الأسئلة الشائعة

هل درس «التدفقات اللانهائية» مجاني؟

نعم — نص درس «التدفقات اللانهائية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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 منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «التدفقات اللانهائية»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Scala for Backend Engineering & Functional Programming هذا؟

نعم. كل درس في Scala for Backend Engineering & Functional Programming يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. شرح الكسلية
  2. إنشاء LazyList
  3. التدفقات اللانهائية
  4. الأخذ والتصفية بكسل
← العودة إلى Scala for Backend Engineering & Functional Programming