نموذج وظيفي على الذهاب: التقنيات الأساسية



مرحباً بالجميع ، نذكرك أنه في هذا الشهر في OTUS ، ستبدأ مجموعة جديدة في دورة Golang Developer . على الرغم من بعض الكراهية للمقال السابق حول Golang ، قرر كاتبنا المستقل المجازفة بمتابعة سلسلة من المقالات المكرسة لهذه اللغة. سنحاول اجتياز هذا الجليد الرقيق مرة أخرى ، بالاعتماد على ما يبدو أن جولانج تعتمد عليه - النموذج الوظيفي.



نذكرك أن هذه المقالة هي نوع من المواد لـ "القراءة اللامنهجية" ولا تتعلق ببرنامج الدورة التدريبية ، والذي يمكن العثور عليه هنا .

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



الآن هناك الكثير من الضجيج حول النموذج الوظيفي (FP). ومع ذلك ، فهي أيضًا ليست علاجًا ناجعًا لجميع المشكلات ، كما أن لها إيجابيات وسلبيات.

باختصار ما هو نموذج وظيفي


جاء النموذج الوظيفي للبرمجة من الرياضيات. إنه يشكل المتطلبات التالية للبرنامج:

  • لا تغيير في البيانات الحالية.
  • لا توجد حالة خفية.

ماذا يعطينا هذا؟

وظائفنا تعمل دون آثار طرف ثالث. بمعنى آخر ، يجب أن تقوم الدالة بإرجاع قيمة فقط ويجب ألا تؤثر على أي بيانات خارجية.

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

إذن ، ما هي الإمكانيات التي تتمتع بها Golang لتنفيذ النموذج الوظيفي:

وظائف من الدرجة الأولى


وظائف الدرجة الأولى متوفرة في العديد من لغات البرمجة. من المرجح أن يعرف قارئ هذا المقال مفهومه من خلال جافا سكريبت الواسعة الانتشار ، لكنني سأكرره مرة أخرى. وظائف الفئة الأولى (دالة ذات ترتيب عالي) هي وظائف يمكنها إرجاع دالة أخرى كمعرفة ، وتأخذ وظيفة كوسيطة ، وتمرير قيمة الدالة إلى متغير آخر.
دعنا نتفق من البداية : لتوفير مساحة ، أخرجت أول سطرين من الكود المقدم هنا: "الحزمة الرئيسية" و "استيراد" استيراد "fmt". ولكن لتشغيل الشفرة على جهازك ، تذكر إضافتها).


func main() { var list = []int{15, 16, 45, 34} //      var out = forEach(list, func(it int) int { //      //forEach   ""  return (it * it) //      }) fmt.Println(out) // [225, 256, 2025, 1156] fmt.Println(list) //      } func forEach(arr []int, fn func(it int) int) []int { //      ,   ,     var newArray = []int{} //     ""   for _, it := range arr { newArray = append(newArray, fn(it)) //      for } return newArray } 


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

الإغلاق ووظائف الكاري


هناك دوائر قصيرة في العديد من لغات البرمجة الحديثة. الإغلاقات هي دالة تشير إلى متغيرات النطاق الحر لوظيفة الأم. إن وظيفة currying هي تغيير في الوظيفة من func(a,b,c) إلى func(a)(b)(c) .

فيما يلي مثال على عمليات الإغلاق والكاري في Go:

 //  func multiply(x int) func(y int) int { //    return func(y int) int { //   ,       JS return x * y } } func main() { //     var mult10 = multiply(10) var mult15 = multiply(15) fmt.Println(mult10(5)) //50 fmt.Println(mult15(15))//225 } 


وظائف نقية


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

فيما يلي مثال لوظيفة فاشلة وقذرة:

 var arrToSave = map[string]int{} //map -    -   Golang func dirtySum(a, b int) int { c := a + b arrToSave[fmt.Sprintf("%d", a, b)] = c //   ,  "%d" -       return c } 

هنا ، يجب أن تقبل وظيفتنا العمل قدر الإمكان كما هو متوقع:

 func simpleSum(x, y int) int { return x + y } func main() { fmt.Printf("%v", dirtySum(13, 12)) //      //   ""      fmt.Printf("%v", simpleSum(13, 12)) } 

"يدخل العودية بطريقة ما الشريط ، ولا يدخل أي شخص آخر الشريط"
من مجموعة من النكات الخادعة.

العودية


في نموذج وظيفي ، من المعتاد إعطاء الأفضلية للتكرار - للنقاء والشفافية ، بدلاً من استخدام التكرار البسيط خلال.

فيما يلي مثال لحساب العامل المضروب باستخدام النموذج الإلزامي والإيضاحي:

 func funcFactorial(num int) int { if num == 0 { return 1 } return num * funcFactorial(num-1) } func imperativeFactorial(num int) int { var result int = 1 for ; num > 0; num-- { //    for result *= num } return result } func main() { fmt.Println(funcFactorial(20)) //        fmt.Println(imperativeFactorial(20)) //      } 


الآن وظيفة العودية تعمل بشكل غير فعال تماما. دعنا نحاول إعادة كتابته قليلاً من أجل تحسين سرعة حسابه:

 func factTailRec(num int) int { return factorial(1, num) //    ""  } func factorial(accumulator, val int) int { if val == 1 { return accumulator } return factorial(accumulator*val, val-1) } func main() { fmt.Println(factTailRec(20)) // 2432902008176640000 } 


ازدادت سرعة الحوسبة الخاصة بنا بشكل طفيف. لن أعطي المعايير).

لسوء الحظ ، لا تنفذ Go تحسين العودية خارج الصندوق ، لذلك عليك تحسين العودية بنفسك. على الرغم من أنه بلا شك ، يمكن بالتأكيد العثور على مكتبة مفيدة حول هذا الموضوع. على سبيل المثال ، هناك مثل "Loadash for Golang" رائع حول هذا الموضوع .

الحوسبة كسول


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

 func mult(x, y int) int { fmt.Println(" ") return x * x. } func divide(x, y int) int { fmt.Println(" ") return x / y //    -  } func main() { fmt.Println(multOrDivide(true, mult, divide, 17, 3)) //   ""   ,   1  , //         fmt.Println(multOrDivide(false, mult, divide, 17, 3)) } //  if - else    ""  func multOrDivide(add bool, onMult, onDivide func(t, z int) int, t, z int) int { if add { return onMult(t, z) } return onDivide(t, z) } 


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



هذا كل شيء. لقد حصلنا فقط على مقدمة للنموذج الوظيفي في Golang. لسوء الحظ ، كان لابد من محاكاة جزء من الاحتمالات. جزئيًا ، لم يتم تضمين التقنيات الوظيفية المتقدمة تمامًا ، مثل monads ، لأن هناك الكثير من المقالات عنها في Go on the hub لا يزال بالإمكان تحسين الكثير في اللغة نفسها ، على سبيل المثال مع النسخة الكبيرة القادمة (GO 2) ، من المتوقع ظهور الوراثة في اللغة. حسنا ، سوف ننتظر ونأمل).

Source: https://habr.com/ru/post/ar476346/


All Articles