تعلم لغة البايثون – الدرس الرابع والعشرون الوظائف Map, Filter, Reduce Map, Filter, Reduce In Python

تعد Map ، وFilter ، وReduce من نماذج البرمجة الوظيفية. إنها تسمح للمبرمج بكتابة كود أبسط وأقصر ، دون الحاجة إلى القلق بشأن التعقيدات مثل الحلقات والتفرعات.

بشكل أساسي ، تسمح لك هذه الوظائف الثلاث بتطبيق دالة عبر عدد من العناصر المتكررة ، في مرة واحدة كاملة. تأتي map وFilter مدمجين مع بايثون (builtins module) ولا تتطلب الاستيراد. ومع ذلك ، يجب استيراده لأنه موجود في وحدة functools. دعونا نحصل على فهم أفضل لكيفية عملهم جميعًا ، بدءًا ب Map.

Map

تحتوي الدالة map () في python على بناء الجملة التالي:

map(func، * iterables)

حيث func هي الوظيفة التي سيطبق عليها كل عنصر في عناصر متكررة (بقدر ما هو). لاحظ علامة النجمة (*) على الأشياء القابلة للتكرار؟ هذا يعني أنه يمكن أن يكون هناك أكبر عدد ممكن من المتكررات ، حتى الآن يحتوي func هذا الرقم الدقيق مثل وسيطات الإدخال المطلوبة. قبل أن ننتقل إلى مثال ، من المهم أن تلاحظ ما يلي:

في Python 2 ، تقوم الدالة map () بإعادة تشغيل قائمة. ومع ذلك ، في Python 3 ، تقوم الدالة بإرجاع كائن mapوهو كائن مولد. للحصول على النتيجة كقائمة ، يمكن استدعاء دالة list () المضمنة على كائن map. أي قائمة (map(func، * iterables))
يجب أن يكون عدد الحجج إلى func هو عدد العناصر المتكررة المدرجة.
دعونا نرى كيف تلعب هذه القواعد مع الأمثلة التالية.

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

my_pets = ['charlie', 'bella', 'oscar', 'Ace']
uppered_pets = []

for pet in my_pets:
    pet_ = pet.upper()
    uppered_pets.append(pet_)

print(uppered_pets)

سنحصل على النتيجة التالية عند التشغيل

['charlie', 'bella', 'oscar', 'Ace']

والتي ستنتج بعد ذلك [‘charlie’، ‘bella’، ‘oscar’، ‘Ace’]

مع وظائف map () ، ليس الأمر أسهل فحسب ، بل إنه أكثر مرونة أيضًا. أنا ببساطة أقوم بهذا:

# بيثون 3
my_pets = ['charlie', 'bella', 'oscar', 'Ace']

uppered_pets = list(map(str.upper, my_pets))

print(uppered_pets)

سنحصل على النتيجة التالية عند التشغيل

['charlie', 'bella', 'oscar', 'Ace']

مما ينتج عنه نفس النتيجة. لاحظ أنه باستخدام بناء جملة map() أعلاه ، فإن func في هذه الحالة هو str.upper و iterables هي قائمة my_pets – واحدة فقط قابلة للتكرار. لاحظ أيضًا أننا لم نستدعي الدالة str.upper (القيام بذلك: str.upper ()) ، لأن وظيفة map تقوم بذلك لنا في كل عنصر في قائمة my_pets.

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

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

تباركنا Python بالفعل بالوظيفة المضمنة round () التي تأخذ حجتين – الرقم المطلوب تقريبه وعدد المنازل العشرية لتقريب الرقم حتى. لذا ، نظرًا لأن الوظيفة تتطلب وسيطتين ، نحتاج إلى تمرير متكررين.

# بيثون 3

circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]

result = list(map(round, circle_areas, range(1,7)))

print(result)

سنحصل على النتيجة التالية عند التشغيل

[3.6, 5.58, 4.009, 56.2424, 9.01344, 32.00013]

شاهد جمال map ()؟ هل يمكنك تخيل المرونة التي يثيرها هذا؟

