الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

تعابير لمدا في العمق Lambda Expressions

بدأه محمد سامر أبو سلو في 30 أبريل 2008 · 3 رد · 792 مشاهدة · في Microsoft Visual Basic.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

تعتبر تعابير لمدا من الإضافات المفيدة في فيجول بايزيك 2008 حيث يمكنك إعادتها كقيمة من وظيفة أو تمريرها كمحدد لوظيفة أخرى حيث تمت إضافتها للغة البايزيك كدعم للغة الاستعلامات المضمنة Linq التي تضيف إمكانيات استعلامية قوية لبرمجة البيانات في فيجول بايزيك وعندما تبدأ باستخدام تعابير لمدا سترى القوة والمرونة الكامنة فيها

ما هي تعابير لمدا

يشكل الكود التالي مثالا عن تعريف تعبير لمدا أساسي فهو يعرف تعبير لمدا يأخذ Integer ويعيد Integer بحيث يأخذ قيمة الدخل ويعيدها مضروبة بـ 2

Dim doubleIt as Func(Of Integer, integer) = _
		Function(x As Integer) x * 2

والنوع Func من الأنواع الجديدة في فيجول بايزيك 2008 وهو في الأساس إجراء مفوض Delegate يعيد نوعا يحدده المحدد الأخير ويمكنك من تمرير أربعة محددات تسبق ذلك المحدد والنوع المفوض Func معرف في المجمع System.Core.dll الأمر الذي يمكنك من الاستفادة منه فوريا وذلك لأن المجمع المذكور يتم استيراده تلقائيا عندما ننشئ تطبيقا جديدا ويمثل الكود التالي تحميلات Overloads مختلفة لـ Func

Dim f0 As Func(Of Boolean)
Dim f1 As Func(Of Integer, Boolean)
Dim f4 As Func(Of Integer, Integer, Integer, Integer, Boolean)

ففي المثال السابق f0 هو مفوض يعيد قيمة Boolean و f1 مفوض يأخذ Integer ويعيد Boolean و f4 مفوض يأخذ أربعة محددات من النوع Integer ويعيد قيمة من النوع Boolean وتكمن النقطة الأساسية في التعبير لمدا هو أنه مفوض قابل للاستدعاء تماما كالمفوضات في فيجول بايزيك 2005 فمن ناحية المساواة اليمنى في قطعة الكود الأولى يمكنك رؤية الصيغة الجديدة للتعابير لمدا فهي تبدأ بالكلمة المحجوزة Function متبوعة بقائمة من المحددات وتعبير وحيد ففي المثال السابق يأخذ تعبير لمدا محددا واحدا من النوع Integer ونلاحظ عدم وجود تعبير Return وذلك لأن المترجم يعرف النوع المؤسس عليه التعبير وبهذا يقوم بتمرير عبارة Return تلقائيا وفي هذه الحالة بما أن x هو من النوع Integer ونتيجة المعادلة هي Integer لهذا فنتيجة تعبير لمدا هي Integer أيضا ويمكن السحر في تعابير لمدا في أنه يمكن استخدامها كمفوض بسيط كما نرى في المثال

Dim doubleIt As Func(Of Integer, Integer) = _
		Function(x As Integer) x * 2
Dim z = doubleIt(20)

فإن نفذت الكود السابق سترى أن القيمة المخزنة في z هي 40 وأنت بهذا قمت بإنشاء تعبير لمدا يضاعف قيمة أي Integer يمرر له.

دعنا الآن نتفحص مثال معقد أكثر باستخدام تعابير لمدا

Dim mult As Func(Of Integer, Func(Of Integer, Integer)) = _
		Function(x As Integer) Function(y As Integer) x * y

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

Dim mult As Func(Of Integer, Func(Of Integer, Integer)) = _
		Function(x As Integer)  _
				Function(y As Integer) x * y

