البرمجة الوظيفية في Go: الإمكانيات والأمثلة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

البرمجة الوظيفية في Go: من النظرية إلى التطبيق



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



1. الوظائف من الدرجة الأولى والوظائف عالية المستوى



في Go، الوظائف هي كائنات من الدرجة الأولى. وهذا يعني أنه يمكن تعيينها لمتغيرات، وتمريرها كوسائط، وإرجاعها من وظائف أخرى. الوظائف التي تعمل مع وظائف أخرى تسمى وظائف عالية المستوى.



مثال: تمرير دالة كوسيط



package main

import "fmt"

// Higher-order function: takes a function and applies it to a numberfunc applyOperation(x int, op func(int) int) int { return op(x)}

func double(x int) int { return x * 2}

func square(x int) int { return x * x}

func main() { result1 := applyOperation(5, double) result2 := applyOperation(5, square)

fmt.Println("Double:", result1) // 10 fmt.Println("Square:", result2) // 25}


مثال: إرجاع دالة من دالة (مصنع دوال)



package main

import "fmt"

// Returns a function that adds a given valuefunc createAdder(x int) func(int) int { return func(y int) int { return x + y }}

func main() { addFive := createAdder(5) addTen := createAdder(10)

fmt.Println(addFive(3)) // 8 fmt.Println(addTen(3)) // 13}


2. الإغلاقات في Go



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



مثال: مولد تسلسل



package main

import "fmt"

func sequenceGenerator() func() int { i := 0 return func() int { i++ return i }}

func main() { next := sequenceGenerator()

fmt.Println(next()) // 1 fmt.Println(next()) // 2 fmt.Println(next()) // 3

// New generator starts from 1 next2 := sequenceGenerator() fmt.Println(next2()) // 1}


هام: كن حذرًا مع الإغلاقات في الحلقات. في الإصدارات الأقدم من Go (قبل 1.22)، كان متغير الحلقة i يُعاد استخدامه، مما أدى إلى أخطاء. ابتداءً من Go 1.22، يتم التقاط متغيرات الحلقة بالقيمة.



3. الدوال البحتة وعدم قابلية التغيير



الدالة البحتة هي دالة:

  • تعيد دائمًا نفس النتيجة لنفس الوسائط.
  • ليس لها آثار جانبية (لا تعدل الحالة الخارجية، لا تكتب إلى ملفات/شبكة).

لا تحتوي Go على دعم مدمج لعدم قابلية التغيير، ولكن يمكننا اتباع هذا المبدأ بأنفسنا.



مثال: دالة بحتة مقابل دالة غير بحتة



package main

import "fmt"

// IMPURE function: modifies an external slicevar counter int

func impureAddToSlice(s []int, val int) []int { counter++ // side effect return append(s, val)}

// PURE function: creates a new slicefunc pureAddToSlice(s []int, val int) []int { newSlice := make([]int, len(s)+1) copy(newSlice, s) newSlice[len(s)] = val return newSlice}

func main() { original := []int{1, 2, 3}

// Pure approach result := pureAddToSlice(original, 4) fmt.Println("Original:", original) // [1 2 3] - unchanged fmt.Println("Result:", result) // [1 2 3 4]}


نصيحة: للعمل مع هياكل غير قابلة للتغيير

المدونات

توصيات الكتب