تعمل دالة النطاق (1،7) كوسيطة ثانية للدالة المستديرة (عدد المنازل العشرية المطلوبة لكل تكرار). لذلك ، عندما تتكرر map من خلال دائرة الدائرة ، أثناء التكرار الأول ، يتم تمرير العنصر الأول من دائرة الدائرة ، 3.56773 مع العنصر الأول للنطاق (1،7) ، 1 إلى تقريب ، مما يجعلها تصبح فعالة تقريبًا (3.56773 ، 1). أثناء التكرار الثاني ، يتم تمرير العنصر الثاني للدائرة ، 5.57668 جنبًا إلى جنب مع العنصر الثاني للنطاق (1.7) ، 2 للدور مما يجعله يترجم إلى الجولة (5.57668 ، 2). يحدث هذا حتى يتم الوصول إلى نهاية قائمة المناطق.

أنا متأكد من أنك تتساءل: “ماذا لو مررت في تكرار أقل من أو أكثر من طول الأول القابل للتكرار؟ أي ماذا إذا مررت النطاق (1،3) أو النطاق (1 ، 9999) الثانية قابلة للتكرار في الوظيفة أعلاه “. والجواب بسيط: لا شيء! حسنًا ، هذا ليس صحيحًا. يحدث “لا شيء” بمعنى أن الدالة map () لن تثير أي استثناء ، بل ستقوم ببساطة بتكرارها فوق العناصر حتى لا تتمكن من العثور على وسيطة ثانية للدالة ، وعندها تتوقف ببساطة وتعرض النتيجة.

لذا ، على سبيل المثال ، إذا قمت بتقييم النتيجة = قائمة (map (مستديرة ، دائرةمنطقة ، نطاق (1،3))) ، فلن تحصل على أي خطأ حتى عند طول دائرةطول المناطق وطول النطاق (1،3) اختلف. بدلاً من ذلك ، هذا ما تفعله Python: فهو يأخذ العنصر الأول من الدائرةالعربية والعنصر الأول من النطاق (1،3) ويمررها إلى تقريب. تقييم الجولة ثم يحفظ النتيجة. ثم ينتقل بعد ذلك إلى التكرار الثاني ، العنصر الثاني من الدائرة العربية والعنصر الثاني من النطاق (1،3) ، يحفظه مرة أخرى. الآن ، في التكرار الثالث (دائرةمنطقة لها عنصر ثالث) ، تأخذ بايثون العنصر الثالث من دائرةمن المناطق ثم تحاول أخذ العنصر الثالث من النطاق (١.٣) ولكن نظرًا لأن النطاق (١.٣) لا يحتوي على عنصر ثالث ، تقوم بايثون ببساطة بإيقاف النتيجة وإرجاعها ، والتي في هذه الحالة ستكون ببساطة [3.6 ، 5.58].

المضي قدما ، جربه.

# بيثون 3

circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]

result = list(map(round, circle_areas, range(1,3)))

print(result)

سنحصل على النتيجة التالية عند التشغيل

[3.6, 5.58]

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

لتعزيز معرفتنا بوظيفة map () ، سنستخدمها لتنفيذ وظيفة zip () المخصصة الخاصة بنا. الدالة zip () هي وظيفة تأخذ عددًا من العناصر القابلة للتكرار ثم تنشئ مجموعة تحتوي على كل عنصر من العناصر الموجودة فيه. مثل map () ، في Python 3 ، تقوم بإرجاع كائن مولد ، والذي يمكن تحويله بسهولة إلى قائمة عن طريق استدعاء وظيفة القائمة المضمنة عليه. استخدم جلسة المترجم أدناه للحصول على قبضة zip () قبل أن ننشئ جلستنا بmap()

# بيثون 3

my_strings = ['a', 'b', 'c', 'd', 'e']
my_numbers = [1,2,3,4,5]

results = list(zip(my_strings, my_numbers))

print(results)

سنحصل على النتيجة التالية عند التشغيل

[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]

