مولدات متعددة
التكرار المتداخل
مولدات متعددة درس مجاني في 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 دروس في المجموع.
تحويل التكرار المتداخل إلى تكرار مسطّح
يمكن أن يحتوي تعبير for-comprehension على عدة مولّدات. كل مولّد هو سطر <- يكرّر عناصر مجموعة. وتنتج هذه المولّدات معًا كل التركيبات الممكنة، في صورة تشبه الحلقة المتداخلة ولكن بصياغة مسطّحة.
object Main {
def main(args: Array[String]): Unit = {
val pairs = for {
x <- List(1, 2)
y <- List('a', 'b')
} yield (x, y)
println(pairs)
}
}إنه حاصل ضرب ديكارتي
ينتج مولّدان بحجمين m وn عددًا قدره m × n من النتائج، أي حاصل الضرب الديكارتي الكامل. المولّد الأول هو الحلقة الخارجية، والثاني هو الحلقة الداخلية.
object Main {
def main(args: Array[String]): Unit = {
val grid = for {
row <- 1 to 3
col <- 1 to 3
} yield s"r$row-c$col"
println(grid.mkString(", "))
}
}يمكن للمولّد الداخلي أن يعتمد على الخارجي
يمكن لمولّد لاحق استخدام القيم المرتبطة بواسطة مولّدات سابقة. وهذا غير ممكن مع حاصل ضرب ديكارتي عادي، وهو أحد أسباب مرونة تعبيرات for-comprehension الكبيرة.
object Main {
def main(args: Array[String]): Unit = {
val upperTriangle = for {
i <- 1 to 4
j <- i to 4
} yield (i, j)
println(upperTriangle)
}
}ثلاثة مولّدات أو أكثر
يمكنكم تكديس العدد الذي تريدونه من المولّدات. ومع ثلاثة مولّدات تحصلون على حلقة متداخلة من ثلاثة مستويات، تنتج tuples أو قيمًا محسوبة.
object Main {
def main(args: Array[String]): Unit = {
val triples = for {
a <- 1 to 2
b <- 1 to 2
c <- 1 to 2
} yield (a, b, c)
println(triples.size + " combinations")
triples.foreach(println)
}
}مثال فيثاغورس الشهير
من الاستخدامات الشهيرة: إيجاد ثلاثيات فيثاغورس. نكرّر ثلاثة أعداد ونحتفظ فقط بتلك التي تحقق a*a + b*b == c*c، مع دمج عدة مولّدات مع حارس.
object Main {
def main(args: Array[String]): Unit = {
val triples = for {
a <- 1 to 20
b <- a to 20
c <- b to 20
if a * a + b * b == c * c
} yield (a, b, c)
triples.foreach(println)
}
}كيفية إزالة الصياغة السكرية
تتحول المولّدات المتعددة إلى استدعاءات flatMap متداخلة مع استدعاء map نهائي:
xs.flatMap(x => ys.flatMap(y => zs.map(z => ...)))
يضيف كل مولّد إضافي مستوى متداخلًا آخر.
object Main {
def main(args: Array[String]): Unit = {
val sugar = for {
x <- List(1, 2)
y <- List(3, 4)
} yield x * y
val desugared = List(1, 2).flatMap(x => List(3, 4).map(y => x * y))
println(sugar == desugared)
}
}مزج المولّدات والحراس
يمكنكم التناوب بحرية بين المولّدات والحراس. فالحارس الموضوع بين مولّدين يستبعد القيم الخارجية قبل بدء الحلقة الداخلية أصلًا.
object Main {
def main(args: Array[String]): Unit = {
val result = for {
x <- 1 to 5
if x % 2 == 1
y <- 1 to 5
if y > x
} yield (x, y)
println(result)
}
}تسطيح المجموعات المتداخلة
عندما تكون لديكم مجموعة من المجموعات، يسطّحها مولّدان بصورة طبيعية: يكرّر الأول عناصر المجموعة الخارجية، ويكرّر الثاني عناصر كل مجموعة داخلية.
object Main {
def main(args: Array[String]): Unit = {
val matrix = List(List(1, 2), List(3, 4), List(5, 6))
val flat = for {
row <- matrix
value <- row
} yield value
println(flat)
}
}بناء شبكة نصية
تُعد المولّدات المتعددة مناسبة جدًا لإنشاء مخرجات منظّمة، مثل الإحداثيات أو جدول الضرب.
object Main {
def main(args: Array[String]): Unit = {
val table = for {
i <- 1 to 3
j <- 1 to 3
} yield s"$i*$j=${i * j}"
println(table.grouped(3).map(_.mkString(" ")).mkString("\n"))
}
}دمج مجموعات ذات أحجام مختلفة
ليس من الضروري أن تكون المولّدات بالطول نفسه. إذ تُنتج كل تركيبة من عنصر واحد من كل مولّد.
object Main {
def main(args: Array[String]): Unit = {
val sizes = List("S", "M", "L")
val colors = List("red", "blue")
val products = for {
s <- sizes
c <- colors
} yield s"$c-$s"
println(products)
}
}ترتيب المولّدات يؤثر في تسلسل المخرجات
يُبقي تبديل ترتيب المولّدات على مجموعة التركيبات نفسها، لكنه يغيّر ترتيبها، لأن المولّد الأخير يتغير بأسرع معدل.
object Main {
def main(args: Array[String]): Unit = {
val ab = for { x <- List(1, 2); y <- List(9, 8) } yield (x, y)
val ba = for { y <- List(9, 8); x <- List(1, 2) } yield (x, y)
println(ab)
println(ba)
}
}تحقق سريع
كم عدد الـ tuples التي ينتجها for { x <- List(1,2,3); y <- List('a','b') } yield (x, y)؟
مراجعة
لقد تعلمتم المولّدات المتعددة:
- يضيف كل سطر
<-مستوى من التكرار المتداخل - تنتج المولّدات معًا حاصل الضرب الديكارتي
- يمكن للمولّدات الداخلية الاعتماد على القيم المرتبطة الخارجية
- تتحول إلى
flatMapمتداخلة معmapنهائي - يغيّر ترتيب المولّدات ترتيب النتائج
الأسئلة الشائعة
هل درس «مولدات متعددة» مجاني؟
نعم — نص درس «مولدات متعددة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.