Scala for Backend Engineering & Functional Programming · درس

Closures

التقاط الحالة

الدرس 4 من 413 خطوة

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

ما هو الإغلاق؟

الإغلاق هو دالة تلتقط المتغيرات من النطاق المحيط الذي عُرّفت فيه. فهي «تغلق على» تلك المتغيرات، وتحتفظ بإمكانية الوصول إليها حتى بعد انتهاء ذلك النطاق.

object Main {
  def main(args: Array[String]): Unit = {
    val factor = 3
    val multiply = (x: Int) => x * factor
    println(multiply(5))
  }
}

التقاط متغير حر

في x => x * factor، يكون x معلمة، بينما يكون factor متغيرًا حرًا مأخوذًا من النطاق المحتوي. وتحتفظ الدالة بمرجع إليه.

object Main {
  def main(args: Array[String]): Unit = {
    val greeting = "Hello"
    val greet = (name: String) => s"$greeting, $name!"
    println(greet("Ann"))
    println(greet("Bob"))
  }
}

تتجاوز الإغلاقات نطاقها

عندما تعيد دالةٌ إغلاقًا، تظل المتغيرات الملتقطة حيّة ما دام الإغلاق موجودًا، حتى بعد انتهاء الدالة التي عرّفته.

object Main {
  def makeAdder(n: Int): Int => Int = x => x + n
  def main(args: Array[String]): Unit = {
    val add10 = makeAdder(10)
    val add100 = makeAdder(100)
    println(add10(5))
    println(add100(5))
  }
}

لكل إغلاق التقاطه الخاص

يؤدي استدعاء مصنع الدوال مرتين إلى إنشاء إغلاقين مستقلين، يتذكر كل منهما القيمة التي التقطها.

object Main {
  def prefixer(p: String): String => String = s => p + s
  def main(args: Array[String]): Unit = {
    val withDr = prefixer("Dr. ")
    val withMr = prefixer("Mr. ")
    println(withDr("Smith"))
    println(withMr("Jones"))
  }
}

التقاط الحالة القابلة للتغيير

يمكن للإغلاق التقاط var وقراءته وتحديثه. ويسمح ذلك للإغلاق بالحفاظ على حالته بين الاستدعاءات، مثل العداد.

object Main {
  def main(args: Array[String]): Unit = {
    var count = 0
    val increment = () => { count += 1; count }
    println(increment())
    println(increment())
    println(increment())
  }
}

مصنع عدادات

اجمعوا الفكرتين: تمنحكم الدالة التي تعيد إغلاقًا يلتقط var خاصًا به عدادًا مستقلًا ومحاطًا بالتغليف.

object Main {
  def counter(): () => Int = {
    var n = 0
    () => { n += 1; n }
  }
  def main(args: Array[String]): Unit = {
    val c1 = counter()
    val c2 = counter()
    println(c1())
    println(c1())
    println(c2())
  }
}

تلتقط الإغلاقات مراجع لا نسخًا

يلتقط الإغلاق المتغير، وليس لقطةً منه. فإذا تغيّر var الملتقط بعد إنشاء الإغلاق، رأى الإغلاق القيمة الجديدة.

object Main {
  def main(args: Array[String]): Unit = {
    var msg = "first"
    val show = () => println(msg)
    show()
    msg = "second"
    show()
  }
}

فضّلوا التقاط val

يؤدي التقاط val غير القابل للتغيير إلى تجنّب المفاجآت: فلا تتغير القيمة الملتقطة، ولذلك يتصرف الإغلاق بطريقة يمكن التنبؤ بها. فضّلوا التقاط val في الشيفرة الوظيفية.

object Main {
  def main(args: Array[String]): Unit = {
    val taxRate = 0.2
    val withTax = (price: Double) => price * (1 + taxRate)
    println(withTax(100))
  }
}

الإغلاقات مع أساليب المجموعات

تكون lambdas الممرّرة إلى map وfilter وغيرهما إغلاقات متى أشارت إلى متغيرات خارجية. وهذا شائع جدًا في Scala اليومية.

object Main {
  def main(args: Array[String]): Unit = {
    val threshold = 3
    val nums = List(1, 2, 3, 4, 5)
    val big = nums.filter(_ > threshold)
    println(big)
  }
}

مشكلة التقاط في الحلقات

في Scala، يكون متغير حلقة for ارتباطًا جديدًا في كل تكرار، ولذلك تلتقط الإغلاقات قيمًا متميزة. وهذا يتجنب خطأً شائعًا يظهر في بعض اللغات الأخرى.

object Main {
  def main(args: Array[String]): Unit = {
    val fns = for (i <- 1 to 3) yield () => i
    println(fns.map(f => f()))
  }
}

تغلف الإغلاقات السلوك والبيانات

يجمع الإغلاق بين السلوك (جسم الدالة) والبيانات (المتغيرات الملتقطة). وهو بديل خفيف لكائن صغير ذي أسلوب واحد.

object Main {
  def discounter(rate: Double): Double => Double = price => price * (1 - rate)
  def main(args: Array[String]): Unit = {
    val sale = discounter(0.25)
    println(sale(80))
    println(sale(200))
  }
}

تحقّق سريع

ماذا يلتقط الإغلاق من النطاق المحيط به؟

مراجعة

لقد تعلّمتم الإغلاقات:

  • يلتقط الإغلاق المتغيرات الحرة من النطاق المحتوي
  • تتجاوز المتغيرات الملتقطة عمر الدالة التي عرّفتها
  • لكل نسخة من الإغلاق حالتها الملتقطة الخاصة
  • تلتقط الإغلاقات مراجع، لذا يمكن أن تتغير الالتقاطات القابلة للتغيير
  • تُعد lambdas الممررة إلى map/filter إغلاقات شائعة الاستخدام
البدء مجانًا

تعلم Scala مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
39
الدروس
143

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

هل درس «Closures» مجاني؟

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

ماذا ستتعلم في «Closures»؟

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

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

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

كم من الوقت يستغرق درس «Closures»؟

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

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

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

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

  1. الدوال كقيم
  2. Currying
  3. التركيب
  4. Closures
← العودة إلى Scala for Backend Engineering & Functional Programming