فتعبير لمدا الخارجي يحتوي تعبير لمدا آخر الذي يستخدم من قبل المترجم كقيمة معادة ويكون التوقيع الخاص بتعبير لمدا الداخلي مماثلا لتوقيع المفوض Func(Of Integer, Integer) في القيمة المعادة من تعبير لمدا الخارجي حيث يقوم المترجم بترجمة التعبير بأكمله دون مشاكل ويمكننا رؤية تعبير لمدا هذا كما يلي

Dim mult_10 = mult(10)
Dim r = mult_10(4)

فالسطر الأول يحدد mult_10 كـ mult(10) وبما أن Mult(10) يعيد تعبير لمدا يأخذ محدد ويضربه بـ 10 والنوع المعاد من mult_10 هو Func(Of Integer, Integer) والسطر الثاني يستدعي mult_10 ممررا له القيمة 4 بهذا ستكون القيمة المخزنة في r هي 40 ويكون نوع r هو Integer ويعتبر mult مصنع لتعابير لمدا فهو يعيد تعبير لمدا مخصص بالمحدد الأول وستلاحظ أن تعبير لمدا الداخلي يستخدم محدد تعبير لمدا الخارجي ولكن فترة حياة تعبير لمدا الداخلي تتجاوز فترة حياة تعبير لمدا الخارجي

تعابير لمدا كاستدعاءات

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

Delegate Function ShouldProcess(Of T) (element As T) As Boolean

Sub ProcessList(Of T) ( _
						Elements As List(Of T), shouldProcess As ShouldProcess(Of T))

		For Each elem in elements 
				If shouldProcess(elem) Then
						' Do some processing here
				End If
		Next
End Sub

ويكون المثال التالي تطبيقا قياسيا على المفوضات فالطريقة ProcessList ستمر على كل عنصر من القائمة وتتحقق فيما إذا كان عليها معالجة العنصر ثم تقوم ببعض المعالجة وحتى تتمكن من استخدام هذا في فيجول بايزيك 2005 عليك إنشاء وظيفة تمتلك نفس توقيع المفوض ثم تمرر عنوان تلك الوظيفة إلى الإجراء ProcessList

Class Person
		Public age As Integer
End Class

Function _PrivateShouldProcess(person As Person) As Boolean
		Return person.age > 50
End Function

Sub DoIt()
		Dim list As New List(Of Person)
		'Obtain list of Person from a database, for example
		ProcessList(list, AddressOf _PrivateShouldProcess)
End Sub

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

دعنا نرى الآن كيف يمكننا استدعاء هذا الإجراء باستخدام تعابير لمدا

Class Person
		Public age As Integer
End Class

Sub DoIt()
		Dim list As new List(Of Person)
		'Obtain list of Person from a database, for example
		ProcessList(list, Function(person As Person) person.age > 50)
End Sub

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

لماذا تم تقديم تعابير لمدا

من أجل دعم استعلامات لينك Linq كان يجب إضافة مجموعة من الإمكانيات الجديدة للغة فيجول بايزيك ومن ضمنها كانت تعابير لمدا. افترض أنه لدينا الاستعلام التالي

Dim q = From p In Process.GetProgesses() _
			   Where p.PriorityClass = ProcessPriorityClass.High _
			   Select P

فلكي يتم ترجمة هذا التعبير يجري الكثير من العمل تحت الغطاء فالمترجم سيقوم بالمرور عبر المجموعة Process.GetProcesses ويطبق المرشح الموجود في قسم Where عليها ويعيد قائمة بالعمليات التي تطابق ذلك الشرط كما نلاحظ وجود تعبير فيجول بايزيك داخل قسم Where هو p.PriorityClass = ProcessPriorityClass.High وذلك لتطبيق المرشح وهنا يقوم المترجم بإنشاء تعبير لمدا من أجل المرشح الموجود في قسم Where ويطبقه على كل عنصر في قائمة العمليات

Dim q = Process.GetProcesses().Where( _
		Function(p) p.PriorityClass = ProcessPriorityClass.High)

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