كمكافأة ، هل يمكنك تخمين ما سيحدث في الجلسة المذكورة أعلاه إذا لم تكن سلسلة my_strings و my_numbers بنفس الطول؟ لا؟ جربها! قم بتغيير طول واحد منهم.

في وظيفة zip () المخصصة الخاصة بنا!

# بيثون 3

my_strings = ['a', 'b', 'c', 'd', 'e']
my_numbers = [1,2,3,4,5]

results = list(map(lambda x, y: (x, y), my_strings, my_numbers))

print(results)

سنحصل على النتيجة التالية عند التشغيل

[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]

مجرد إلقاء نظرة على ذلك! لدينا نفس النتيجة مثل الرمز البريدي.

هل لاحظت أيضًا أنني لست بحاجة إلى إنشاء وظيفة باستخدام طريقة def my_function () القياسية؟ هذا هو مدى مرونة map () و Python بشكل عام! أنا ببساطة استخدم وظيفة لامدا. هذا لا يعني أن استخدام طريقة تعريف الوظيفة القياسية (لـ def function_name ()) غير مسموح به ، فهو لا يزال كذلك. لقد فضلت ببساطة كتابة رمز أقل (تكون “Pythonic”).

هذا كل شيء عن map. على عامل Filter ()

Filter

بينما تقوم map () بتمرير كل عنصر في العنصر القابل للتكرار من خلال دالة وإرجاع نتيجة جميع العناصر التي اجتازت الدالة ، فإن المرشح () ، أولاً وقبل كل شيء ، يتطلب من الدالة إرجاع القيم المنطقية (صواب أو خطأ) ثم تمرير كل عنصر عنصر في التكرار من خلال الوظيفة ، “Filter” بعيدا عن تلك التي كاذبة. يحتوي على بناء الجملة التالي:

مرشح (func ، iterable)

تجدر الإشارة إلى النقاط التالية فيما يتعلق بالفلتر ():

على عكس map () ، يلزم تكرار واحد فقط.
الوسيطة func مطلوبة لإرجاع نوع منطقي. إذا لم يحدث ذلك ، فسيقوم المرشح ببساطة بإرجاع التكرار الذي تم تمريره إليه. أيضًا ، نظرًا لأن هناك حاجة إلى تكرار واحد فقط ، فمن الواضح أن func يجب أن يأخذ وسيطة واحدة فقط.
يمرر المرشح كل عنصر في العنصر القابل للتكرار من خلال func ويعيد فقط العناصر التي يتم تقييمها إلى true. أعني ، هناك حق في الاسم – “مرشح”.
دعنا نرى بعض الأمثلة

فيما يلي قائمة (قابلة للتكرار) لعشرات الطلاب في امتحان الكيمياء. دعونا تصفية أولئك الذين اجتازوا عشرات أكثر من 75 … باستخدام عامل Filter.

# بيثون 3
scores = [66, 90, 68, 59, 76, 60, 88, 74, 81, 65]

def is_A_student(score):
    return score > 75

over_75 = list(filter(is_A_student, scores))

print(over_75)

سنحصل على النتيجة التالية عند التشغيل

[90, 76, 88, 81]

المثال التالي سيكون كاشف متلازمة. “palindrome” هي كلمة أو عبارة أو تسلسل يقرأ نفس الوراء كما في الأمام. لنقم بFilter الكلمات التي تكون متناظرة من مجموعة (متكررة) من المتناظرة المشتبه بها.

# بيثون 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")

palindromes = list(filter(lambda word: word == word[::-1], dromes))

print(palindromes)

سنحصل على النتيجة التالية عند التشغيل

['madam', 'anutforajaroftuna']

التي يجب أن تنتج [“madam” ، “anutforajaroftuna”].

أنيق جدا هاه؟ أخيرًا ، قلل ()

Reduce

يطبق Reduce وظيفة وسيطتين بشكل تراكمي على عناصر عنصر قابل للتكرار ، بدءًا من وسيطة مبدئيًا. يحتوي على بناء الجملة التالي:

Reduce (func ، iterable [، الأولي])

