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

إزالة السكر النحوي من for

map وflatMap

إزالة السكر النحوي من for درس مجاني في 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 دروس في المجموع.

ما المقصود بـ for-comprehension؟

إن for-comprehension في Scala صياغة خاصة للعمل مع المجموعات وأنواع الحاويات الأخرى. تبدو كحلقة إجرائية، لكنها في الحقيقة صياغة مختصرة يعيد المترجم كتابتها إلى استدعاءات map وflatMap وfilter وforeach.

يساعدك فهم إزالة الصياغة المختصرة على تحليل أي for-comprehension، حتى تلك التي تعمل على أنواع مخصصة.

object Main {
  def main(args: Array[String]): Unit = {
    val result = for (x <- List(1, 2, 3)) yield x * 10
    println(result)
  }
}

يتحول المولّد الواحد إلى map

عندما تحتوي for-comprehension على مولّد واحد بالضبط وتستخدم yield، يعيد المترجم كتابتها إلى استدعاء map واحد.

  • for (x <- xs) yield f(x)
  • تصبح xs.map(x => f(x))
object Main {
  def main(args: Array[String]): Unit = {
    val a = for (x <- List(1, 2, 3)) yield x + 1
    val b = List(1, 2, 3).map(x => x + 1)
    println(a == b)
  }
}

الكلمة المفتاحية yield

تخبر الكلمة المفتاحية yield لغة Scala أن تجمع النتائج في مجموعة جديدة من النوع نفسه.

من دون yield، تنفذ for-comprehension أثرًا جانبيًا لكل عنصر وتُرجع Unit (وتصبح foreach).

object Main {
  def main(args: Array[String]): Unit = {
    // No yield: side effect only
    for (x <- List("a", "b")) println(x)
    // With yield: builds a new List
    val upper = for (x <- List("a", "b")) yield x.toUpperCase
    println(upper)
  }
}

يتحول مولّدان إلى flatMap + map

مع وجود مولّدين أو أكثر، تتحول المولّدات الخارجية إلى flatMap، بينما يتحول المولّد الأعمق إلى map.

  • for (x <- xs; y <- ys) yield f(x, y)
  • تصبح xs.flatMap(x => ys.map(y => f(x, y)))
object Main {
  def main(args: Array[String]): Unit = {
    val pairs = for {
      x <- List(1, 2)
      y <- List("a", "b")
    } yield (x, y)
    println(pairs)
  }
}

رؤية flatMap المكافئ

نكتب المنطق نفسه هنا بالطريقتين. وإثبات تكافؤهما يوضح أن for-comprehensions لا تضيف سحرًا، بل قابلية قراءة فحسب.

object Main {
  def main(args: Array[String]): Unit = {
    val sugar = for {
      x <- List(1, 2)
      y <- List(10, 20)
    } yield x + y

    val desugared = List(1, 2).flatMap(x => List(10, 20).map(y => x + y))

    println(sugar)
    println(sugar == desugared)
  }
}

لماذا نستخدم flatMap للمولّد الخارجي؟

ينتج كل عنصر خارجي مجموعة كاملة من النتائج الداخلية. وإذا استخدمنا map للمولّد الخارجي، فسنحصل على مجموعة متداخلة مثل List(List(...), List(...)).

يفكك flatMap هذه القوائم المتداخلة إلى نتيجة مسطحة واحدة.

object Main {
  def main(args: Array[String]): Unit = {
    val nested = List(1, 2).map(x => List(10, 20).map(y => x + y))
    val flat   = List(1, 2).flatMap(x => List(10, 20).map(y => x + y))
    println(nested)
    println(flat)
  }
}

‏for بدون yield تصبح foreach

تُعاد كتابة حلقة for التي لا تحتوي على yield إلى foreach. فهي تنفذ جسم الحلقة من أجل آثاره الجانبية وتنتج Unit.

  • for (x <- xs) doSomething(x)
  • تصبح xs.foreach(x => doSomething(x))
object Main {
  def main(args: Array[String]): Unit = {
    val r: Unit = for (x <- List(1, 2, 3)) print(x + " ")
    println()
    println("Return type is Unit")
  }
}

يتبع نوع النتيجة المولّد الأول

يتحدد نوع النتيجة بواسطة مجموعة المولّد الأول. فـ for على List تُرجع List، وعلى Set تُرجع Set، وعلى Option تُرجع Option.

object Main {
  def main(args: Array[String]): Unit = {
    val fromList = for (x <- List(1, 2, 2, 3)) yield x
    val fromSet  = for (x <- Set(1, 2, 2, 3)) yield x
    println(fromList)
    println(fromSet)
  }
}

ربط القيم باستخدام =

يمكنك داخل for-comprehension ربط القيم الوسيطة باستخدام =. وهذا يتجنب إعادة الحساب ويحسن قابلية القراءة.

وتُزال صيغته المختصرة إلى map يحمل القيمة الإضافية داخل tuple.

object Main {
  def main(args: Array[String]): Unit = {
    val result = for {
      x <- List(1, 2, 3)
      doubled = x * 2
    } yield doubled + 1
    println(result)
  }
}

تعمل مع أي نوع يحتوي على map/flatMap

نظرًا لأن إزالة الصياغة المختصرة تعتمد فقط على map وflatMap، فإن for-comprehensions تعمل مع أي نوع يعرّف هذين الأسلوبين: Option وEither وTry وFuture وفئاتك الخاصة.

object Main {
  def main(args: Array[String]): Unit = {
    val combined = for {
      a <- Some(2)
      b <- Some(3)
    } yield a * b
    println(combined)
  }
}

الفائدة الحقيقية هي قابلية القراءة

قارن بين سلسلة متداخلة بعمق من flatMap/map وfor-comprehension واضحة. فهما تُترجمان إلى الشيء نفسه، لكن نسخة for تُقرأ من الأعلى إلى الأسفل كأنها وصفة.

object Main {
  def main(args: Array[String]): Unit = {
    val chained = List(1, 2).flatMap(a => List(3, 4).map(b => a * b))
    val readable = for {
      a <- List(1, 2)
      b <- List(3, 4)
    } yield a * b
    println(chained == readable)
  }
}

تحقق سريع

كيف يزيل المترجم الصياغة المختصرة لـ for-comprehension تحتوي على مولّدين وyield؟

مراجعة

تعلمت كيفية إزالة الصياغة المختصرة لـ for-comprehensions:

  • مولّد واحد + yield → map
  • مولّدات متعددة + yield → flatMap (الخارجي) + map (الداخلي)
  • من دون yield → foreach الذي يُرجع Unit
  • تضيف ارتباطات val = قيمًا وسيطة
  • يتبع نوع النتيجة المولّد الأول

يمكن استخدام أي نوع يحتوي على map وflatMap في for-comprehension.

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

هل درس «إزالة السكر النحوي من for» مجاني؟

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

ماذا ستتعلم في «إزالة السكر النحوي من for»؟

map وflatMap تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Scala for Backend Engineering & Functional Programming؟

لا تُشترط خبرة سابقة. Scala for Backend Engineering & Functional Programming على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «إزالة السكر النحوي من for»؟

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

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

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

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

  1. إزالة السكر النحوي من for
  2. التصفية باستخدام if
  3. مولدات متعددة
  4. الاستخدام مع Option وFuture
← العودة إلى Scala for Backend Engineering & Functional Programming