الاستدلال المحلي على النوع

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

Dim lambda As Func(Of Integer, Integer) = Function(x) x * x

ففي هذا المثال يكون نوع تعبير لمدا هو Func(Of Integer, Integer) وهو مفوض يأخذ محدد من النوع Integer ويعيد محدد من النوع Integer وكنتيجة لهذا فالمترجم يستدل آليا على أن المحدد x العائد لتعبير لمدا هو من النوع Integer والقيمة المعادة من التعبير لمدا هي Integer أيضا كما يمكنك الاستفادة من الاستدلال على نوع تعابير لمدا عندما تستدعي طريقة تأخذ مفوضا لاحظ الكود التالي

Delegate Function ShouldProcess(Of T) (element As T) As Boolean

Sun ProcessList(Of T) (_
						Elements As List(Of T), shouldProcess As ShouldProcess(Of T))
		' Method body removed for brevity
End Sub

في هذه الحالة تأخذ الوظيفة ProcessList تعبير لمدا ويمكن استدعاؤها على الشكل

Sub DoIt()
		Dim list As new List(Of A)
		' fill or obtain elements in list
		ProcessList(list, Function(a) a.x > 50)
End Sub

لاحظ أننا لم نحدد نوع المحدد الممرر للتعبير لمدا كما فعلنا سابقا وذلك لأن المعالج يستدل عليه بنفسه.

كيف يمكن حدوث شئ كهذا؟ في الحقيقة هناك عدة مستويات من الاستدلال على النوع في هذا المثال ففي البداية يرى المترجم ProcessList كإجراء عادي يأخذ list(Of T) كدخل له و ShouldProcess(Of T) في استدعاء ProcessList ويرى المترجم أن list هي المحدد الأول وأنها list(Of Person) وبما أن المحدد الثاني لا يوفر تلميحات حول ماهية نوع T فيقرر المترجم أن T من النوع Person ويستدل من هذا على أن محدد ShouldProcess(Of T) هو من النوع Person وبهذا يستدل على أن المحدد الثاني هو من النوع ShouldProcess(Of T) وأخيرا بما أن تعبير لمدا لا يقدم نوع المحدد الخاص به والمترجم يعرف أن نوع المحدد يعتمد على توقيع

المفوض ShouldProcess(Of T) وقد استدل على أن نوع المحدد a هو Person ويعتبر هذا نوعا قويا من الاستدلال على النوع فليس عليك معرفة نوع محددات المفوض عندما تبني تعبير لمدا وفي الحقيقة من الأفضل ترك المترجم يقوم بذلك العمل نيابة عنك والاستدلال على نوع النتيجة بهذه الطريقة مفيد حقيقة إن لم يكن لديك نوع مفوض وتريد من المترجم أن يقوم بتصنيعه من أجلك علما بأن هذه الميزة متوفرة في فيجول بايزيك فقط

Dim lambda = Function(x As Integer) x * x

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

Class Motorcycle
		Public color As String
		Public CC As Integer
		Public weight As Integer
End Class

Sub PrintReport(motorcycle As New Motorcycle)
		If motorcycle.color = "Red" And motorcycle.CC = 600 And _
				Motorcycle.weight > 300 And Motorcycle.weight < 400 Then

				' do something here
		End If

		' do something here

		If motorcycle.color = "Red" And motorcycle.CC = 600 And _
				Motorcycle.weight > 300 And Motorcycle.weight < 400 Then

				' do something here
		End If
End Sub

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

Sub PrintReport(motorcycle As New Motorcycle)
		Dim check = Function(m As Motorcycle) m.color =  "Red" And _
																					m.CC = 600 And _
																				   m.weight > 300 And _
																				   m.weight < 400

		If check(motorcycle) Then
				' do something here
		End If

		' do something here

		If check(motorcycle) Then
				' do something here
		End If
End Sub

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