حيث func هي الوظيفة التي يتم تطبيق كل عنصر في التكرار عليها تراكميًا ، والقيمة الأولية هي القيمة الاختيارية التي يتم وضعها قبل عناصر التكرار في الحساب ، وتعمل كإعداد افتراضي عندما يكون التكرار فارغًا. يجب ملاحظة ما يلي حول Reduce (): 1. يتطلب func حجتين ، الأولى هي العنصر الأول في التكرار (إذا لم يتم توفير الأولي) والثاني العنصر الثاني في التكرار. إذا تم توفير الأولية ، فإنها تصبح الحجة الأولى ل func والعنصر الأول في التكرار يصبح العنصر الثاني. 2. Reduce “يقلل” (أعلم ، اغفر لي) إلى قيمة واحدة.

كالعادة ، دعنا نرى بعض الأمثلة.

لنقم بإنشاء نسختنا الخاصة من دالة sum () sum () المضمنة في Python. تُرجع الدالة sum () مجموع كافة العناصر الموجودة في العنصر القابل للتكرار الذي تم تمريرها إليها

# بيثون 3
from functools import reduce

numbers = [3, 4, 6, 9, 34, 12]

def custom_sum(first, second):
    return first + second

result = reduce(custom_sum, numbers)
print(result)

سنحصل على النتيجة التالية عند التشغيل

68

النتيجة ، كما تتوقع هي 68.

اذا ماذا حصل؟

كالعادة ، كل شيء يتعلق بالتكرار: Reduce يأخذ العناصر الأولى والثانية في الأرقام ويمررها إلى custom_sum على التوالي. يحسب custom_sum مجموعها ويعيدها لتقليلها. Reduce ثم يأخذ هذه النتيجة ويطبقها كعنصر أول على custom_sum ويأخذ العنصر التالي (الثالث) في الأرقام كعنصر ثانٍ إلى custom_sum. يفعل ذلك بشكل مستمر (بشكل تراكمي) حتى يتم استنفاد الأرقام.

دعونا نرى ما يحدث عندما أستخدم القيمة الأولية الاختيارية.

# بيثون 3
from functools import reduce

numbers = [3, 4, 6, 9, 34, 12]

def custom_sum(first, second):
    return first + second

result = reduce(custom_sum, numbers, 10)
print(result)

سنحصل على النتيجة التالية عند التشغيل

78

والنتيجة ، كما هو متوقع ، هي 78 لأن الاختزال ، مبدئيًا ، يستخدم 10 كوسيطة أولى للعرف_المخصص.

هذا كل شيء عن map Python ، والحد منها ، وFilter. جرب التمارين أدناه للمساعدة في التأكد من فهمك لكل وظيفة.

تمرين

في هذا التمرين ، ستستخدم كل map وFilter وتصغير لإصلاح الرمز المكسور.

from functools import reduce 

# استخدم map لطباعة مربع كل أرقام مقربة
# إلى منزلين عشريين
my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59]

# استخدم عامل Filterلطباعة الأسماء الأقل من فقط
# أو يساوي سبعة أحرف
my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"]

# Use reduce to print the product of these numbers
my_numbers = [4, 6, 9, 23, 5]

# إصلاح الثلاثة على التوالي.
map_result = list(map(lambda x: x, my_floats))
filter_result = list(filter(lambda name: name, my_names, my_names))
reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers, 0)

print(map_result)
print(filter_result)
print(reduce_result)

الحل

#### map
from functools import reduce 

my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59]
my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"]
my_numbers = [4, 6, 9, 23, 5]

map_result = list(map(lambda x: round(x ** 2, 3), my_floats))
filter_result = list(filter(lambda name: len(name) <= 7, my_names))
reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers)

print(map_result)
print(filter_result)
print(reduce_result)

سنحصل على النتيجة التالية عند التشغيل

<script.py> output:
    [18.922, 37.088, 10.562, 95.453, 4.666, 78.854, 21.068]
    ['olumide', 'josiah', 'omoseun']
    24840





Leave a Reply