Functional Programming

  • Java 8 Streams: Summing Items Grouped by ID

    المطلوب هو استخدام Java 8 Streams API لحساب مجموع الكميات لكل عنصر مماثل في قائمة من القوائم. تحتاج إلى تجميع البيانات حسب معرف العنصر وحساب المجموع لكل عنصر عبر القوائم المختلفة. للقيام بذلك، يمكننا استخدام دمج العمليات مع Streams API لتحقيق الغرض المطلوب.

    أولاً، يجب علينا تعريف وظيفة تقوم بدمج الكميات لنفس العنصر. ثم، سنقوم بتطبيق هذه الوظيفة على كل عنصر في كل قائمة، ثم نقوم بتجميع النتائج باستخدام تجميع البيانات حسب معرف العنصر.

    اليك الشفرة التالية التي تقوم بذلك:

    java
    import java.math.BigDecimal; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Streams { static class PurchaseItemCancellation { private Integer id; private BigDecimal quantity; public PurchaseItemCancellation(Integer id, BigDecimal quantity) { this.id = id; this.quantity = quantity; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public BigDecimal getQuantity() { return quantity; } public void setQuantity(BigDecimal quantity) { this.quantity = quantity; } } static class PurchaseCancellation { private Integer id; private List purchaseItemsCancellations; public PurchaseCancellation(Integer id, List purchaseItemsCancellations) { this.id = id; this.purchaseItemsCancellations = purchaseItemsCancellations; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public List getPurchaseItemsCancellations() { return purchaseItemsCancellations; } public void setPurchaseItemsCancellations(List purchaseItemsCancellations) { this.purchaseItemsCancellations = purchaseItemsCancellations; } } public static void main(String... args) { PurchaseItemCancellation item1 = new PurchaseItemCancellation(1, new BigDecimal("10.00")); PurchaseItemCancellation item2 = new PurchaseItemCancellation(2, new BigDecimal("20.00")); PurchaseItemCancellation item3 = new PurchaseItemCancellation(3, new BigDecimal("30.00")); PurchaseItemCancellation item4 = new PurchaseItemCancellation(4, new BigDecimal("40.00")); PurchaseCancellation purchaseCancellation1 = new PurchaseCancellation(1, List.of(item1, item2)); PurchaseCancellation purchaseCancellation2 = new PurchaseCancellation(2, List.of(item3, item4)); PurchaseCancellation purchaseCancellation3 = new PurchaseCancellation(3, List.of(item4, item1)); List cancellations = List.of(purchaseCancellation1, purchaseCancellation2, purchaseCancellation3); final Comparator byID = Comparator.comparing(PurchaseItemCancellation::getId); Map summedQuantities = cancellations.stream() .flatMap(cancellation -> cancellation.getPurchaseItemsCancellations().stream()) .collect(Collectors.groupingBy(PurchaseItemCancellation::getId, Collectors.mapping(PurchaseItemCancellation::getQuantity, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)))); summedQuantities.forEach((id, quantity) -> System.out.println("id: " + id + ", quantity: " + quantity)); } }

    هذه الشفرة تقوم بالتالي:

    1. تستخدم Java 8 Streams API لتقليل التعقيد في عملية الحساب.
    2. تستخدم دوال الجمع والتحويل والتجميع لجمع الكميات لكل عنصر.
    3. تقوم بطباعة النتائج للتحقق من صحتها.

    بعد تنفيذ الشفرة، يجب أن ترى النتائج المتوقعة كما هو مطلوب في المثال الخاص بك.

    المزيد من المعلومات

    بالطبع، سأوفر المزيد من المعلومات حول الشفرة والطريقة التي تعمل بها:

    1. الفئات الداخلية (Inner Classes): في الشفرة المقدمة، تم استخدام فئتين داخليتين، وهما PurchaseItemCancellation وPurchaseCancellation. تستخدم هذه الفئات لتمثيل بيانات الإلغاء والعناصر المشتراة الملغاة على التوالي.

    2. Java 8 Streams API: تم استخدام Streams API لتبسيط عملية التحليل والمعالجة للبيانات. باستخدام stream() وflatMap() وcollect() ودوال أخرى من Streams API، يمكن فصل وتحليل البيانات بطريقة سهلة وفعالة.

    3. دمج العمليات (Flattening Operations): باستخدام flatMap()، تم فتح القوائم المتداخلة لإجراء عمليات الاستخراج والمعالجة على كل عنصر بشكل منفصل. هذا يسمح بتحقيق العمليات المطلوبة بشكل فعال.

    4. تحليل البيانات وتجميعها (Data Processing and Aggregation): تم استخدام Collectors.groupingBy() وCollectors.mapping() وCollectors.reducing() لتجميع البيانات وتحليلها حسب الاحتياجات المحددة. يتم تقسيم البيانات بواسطة معرف العنصر، ثم يتم حساب مجموع الكميات لكل عنصر.

    5. الطباعة والتحقق: يتم طباعة النتائج النهائية باستخدام forEach() للتحقق من صحتها ومطابقتها للمتوقع.

    هذه المعلومات توضح كيف تم تنفيذ الشفرة لتحقيق الغرض المطلوب، بما في ذلك الاستفادة الكاملة من Java 8 Streams API وتقنيات تحليل البيانات والتجميع.

  • Deep Merge in Elixir Maps

    في بيئة برمجة Elixir، عند العمل مع الخرائط (Maps)، قد تحتاج في بعض الأحيان إلى دمج (merge) الخرائط بعمق، أي دمج القيم في المفاتيح المتداخلة. على سبيل المثال، عند دمج خريطة تحتوي على خريطة داخلية في مفتاح مع خريطة أخرى تحتوي على نفس المفتاح الداخلي، يمكن أن تؤدي عملية الدمج العادية باستخدام Map.merge/2 إلى استبدال القيم بدلاً من دمجها.

    لحل هذه المشكلة وتحقيق الدمج العميق، يمكنك استخدام دالة مساعدة مثل Map.merge/3 بتمرير قيمة &Map.merge/3 كدالة تابعة. هذا يسمح بدمج الخرائط بشكل عميق بدون الحاجة إلى كتابة دوال متكررة معقدة. توفر هذه الطريقة حلاً بديلاً أنيقًا وفعالًا للدمج العميق دون الحاجة إلى استخدام دوال مخصصة.

    لذا، يمكنك تطبيق الحل التالي:

    elixir
    Map.merge(%{ a: %{ b: 1 }}, %{ a: %{ c: 3 }}, &Map.merge/3) # => %{a: %{b: 1, c: 3}}

    في هذا الحل، تم استخدام Map.merge/3 حيث يتم تمرير الدالة &Map.merge/3 كوظيفة تابعة. هذا يؤدي إلى استدعاء الدالة المحددة مرة أخرى عند الحاجة إلى دمج القيم في المفاتيح المتداخلة، مما يسمح بالدمج العميق دون الحاجة إلى كتابة الدوال المتكررة بشكل يدوي.

    بهذه الطريقة، يمكنك الحصول على النتيجة المرجوة بطريقة بسيطة وفعالة دون الحاجة إلى كتابة رمز إضافي أو استخدام دوال مخصصة للدمج العميق.

    المزيد من المعلومات

    عند العمل مع لغة برمجة Elixir والتعامل مع الخرائط (Maps)، يُعتبر الدمج العميق (Deep Merge) أحد العمليات الشائعة التي قد تحتاج إليها عندما تحتوي الخرائط على تداخل مفاتيحها. يشير الدمج العميق إلى عملية دمج القيم في المفاتيح المتداخلة بدلاً من استبدالها.

    باستخدام دالة Map.merge/3، يمكنك دمج الخرائط بطريقة عميقة عن طريق تمرير دالة تابعة كوسيط للدمج. تقوم هذه الدالة التابعة بتحديد كيفية التعامل مع القيم عند وجود تداخل في المفاتيح.

    في الحل الذي تم تقديمه، تم استخدام Map.merge/3 لدمج الخرائط %{ a: %{ b: 1 }} و%{ a: %{ c: 3 }} بواسطة دالة تابعة &Map.merge/3. هذا يُجري الدمج العميق بحيث يتم الاحتفاظ بكل القيم بدلاً من استبدالها.

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

    يعكس هذا الحل ميزة Elixir في القدرة على التعامل مع البيانات بطريقة بسيطة وفعالة، وتوفير أدوات مدمجة للتعامل مع العمليات الشائعة مثل الدمج العميق.

  • Parsec: Monads vs Applicatives

    عند البدء في استخدام Parsec (وأنا لدي تجربة قليلة في البرمجة باستخدام Haskell)، قد تكون محتارًا قليلاً بين استخدام المونادات (Monads) أو الأبليكاتيف (Applicatives). الانطباع العام الذي اكتسبته بعد قراءة كتب مثل “Real World Haskell” و “Write You a Haskell”، بالإضافة إلى بعض الأسئلة التي طرحت هنا، هو أن الأبليكاتيف تُفضل، لكنني حقًا لا أعلم.

    لذا، الأسئلة التي تطرح نفسها هي:

    1. ما هو النهج المُفضل؟
    2. هل يمكن خلط المونادات والأبليكاتيف واستخدام كل منهما حسب الحاجة؟
    3. إذا كانت الإجابة على السؤال السابق بنعم، هل يجب عليّ فعل ذلك؟

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

    بالنسبة للسؤال الثاني، نعم، يمكن خلط المونادات والأبليكاتيف واستخدام كل منهما في الحالات التي تتناسب معها. في بعض الأحيان، قد تحتاج إلى استخدام المونادات لتوفير المزيد من المرونة في التعامل مع الآثار الجانبية أو سير العمل المعقد، بينما قد يكون استخدام الأبليكاتيف أكثر فعالية في حالات أخرى حيث لا تكون هناك حاجة لهذه المرونة.

    أما بالنسبة للسؤال الثالث، فإن قرار استخدام المونادات أو الأبليكاتيف أو خلطهما يعتمد على الحالة الخاصة وتفضيلاتك الشخصية. إذا كانت الأبليكاتيف تقدم الحل المناسب بدون تعقيدات إضافية، فقد يكون من الأفضل استخدامها، ولكن في الحالات التي تتطلب التعامل مع الآثار الجانبية أو التحكم في تسلسل العمليات بشكل أكبر، قد يكون من الأفضل اللجوء إلى المونادات.

    باختصار، توجد فوائد لكل من المونادات والأبليكاتيف، وقد تحتاج إلى تقييم الحالة الخاصة بك والاعتماد على تجربتك وفهمك للتقنيات لاتخاذ القرار الأمثل.

    المزيد من المعلومات

    بالطبع، إليك المزيد من المعلومات حول استخدام المونادات والأبليكاتيف في Parsec وHaskell بشكل عام:

    1. المونادات (Monads):

      • في Haskell، المونادات هي نوع من الهياكل البيانية التي تسمح بتمثيل سياقات الحسابات المتتالية.
      • يُستخدم المونادات في Parsec لتمثيل العمليات التي تتطلب الانتظار على النتائج السابقة لاتخاذ القرارات المستقبلية.
      • على سبيل المثال، عند كتابة محلل لغة، قد يكون من الضروري استخدام المونادات لإدارة حالات الخطأ أو للتحكم في تسلسل العمليات.
    2. الأبليكاتيف (Applicatives):

      • الأبليكاتيف هي هيكل بياني يسمح بتطبيق وظائف على قيم مُعبّر عنها.
      • تستخدم الأبليكاتيف في Parsec عندما يكون من السهل توصيف العمليات بطريقة تعتمد على تطبيق الوظائف على البيانات.
      • على سبيل المثال، عندما تكون عملية التحليل بسيطة وتستفيد من تطبيق الوظائف على البيانات بدون الحاجة إلى التحكم في تسلسل العمليات، قد يكون الأبليكاتيف هو الخيار الأمثل.
    3. الاستخدام المشترك:

      • يُمكن استخدام المونادات والأبليكاتيف معًا في Parsec وفقًا لحاجة المشروع والمتطلبات.
      • عندما تكون الحالة تتطلب التحكم في التسلسل وإدارة الآثار الجانبية، يمكن استخدام المونادات.
      • أما إذا كان التحليل يمكن توصيفه بواسطة تطبيق الوظائف على البيانات بشكل مستقل، فقد يكون الأبليكاتيف هو الخيار المفضل.
    4. الخلط بين المونادات والأبليكاتيف:

      • يُمكن خلط استخدام المونادات والأبليكاتيف بحسب الحاجة.
      • قد تحتاج إلى استخدام المونادات في بعض الأجزاء من الكود حيث تكون المرونة في التحكم في التسلسل أو إدارة الأخطاء ضرورية.
      • وفي الأجزاء الأخرى من الكود، قد تكون الأبليكاتيف هي الخيار الأمثل للتعبير عن عمليات بسيطة ومستقلة.

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

  • Understanding Haskell’s let Expression and IO Type

    في البداية، يجب أن نفهم جيدًا كيف يتم استخدام تعبير let في هذا السياق. في الكود الذي قدمته، يتم استخدام let لتعريف متغير f الذي يحتوي على تعبير putStrLn "foo"، ومن ثم يتم استخدام هذا المتغير في تعبير آخر in 42. لنقم بتحليل ذلك.

    أولاً، نلقي نظرة على نوع f، وهو يتم تعيينه بناءً على التعبير putStrLn "foo". تعتبر putStrLn وظيفة تقوم بطباعة سلسلة نصية إلى الإخراج القياسي (stdout) ولا تعيد قيمة. لذلك، يكون نوع f هو IO (). يشير IO () إلى أن التعبير يتعامل مع الإدخال والإخراج ولا يعيد قيمة نهائية.

    السبب في عدم ظهور نص “foo” قبل الناتج 42 هو نوع التعبير f الذي يحتوي على putStrLn "foo". يجري تقييم putStrLn "foo" في وقت الركض، ولكن نظرًا لأن هذا التعبير يقوم بالطباعة ولا يعيد قيمة، فإن النص “foo” لا يظهر على الإخراج بشكل مباشر.

    أما بخصوص السؤال الأخير حول عدم عمل :t f، فإن ذلك يرجع إلى نطاق المتغير f. فيما يتعلق بالبيئة التفاعلية في GHCi، فإن المتغيرات التي تم تعريفها باستخدام let في تعبير واحد ليست مرئية في تعبيرات أخرى. لذلك، عند محاولة استخدام :t f، يظهر خطأ “Not in scope: ‘f’” لأن f ليس معرفًا في السياق الحالي.

    لحل هذه المشكلة، يمكنك استخدام let في نفس السياق الذي تحاول فيه استخدام :t f، مثل:

    haskell
    λ: let f = putStrLn "foo" in :t f

    وبهذا، ستتمكن من الحصول على نوع f بنجاح.

    المزيد من المعلومات

    لفهم أفضل حول كيفية عمل تعبير let في السياق الحالي ولماذا يحدث ما يحدث، يجب النظر في النظام النصي للغة هاسكيل (Haskell).

    تعتبر Haskell لغة برمجة ذات نظام نصي صارم، وهذا يعني أن التعبيرات يتم تقييمها بشكل جاد وبترتيب محدد. عند استخدام let في هذا السياق، يتم تقييم التعبير الذي يتبع in بشكل مستقل ومن ثم يتم استخدام النتيجة في السياق الذي يلي in. في حالة الكود الذي قدمته، يتم تقييم putStrLn "foo" أولاً، ونتيجتها هي تنفيذ العملية الجانبية لطباعة “foo”، ولكن لا توجد قيمة تُرجع. ثم يتم استخدام النتيجة الفعلية لـ let (التي هي 42 في هذه الحالة) في السياق الرئيسي.

    بمعنى آخر، عندما يتم تنفيذ let f = putStrLn "foo" in 42، يتم طباعة “foo” في الواقع، ولكن لا يتم عرض الناتج على الشاشة بشكل مباشر في GHCi. يظهر الناتج 42 بدون ظهور “foo”، لكن يتم تنفيذ العملية الجانبية (putStrLn "foo") عند تقييم let.

    فيما يتعلق بسؤال لماذا :t f لا يعمل خارج نطاق let، يعود الأمر إلى نطاق المتغير. يتم تعريف f داخل نطاق let، ولا يكون مرئيًا في السياق الرئيسي للتفاعل في GHCi. لاستخدام :t f بنجاح، يجب تعريف f في نفس النطاق الذي يتم فيه استخدام :t f، وهذا يمكن أن يتم بشكل مباشر باستخدام let كما تم ذكره في الإجابة السابقة.

  • فهم الاختلاف بين updateAndGet() وaccumulateAndGet() في AtomicInteger

    عند مناقشة الفرق الوظيفي بين AtomicInteger.updateAndGet() وAtomicInteger.accumulateAndGet() في لغة البرمجة جافا، يمكن أن نشير إلى أن هاتين الوظيفتين تتيحان للمطورين تنفيذ عمليات ذات طابع ذري بشكل آمن في بيئات متعددة الخيوط. تتبع هاتين الوظيفتين نفس الفكرة الأساسية لتحديث قيمة AtomicInteger، لكن هناك فارق يكمن في كيفية تنفيذ هذه التحديثات.

    تُستخدم AtomicInteger.updateAndGet() لتحديث القيمة في مكانها باستخدام الدالة التي يتم تمريرها كواجهة دالة. في حين أن AtomicInteger.accumulateAndGet() تقوم بتنفيذ عملية تجميع (أو تراكم) باستخدام دالة التراكم التي تمرر أيضًا كواجهة دالة.

    من الناحية العملية، يمكن استخدام AtomicInteger.updateAndGet() لتحقيق نفس النتيجة كما في AtomicInteger.accumulateAndGet() في السياق الذي قدمته في المثال البسيط. ولكن هناك فارق يظهر بشكل واضح عند استخدام وظيفة تجميع مع accumulateAndGet().

    عند استخدام accumulateAndGet()، يتم استخدام القيمة الحالية لـ AtomicInteger كواحدة من المداخل للدالة التي تقوم بالتجميع، ويتم تحديث القيمة في المكان بالناتج الجديد. هذا يسمح بتراكم القيم بشكل متسلسل وفعال في السياقات التي تتطلب ذلك.

    بشكل عام، يمكن القول أن accumulateAndGet() تعتبر أكثر قوة ومرونة في بعض الحالات حيث يكون التراكم ذو أهمية. ومع ذلك، في الحالات البسيطة، يمكن استخدام updateAndGet() بشكل مكافئ بطريقة أقل تعقيدًا.

    المزيد من المعلومات

    لفهم الاختلافات بين AtomicInteger.updateAndGet() وAtomicInteger.accumulateAndGet() بشكل أفضل، يمكننا التركيز على تصميم وغرض كل منهما.

    1. updateAndGet():

      • يقوم بتحديث قيمة AtomicInteger باستخدام دالة يتم تحديدها.
      • الدالة المحددة تأخذ القيمة الحالية للـ AtomicInteger كمدخل وتُرجع القيمة الجديدة للتحديث.
      • يستخدم هذا النمط في حالات بسيطة حيث يكون التحديث مباشرًا ولا يتطلب تراكم القيم.
    2. accumulateAndGet():

      • يقوم بتنفيذ عملية تراكم باستخدام دالة تم تحديدها.
      • الدالة المحددة تأخذ قيمة AtomicInteger الحالية وقيمة مُدخلة كمدخلين وتُرجع القيمة الجديدة للتحديث.
      • يُستخدم هذا النمط عندما يكون هناك حاجة لتجميع القيم، مثل تجميع النتائج في سياق متعدد الخيوط.

    عند النظر إلى الشيفرة المقدمة في المثال السابق، يظهر أن updateAndGet() و accumulateAndGet() يمكن استخدامهما بشكل متبادل في بعض الحالات. ومع ذلك، يصبح وضوح الاستخدام الفعلي لكل منهما أكثر وضوحًا في حالات أكثر تعقيدًا أو عندما يكون هناك حاجة لتراكم النتائج بشكل متسلسل.

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

  • فهم رمز -> في Java وتحويل تعبيرات اللامبدا لدعم Java 7

    فيما يتعلق برمز -> في الكود الذي قدمته، يُعتبر هذا الرمز جزءًا من ميزة معروفة باسم تعبيرات اللامبدا (Lambda Expressions) التي تم إضافتها في Java 8. تعتبر تعبيرات اللامبدا أحد التحسينات الهامة التي أدخلتها Java 8 لتسهيل كتابة الشيفرة وتحسين فعالية البرمجة.

    تعبير اللامبدا يُستخدم لتبسيط كتابة التعبيرات الوظيفية (Functional Expressions) في Java. في هذا السياق، يتم استخدام -> لإنشاء واستخدام كائنات واجهات وظيفية (Functional Interfaces) دون الحاجة إلى كتابة كود طويل.

    الكود الذي قدمته يستخدم تعبير لامبدا في السطور التالية:

    java
    .retryWhen((ClientResponse response) -> response.getStatus() != 200)

    هنا، يُستخدم تعبير اللامبدا لتعيين سلوك الإعادة التلقائية (Retry Policy) على أساس حالة الاستجابة من الخادم. يُعبر -> عن جزء الوظيفة.

    بالنسبة لتوافق الكود مع Java 7، يجب أولاً التأكد من أن النسخة المستخدمة من Java هي 8 أو أعلى لدعم تعبيرات اللامبدا. إذا كانت النسخة أقل من ذلك، يمكن تحويل الكود باستخدام واجهات وظيفية تقليدية وكتابة كلاسات منفصلة تمثل السلوك المطلوب. ومن ثم، يتم استخدام كائنات هذه الكلاسات لتحديد سلوك الإعادة التلقائية.

    لن يكون التحويل مباشرًا، ولكن يمكن تحقيق الغرض من خلال كتابة كود يعكس نفس السلوك باستخدام البنية التقليدية في Java 7.

    باختصار، رمز -> يُستخدم للدلالة على تعبير لامبدا في Java، ويمثل جزءًا أساسيًا من مفهوم البرمجة الوظيفية التي تمت إضافتها في Java 8.

    المزيد من المعلومات

    تعتبر تعبيرات اللامبدا في Java تطويرًا هامًا في اللغة، حيث أُدخلت لتمكين المبرمجين من كتابة كود أكثر إيجازًا وفعالية عند التعامل مع وظائف واجهات البرمجة (APIs) والبرمجة الوظيفية.

    تعبيرات اللامبدا تأتي في شكل مُختصر وتعبيري، مما يجعلها مثالية للإستخدام في السياقات التي تتطلب تمرير وظائف كمعلمة أو استخدام واجهات وظيفية صغيرة الحجم.

    في الكود الذي قدمته، يستخدم تعبير اللامبدا لتعريف شرط الإعادة التلقائية عندما يكون حالة الاستجابة من الخادم غير تساوي 200. إليك توضيح إضافي:

    java
    .retryWhen((ClientResponse response) -> response.getStatus() != 200)

    هنا، يُعبر (ClientResponse response) -> response.getStatus() != 200 عن واجهة وظيفية تحتوي على وظيفة واحدة فقط، وهي شرط التكرار (Retry Condition). يُمكن تعويضها بكود يستخدم واجهة وظيفية تقليدية، وتبدو السطور التالية مثالًا على كيفية فعل ذلك:

    java
    .retryWhen(new RetryCondition() { @Override public boolean shouldRetry(ClientResponse response) { return response.getStatus() != 200; } })

    هنا، تم استبدال تعبير اللامبدا بكائن من الواجهة الوظيفية RetryCondition التي تحتوي على وظيفة shouldRetry تقوم بنفس الغرض.

    تعبيرات اللامبدا تُستخدم أيضًا في الحالات التي تتطلب تنقل بين موضوعات (Closures)، حيث يُمكن للتعبيرات تلك أن تستخدم متغيرات من السياق الخارجي بشكل آمن وفعّال.

    باختصار، تعتبر تعبيرات اللامبدا أحد الأدوات المهمة في تحسين أداء البرمجة في Java وتحقيق كود أكثر إيجازًا وفهمًا.

زر الذهاب إلى الأعلى
إغلاق

أنت تستخدم إضافة Adblock

يرجى تعطيل مانع الإعلانات حيث أن موقعنا غير مزعج ولا بأس من عرض الأعلانات لك فهي تعتبر كمصدر دخل لنا و دعم مقدم منك لنا لنستمر في تقديم المحتوى المناسب و المفيد لك فلا تبخل بدعمنا عزيزي الزائر