Dim lambda = Function(x) x * x

وهنا أيضا يولد المعالج مفوض مجهول من أجلك ولكن يحدد نوع تعبير لمدا كـ System.Object وهذا يعني أنه قد تم تفعيل الربط المتأخر في هذا السيناريو عندما يكون الخيار Option Strict على الوضع Off ويعتبر هذا السيناريو جيدا بالنسبة لأولئك الذين يعتمدون على الربط المتأخر حيث أن تعابير لمدا تدعم عمليات الربط المتأخر بشكل كامل ففي المثال السابق طالما أن المعامل * معرف على الأنواع الممررة إلى تعبير لمدا فسوف يعمل

Dim a = lambda(10)
Dim b = lambda(CDec(10))
Dim c = lambda("This will throw an exception because " & _
							 "strings don't support the * operator")

وكما ترى من المثال السابق طالما أن المعامل * موجود في مكتبات زمن التشغيل بالنسبة للنوع الممرر فسوف يجري كل شئ بشكل جيد كما أن تعابير لمدا تتأقلم بشكل رائع مع الربط المتأخر في فيجول بايزيك

عن عائشة رضي الله عنها أن النبي صلى الله عليه وسلم قال: إن الله يحب إذا عمل أحدكم عملا أن يتقنه

نعيب زماننا والعيب فينا ... وما لزماننا عيب سوانا

ونهجو ذا الزمان بغير ذنب ... ولو نطق الزمان لنا هجانا

محمد سامر أبو سلو

#2

توليد الكود تحت الغطاء

بعدما استكشفنا تعابير لمدا دعنا نلقي نظرة على الكود الذي يتم توليده من قبل المترجم. انظر للكود السابق

Sub TestLambda()
		Dim doubleIt As Func(Of Integer, Integer) = _
						Function(x As Integer) X * 2
		Console.WriteLine(doubleIt(10))
End Sub

أنت تعلم أن Func هو مفوض والمفوضات هي مؤشرات للوظائف فكيف يقوم المترجم إذا بالعمل؟ في هذه الحالة يقوم المترجم بإصدار وظيفة جديدة ويربطها بمفوض يشير إلى تلك الوظيفة الجديدة

Private Function $GeneratedFunction$(x As Integer) As Integer
		Return x * 2
End Function

