الصورة: كريس ريدفي هذه المقالة ، ستفهم ماهية النموذج الوظيفي وكيفية استخدام البرمجة الوظيفية في بيثون. سوف تتعلم أيضا عن
تجريد القائمة وفهم القائمة الأخرى.
نموذج وظيفي
في نموذج ضروري ، تكتب برنامجًا عن طريق تحديد سلسلة من الإجراءات التي يتم تنفيذها لاحقًا. في هذا الوقت ، تتغير الحالات (
تقريبًا. المترجم: المتغيرات والمصفوفات وما إلى ذلك ). على سبيل المثال ، اسمح للمتغير A بتخزين القيمة 5 ، وبعد ذلك قمت بتغيير قيمة هذا المتغير. يمكنك استخدام المتغيرات بحيث تتغير قيمها.
في نموذج وظيفي ، لا تخبر الكمبيوتر بما يجب القيام به ، بل تحدد طبيعة الإجراءات ذاتها. ما هو أكبر مقسوم مشترك للرقم ، نتيجة الحسابات من 1 إلى ن ، إلخ.
لذلك ، لا تتغير المتغيرات. بمجرد تهيئة المتغير ، يتم حفظ قيمته إلى الأبد (لاحظ أنه في اللغات الوظيفية الخالصة لا يطلق عليها حتى المتغيرات). لذلك ، في النموذج الوظيفي ، ليس للوظائف
آثار جانبية . يمكن تعريف التأثير الجانبي على أنه اللحظة التي تتغير فيها الوظيفة شيئًا ما يتجاوز حدودها. ألقِ نظرة على مثال:
a = 3 def some_func(): global a a = 5 some_func() print(a)
نتيجة تنفيذ هذا الرمز هي 5. في البرمجة الوظيفية ، يُحظر تغيير المتغيرات وتغيير وظائف شيء خارج حدودها أيضًا. كل ما يمكن القيام به هو حساب / معالجة شيء ما وإرجاع النتيجة.
الآن ، ربما كنت تفكر: "لا متغيرات ، لا آثار جانبية؟ لماذا هذا جيد؟ " سؤال جيد حقا
إذا تم استدعاء دالة مرتين باستخدام نفس المعلمات ، فمن الواضح أنها ستُرجع نفس النتيجة. إذا كنت قد درست شيئًا ما حول
الوظائف الرياضية ، فستقدر هذه الفرصة. وهذا ما يسمى ارتباط الشفافية أو الشفافية المرجعية. نظرًا لأن الوظائف ليس لها آثار جانبية ، إذا كنت تقوم بتطوير برنامج حسابي ، فيمكنك تسريع عملية التنفيذ. إذا كان البرنامج يعلم أن func (2) هو 3 ، فيمكننا تذكر ذلك. هذا يمنع الوظيفة من استدعاء مرة أخرى عندما نعرف بالفعل النتيجة.
عادة ، في البرمجة الوظيفية ، لا تستخدم الحلقات. يستخدم العودية. العودية مفهوم رياضي ، في الحقيقة ، تعني "إطعام شيء ما للذات". في دالة تكرارية ، تطلق الوظيفة نفسها على نفسها دور وظيفة فرعية. فيما يلي مثال لوظيفة عودية في بيثون:
def factorial_recursive(n):
بعض لغات البرمجة
كسول . هذا يعني أنهم يقومون بحساب كل شيء في اللحظة الأخيرة. لنفترض أنه إذا كان يجب تنفيذ التعليمات البرمجية 2 + 2 ، فسيقوم البرنامج الوظيفي بحساب النتيجة فقط عند الحاجة إلى النتيجة. سنتعلم عن بيثون الكسل قليلا في وقت لاحق.
خريطة
لفهم الخريطة ، يجب عليك أولاً التعامل مع الحاويات القابلة للتكرار. هذه حاوية يمكنك من خلالها "الانتقال". غالبًا ما تكون هذه القوائم أو المصفوفات ، ولكن يوجد العديد من هذه الحاويات في بيثون. يمكنك حتى إنشاء الحاوية الخاصة بك عن طريق إدخال
أساليب السحر . هذه الطرق ، مثل واجهات برمجة التطبيقات ، تساعد الكائنات على أن تصبح أكثر ثراء. هناك طريقتان لجعل هذا الكائن قابلاً للتكرار:
class Counter: def __init__(self, low, high):
الأسلوب السحري الأول هو "___iter__" أو dunder (ضعف تسطير أسفله) ، حيث يقوم iter بإرجاع كائن قابل للتكرار ، وغالبًا ما يستخدم هذا في بداية الحلقة. Dunder next (__next__) بإرجاع الكائن التالي.
تحقق هذا:
for c in Counter(3, 8): print(c)
نتيجة التنفيذ:
3
4
5
6
7
8
في Python ، التكرار هو كائن يحتوي فقط على طريقة __iter__. هذا يعني أنه يمكنك الوصول إلى مكان خلايا الكائن (الحاوية) ، لكن لا يمكنك "السير" عبرها. تحتوي بعض الكائنات على طريقة __next__ الرائعة فقط ، بدون طريقة __iter__ السحرية ، على سبيل المثال ، قم بتعيين (المزيد عن ذلك لاحقًا). في هذه المقالة ، سنغطي كل ما يتعلق بالكائنات القابلة للتكرار.
الآن نحن نعرف ما هو كائن قابل للتكرار ، دعنا نعود إلى وظيفة الخريطة. تسمح لنا هذه الوظيفة بتطبيق إجراء بعض الوظائف الأخرى على كل عنصر في حاوية متكررة. نريد تطبيق وظيفة على كل عنصر في القائمة ، وهذا ممكن لجميع الحاويات القابلة للتكرار تقريبًا. الخريطة ، تأخذ وسيطين: الوظيفة المطلوب تطبيقها ، والحاوية (القائمة ، إلخ).
map(function, iterable)
لنفترض أن لدينا قائمة بالعناصر التالية:
[1, 2, 3, 4, 5]
ونريد تربيع كل عنصر ، ويمكن القيام بذلك على النحو التالي:
x = [1, 2, 3, 4, 5] def square(num): return num*num print(list(map(square, x)))
وظائف وظيفية في بيثون كسول. إذا لم نقم بإضافة "list ()" ، فستقوم الوظيفة بتخزين وصف الحاوية (list) ، وليس القائمة نفسها. نحتاج مباشرةً إلى إخبار Python لتحويل هذا إلى قائمة.
إنه أمر غريب بعض الشيء أن تنتقل من تعريف غير كسول إلى تعريف كسول فجأة. سوف تعتاد على ذلك إذا كنت تفكر بطريقة وظيفية أكثر من كونها ضرورية.
وظائف الكتابة ، على سبيل المثال ، "مربع (الأسطوانات)" أمر طبيعي ، ولكن ليس صحيحًا تمامًا. هل نحتاج إلى الإعلان عن وظيفة بأكملها فقط لاستخدامها في الخريطة؟ يمكن تبسيط ذلك عن طريق إدخال وظائف (مجهول) lambda.
تعبيرات لامدا
تعبيرات Lambda هي وظائف في سطر واحد ، على سبيل المثال ، فيما يلي تعبير lambda يربيع الرقم الناتج:
square = lambda x: x * x
وتشغيل هذا:
>>> square(3)
9
يمكنني سماعك. "براندون ، أين هي الحجج؟" ما هذا كله؟ هذا ليس مثل وظيفة. "
نعم ، يمكن أن يكون مربكا ، ولكن يمكن تفسيره. في هذا الخط ، نخصص شيئًا للمتغير "square". هذا الجزء:
lambda x: x * x
يخبر بيثون أننا نستخدم وظيفة lambda ، والإدخال يدعى x. كل شيء بعد النقطتين هو ما سيحدث للمدخلات ، وسنحصل تلقائيًا على النتيجة لاحقًا.
إلى برنامجنا اتخذ شكل سطر واحد ، تحتاج إلى القيام بذلك:
x = [1, 2, 3, 4, 5] print(list(map(lambda num: num * num, x)))
لذلك ، في تعبيرات lambda ، تكون الحجج على اليسار ، والإجراءات المتعلقة بها على اليمين. هذا غير مرتب قليلاً ، لا أحد ينكر. الحقيقة هي أن هناك شيء ما فيه ، لكتابة مثل هذا الرمز الوظيفي. أيضًا ، من الجيد جدًا تحويل الوظائف إلى سطر مفرد.
الحد
Reduce هي وظيفة تحول الحاوية القابلة للتكرار إلى شيء واحد. بمعنى ، يتم إجراء حسابات تحول القائمة إلى رقم واحد. يبدو مثل هذا:
reduce(function, list)
يمكننا (وغالبا ما نستخدم) وظائف lambda كوسيطة دالة.
إذا كنا نريد مضاعفة جميع الأرقام في القائمة ، فيمكن القيام بذلك على النحو التالي:
product = 1 x = [1, 2, 3, 4] for num in x: product = product * num
ومع الحد من ذلك سيبدو كما يلي:
from functools import reduce product = reduce((lambda x, y: x * y),[1, 2, 3, 4])
ستكون النتيجة هي نفسها ، لكن الرمز أقصر ومع معرفة البرمجة الوظيفية لاستخدامه بشكل أكثر دقة.
فلتر
تأخذ وظيفة المرشح حاوية قابلة للتكرار وترشحها وفقًا لقاعدة معينة (دالة أيضًا).
عادة ما يستغرق وظيفة وقائمة المدخلات. في وقت لاحق ، يتم تطبيق وظيفة على كل عنصر في القائمة ، وإذا كانت الدالة ترجع إلى True ، لا يحدث شيء ، وإذا تم False ، تتم إزالة العنصر من القائمة.
بناء الجملة:
filter(function, list)
دعونا نلقي نظرة على مثال دون استخدام مرشح:
x = range(-5, 5) new_list = [] for num in x: if num < 0: new_list.append(num)
جنبا إلى جنب مع مرشح:
x = range(-5, 5) all_less_than_zero = list(filter(lambda num: num < 0, x))
وظائف النظام العالي
يمكن أن تأخذ وظائف الترتيب العالي وظائف كوسائط وإعادتها. مثال بسيط سيبدو كالتالي:
def summation(nums): return sum(nums) def action(func, numbers): return func(numbers) print(action(summation, [1, 2, 3]))
أو مثال أكثر بساطة:
def rtnBrandon(): return "brandon" def rtnJohn(): return "john" def rtnPerson(): age = int(input("What's your age?")) if age == 21: return rtnBrandon() else: return rtnJohn()
تذكر في وقت سابق قلت أن البرمجة الوظيفية الحقيقية لا تستخدم المتغيرات. وظائف الترتيب العالي تجعل هذا ممكنًا. لا تحتاج إلى حفظ المتغير في مكان ما إذا كنت تقوم بتمرير المعلومات عبر "نفق" طويل من الوظائف.
جميع الوظائف في بيثون هي كائنات من الدرجة الأولى. يتم تعريف كائن من الفئة الأولى على هذا النحو ، والذي يتوافق مع واحد أو أكثر من المعلمات التالية:
- يخلق دورة العمل
- معين لمتغير أو عنصر في بنية بيانات
- مرت حجة وظيفة
- عاد نتيجة لتنفيذ وظيفة
لذلك فإن جميع الوظائف في بيثون هي كائنات من الدرجة الأولى ، ويمكن استخدامها كوظائف ذات ترتيب أعلى.
تطبيق جزئي
الاستخدام الجزئي (أيضًا المقاطعة) غريب بعض الشيء ، لكنه رائع جدًا. يمكنك استدعاء الوظيفة دون استخدام جميع الوسائط المحددة. لنلقِ نظرة على مثال. نريد إنشاء دالة تأخذ حجة اثنين ، الأساس والدرجة ، وتعيد القاعدة المثارة إلى السلطة ، تبدو كما يلي:
def power(base, exponent): return base ** exponent
نحتاج الآن إلى إنشاء وظيفة منفصلة للتربيع وحسابها باستخدام وظيفة الطاقة:
def square(base): return power(base, 2)
إنه يعمل ، لكن ماذا لو أردنا مكعب رقم؟ أو في الدرجة الرابعة؟ يجب أن تكتب هذه الوظائف إلى الأبد؟ بالطبع يمكنك ذلك. لكن المبرمجين كسول. إذا كررت نفس الشيء عدة مرات ، فربما يكون هناك طريقة للقيام بذلك بشكل أسرع والتوقف عن القيام بالتكرار. يمكن استخدام التطبيق الجزئي هنا. دعونا نلقي نظرة على مثال لوظيفة الطاقة باستخدام تطبيق جزئي:
from functools import partial square = partial(power, exponent=2) print(square(2))
ليس هو بارد؟ يمكننا استدعاء دالة تحتاج إلى وسيطين ، وذلك باستخدام وسيطة واحدة فقط ، وتحديد ما هي الثانية من تلقاء نفسها.
يمكنك أيضًا استخدام حلقة لمحاكاة وظيفة الطاقة التي ستعمل مع المكعبات حتى الطاقة 1000.
from functools import partial powers = [] for x in range(2, 1001): powers.append(partial(power, exponent = x)) print(powers[0](3))
البرمجة الوظيفية لا تتطابق مع شرائع الثعبان
ربما لاحظت أن الكثير من الأشياء التي نريد القيام بها في البرمجة الوظيفية تدور حول القوائم. إلى جانب وظيفة الاختزال والتطبيق الجزئي ، فإن جميع الوظائف التي شاهدتها تنشئ قوائم. Guido (خالق Python`a) لا يحب الأشياء الوظيفية في Python`e ، لأن Python لديها طريقة خاصة بها لإنشاء قوائم.
إذا كتبت "استيراد هذا" في وحدة التحكم ، فستحصل على:
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one — and preferably only one — obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let's do more of those!
هذا هو بيثون زين. هذه آية حول معنى أن تكون بيثوناً. الجزء الذي يهمنا هو:
يجب أن يكون هناك طريقة واحدة - ويفضل أن تكون واحدة - طريقة واضحة للقيام بذلك.
يجب أن يكون هناك طريقة واحدة - ويفضل أن تكون واحدة - طريقة واضحة لعمل شيء ما.
في Python ، يمكن للخريطة والمرشح أن يقومان بنفس طريقة تجريد القائمة (
الرابط ). هذا ينتهك أحد قواعد Python-Zen ، لذلك هذا الجزء من البرمجة الوظيفية ليس "pythonic".
الأشياء التالية للحديث عنها هي وظيفة lambda. في بيثون ، وظيفة لامدا هي وظيفة طبيعية. وفي الواقع هو السكر النحوي. كل من هذه الأجزاء تفعل الشيء نفسه:
foo = lambda a: 2 def foo(a): return 2
قد تظل الوظيفة القياسية هي نفسها وظيفة lambda ، ولكن ليس العكس. لا يمكن أن تكون وظيفة Lambda هي نفسها كدالة عادية.
كانت هذه ملاحظة صغيرة حول سبب عدم توافق البرمجة الوظيفية مع الإيديولوجية الصورية. في وقت سابق ، ذكرت تجريد القوائم (
إدراج القائمة أيضًا ) ، والآن دعونا نتحدث عن ذلك.
قائمة التجريد
لقد قلت بالفعل أن كل ما يمكن القيام به باستخدام الخريطة والمرشح يمكن القيام به باستخدام قائمة التجريد. في هذا الجزء سوف نناقشه.
قائمة التجريد هي طريقة لإنشاء قوائم في بيثون. بناء الجملة:
[function for item in iterable]
فلنضع مربعا لكل عنصر في القائمة ، على سبيل المثال:
print([x * x for x in [1, 2, 3, 4]])
حسنًا ، يمكننا أن نرى كيفية تطبيق الوظيفة على كل عنصر من عناصر القائمة. كيف نحصل على مرشح؟ ألقِ نظرة على هذا الكود:
x = range(-5, 5) all_less_than_zero = list(filter(lambda num: num < 0, x)) print(all_less_than_zero)
الآن استخدم قائمة التجريد:
x = range(-5, 5) all_less_than_zero = [num for num in x if num < 0]
قائمة التجريد تدعم التعبيرات الشرطية من هذا النوع. لم تعد بحاجة إلى استخدام مليون وظيفة للحصول على شيء ما. في الواقع ، إذا كنت تحاول أن تفعل شيئًا ما بقائمة ، فهناك احتمال أن تكون أكثر نظافة وأسهل في تحقيق ذلك من خلال تجريد القوائم.
ماذا لو أردنا تربيع كل عنصر من عناصر القائمة أقل من الصفر. مع وظيفة lambda والخريطة والمرشح ، ستبدو كما يلي:
x = range(-5, 5) all_less_than_zero = list(map(lambda num: num * num, list(filter(lambda num: num < 0, x))))
هذا الإدخال ليس عقلانيًا وليس بسيطًا جدًا. باستخدام تجريد القائمة ، سيبدو كما يلي:
x = range(-5, 5) all_less_than_zero = [num * num for num in x if num < 0]
قائمة التجريد جيدة فقط ، بشكل غريب بما فيه الكفاية ، للقوائم. قم بتخطيط وتصفية العمل لكل حاوية قابلة للتكرار ، فما الخطأ؟ .. نعم ، يمكنك استخدام التجريد لكل حاوية قابلة للتجميع تستوفيها.
التجريدات الأخرى
يمكنك تطبيق التجريد لكل حاوية قابلة للتكرار.
يمكن إنشاء كل حاوية قابلة للتكرار باستخدام التجريد. بدءًا من الإصدار 2.7 ، يمكنك إنشاء قاموس (جدول التجزئة).
إذا كان شيء ما عبارة عن حاوية قابلة للتكرار ، فيمكن إنشاء شيء ما. دعونا نلقي نظرة على المثال الأخير باستخدام مجموعة. إذا كنت لا تعرف المجموعة ، فراجع
هذا المقال الذي كتبه أنا أيضًا. باختصار:
- Set عبارة عن حاوية من العناصر ؛ ولا تتكرر العناصر الموجودة فيها
- النظام ليس مهما
كما قد تلاحظ ، فإن set ، مثل القاموس ، يستخدم الأقواس المتعرجة. بيثون ذكي حقا. سوف يخمن ما إذا كنت تستخدم تجريد القاموس أو تجريد set`a ، بناءً على ما إذا كنت تحدد معلمات إضافية للقاموس أم لا. إذا كنت تريد معرفة المزيد عن التجريدات ، اقرأ
هذا . إذا عن التجريد والجيل ، ثم
هذا واحد .
يؤدي
البرمجة الوظيفية كبيرة. يمكن أن تكون الشفرة الوظيفية نظيفة أو غير نظيفة للغاية. بعض الثعابين المتشددين لا يقبلون النموذج الوظيفي في بيثون. يجب عليك استخدام ما تريد وما يناسبك.
صفحة المؤلف .