Sub TestLambda()
		Dim doubleIt As Func(Of Integer, Integer) = _
				AddressOf $GeneratedFunction$
		Console.WriteLine(doubleIt(10)
End Sub

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

تعابير لمدا ورفع المتغيرات

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

Function MakeLambda() As Func(Of Integer, Integer)
		Dim y As Integer = 10
		Dim addTen As Func(Of Integer, Integer) = Function(ByVal x) x + y
		Return addTen
End Function

Sub UseLambda()
		Dim addTen = MakeLambda()
		Consloe.WriteLine(addTen(5))
End Sub

فهذا الكود سيقوم بطباعة 15 على نافذة الكونسول عندما يتم استدعاء UseLambda ولكن يمكن أن تسأل نفسك كيف يعمل هذا؟ تحدد الوظيفة MakeLambda المتغير y كمتغير محلي والتعبير لمدا يستخدم y ولكن التعبير لمدا يتم إعادته كنوع معاد من الوظيفة MakeLambda والوظيفة UseLambda تحصل على التعبير لمدا من الوظيفة MakeLambda وتنفذ التعبير لمدا ويبدو الأمر كما لو أن المتغير y قد تم تذكره من قبل التعبير لمدا. ففترة حياة المتغير y تنتهي مع نهاية الطريقة MakeLambda فعندما نحصل على التعبير لمدا من MakeLambda فسوف تصبح MakeLambda خارج المجال ويجب إزالة المساحة التي تحجزها في المكدس وبطريقة ما يعلق هذا المتغير مع تعبير لمدا وهذا ما يعرف برفع المتغير Variable Lifting ففي هذه الحالة يدعى المتغير y بالمتغير المرفوع وكما ترى فالمتغيرات المرفوعة تعتبر ميزة برمجية قوية فالمترجم يقوم بالكثير من العمل من أجل تمكينك من إمساك حالة المتغير حيث يحفظها خارج مجال فترة حياتها الطبيعية فعندما يصادف المترجم تعابير لمدا تستخدم متغيرات حرة يقوم برفع المتغير إلى فئة تدعى Closure بحيث تكون فترة حياة هذه الفئة تمتد إلى ما بعد فترة حياة المتغيرات الحرة المستضافة داخلها ويقوم المترجم بإعادة كتابة الوصول إلى المتغيرات في الطرق ليتم الوصول إلى نسختها الموجودة في الفئة Closure

دعنا نسير مرة أخرى عبر المثال MakeLambda

Dim MakeLambda() As Func(Of Integer, Integer)
		Dim y As Integer = 10
		Dim addTen As Func(Of Integer, Integer) = Function(ByVal x) x + y
Return addTen
End Function

وكما قمنا بالتحليل سابقا فالمتغير x مرتبط بمحدد التعبير لمدا ولكن المتغير y تعبير حر ويقوم المترجم بالكشف عن ذلك ويتابع بإنشاء الفئة Closure التي تلتقط المتغيرات الحرة كما في تعريف تعبير لمدا

Public Class _Closure$__1
		Public y As Integer
		Public Function _Lambda$__1(ByVal x As Integer) As Integer
				Return x + Me.y
		End Function
End Class

يمكنك رؤية أن متغير Closure يلتقط المتغير y ويخزنه في الفئة Closure ويتم تحويل المتغير الحر بعدها إلى متغير مرتبط داخل الفئة Closure كما يقوم المترجم بإعادة كتابة الطريقة التي تحتوي على التعبير لمدا لتبدو كما يلي

Function MakeLambda() As Func(Of Integer, Integer)
		Dim Closure As New _Closure$__1
		Closure.y = 10
		Return AddressOf Closure._Lambda$__1
End Function

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

Sub Test()
		Dim y As Integer = 10
		Dim Lambda As Func(Of Integer, Integer) = Function(ByVal x) x + y
		y = 20
		Console.WriteLine(Lambda(5))
End Sub

ما هي القيمة التي تظهر عند تنفيذ الوظيفة السابقة؟ إن قلت 25 فقد أصبت. فلماذا 25 إذا؟ المترجم يقوم بالتقاط وإعادة كتابة جميع المتغيرات الحرة y إلى نسخة Closure كالتالي

Sub Test()
		Dim Closure As New $Closure_Compiler_Generated_Name$
		Closure.y = 10
		Dim Lambda = AddressOf Closure.Lambda_1
		Closure.y = 20
		Console.WriteLine(Lambda(5))
End Sub

ففي الوقت الذي يتم تنفيذ تعبير لمدا فيه تكون قيمة y قد تغيرت إلى 20 وبهذا فعندما يتم تنفيذ تعبير لمدا يعيد 20 + 5 وهذا هام جدا لأنه عندما نأتي للحديث عن الحلقات وأن المتغيرات الحرة يتم التقاطها في Closure وحيد قد ترى تصرفات غريبة. انظر للمثال التالي

Sub Test()
		For I = 1 To 5
				StartThread(Function() I + 10)
		Next
End Sub

افرض أن StartThread ينشئ مسارا جديدا ويطبع النتيجة على الكونسول وطالما أنه تم التقاطه إلى Closure فيمكن أن تكون الحلقة قد غيرت قيمة I في الوقت الذي يقوم المسار فيه باستدعاء تعبير لمدا وفي هذه الحالة فالبرنامج قد لا يطبع النتيجة المتوقعة وبدلا عن ذلك عليك رؤية المتغير الملتقط داخل الحلقة

Sub Test
		For I = 1 To 5
				Dim x = I
				StartThread(Function() x + 10)
		Next
End Sub

فالكود سيلتقط الآن قيمة x في Closure والبرنامج سيطبع القيم كما هو متوقع ومن الهام جدا معرفة أية متغيرات سيتم رفعها عندما سيتم تنفيذ تعبير لمدا ومتى سيتم تغيير قيمة تلك المتغيرات المرفوعة وبذلك يمكنك التأكد من أن برنامجك يتم تنفيذه بصورة صحيحة.

استخدام تعابير لمدا بالشكل الأمثل

في فيجول بايزيك 2008يمكنك تمرير تعبير واحد كجسم لتعبير لمدا وقد تم تقديم كلمة محجوزة ثلاثية جديدة هي If لتمكنك من كتابة تعابير شرطية ذات نوع كامل

Dim x = IF(condition, 10, 20)

والكلمة المحجوزة If مشابهة لاستدعاء الوظيفة IIF فيما عدا أنها آمنة ضد النوع. وهذا يعني أنه في المثال السابق يتتبع المترجم كلا فرعي الكلمة المحجوز If ويعيد Integer وبهذا فهو يطبق قواعد الاستدلال على النوع ويقرر أن نوع x هو integer ولكن استخدام IIf سيعيد النوع Object. كما يمكنك استخدام If في تعبير لمدا

Dim x = Function(c As Customer) _
		If(c.Age >= 18, c.Address, c.Parent.Address)

ففي المثال السابق افترض أنه لديك فئة Customer يتضمن تعريفها الخاصية Address التي تمثل العنوان الحالي للزبون حيث أن تعبير لمدا يستخدم التعبير الثلاثي Ternary Expression لتطبيق الشرط على محدد الدخل فإن كان عمر الزبون مساويا أو أكثر من 18 فهو يعيد عنوانه وإلا فهو يعيد عنوان والده وهنا يتم استخدام الاستدلال على النوع أيضا ويقوم المترجم بتحديد نوع تعبير لمدا ليكون Address ثم يقوم بإنشاء النوع المفوض x بالطريقة التي تمت مناقشتها سابقا حيث يأخذ النوع المفوض Customer كدخل ويعيد Address.

تم بعون الله

مترجم بتصرف

تم تعديل هذه المشاركة بواسطة محمد سامر أبو سلو في 30 أبريل 2008 في 23:17

عن عائشة رضي الله عنها أن النبي صلى الله عليه وسلم قال: إن الله يحب إذا عمل أحدكم عملا أن يتقنه

نعيب زماننا والعيب فينا ... وما لزماننا عيب سوانا

ونهجو ذا الزمان بغير ذنب ... ولو نطق الزمان لنا هجانا

محمد سامر أبو سلو

#3

مرفق مثال عملي بسيط على استخدام تعابير لمدا - أرجو من أحد الإخوة الذين قرأوا الموضوع شرح كود البرنامج في رد على هذا الموضوع كي نحقق الفائدة من الموضوع قدر الإمكان

WadeeaaCalc.rar

عن عائشة رضي الله عنها أن النبي صلى الله عليه وسلم قال: إن الله يحب إذا عمل أحدكم عملا أن يتقنه

نعيب زماننا والعيب فينا ... وما لزماننا عيب سوانا

ونهجو ذا الزمان بغير ذنب ... ولو نطق الزمان لنا هجانا

محمد سامر أبو سلو

#4

من مواضيعي المرتبطة بهذا الموضوع الاستدلال المحلي على النوع Local Type Inference و If Operator و Lambda Expression

عن عائشة رضي الله عنها أن النبي صلى الله عليه وسلم قال: إن الله يحب إذا عمل أحدكم عملا أن يتقنه

نعيب زماننا والعيب فينا ... وما لزماننا عيب سوانا

ونهجو ذا الزمان بغير ذنب ... ولو نطق الزمان لنا هجانا

محمد سامر أبو سلو

مواضيع مشابهة