الخريطة (دالة من الرتبة العليا)
في العديد من لغات البرمجة ، تُعتبر دالة map دالة من الرتبة العليا تُطبّق دالة مُحددة على كل عنصر من عناصر مجموعة ، مثل قائمة أو مجموعة ، وتُعيد النتائج في مجموعة من نفس النوع. وغالبًا ما تُسمى دالة تُطبّق على الكل عند النظر إليها في سياق البرمجة الوظيفية .
إن مفهوم الخريطة لا يقتصر على القوائم: فهو يعمل مع الحاويات المتسلسلة ، والحاويات الشبيهة بالأشجار، أو حتى الحاويات المجردة مثل المستقبلات والوعود .
أمثلة: رسم خريطة لقائمة
لنفترض أن لدينا قائمة من الأعداد الصحيحة [1, 2, 3, 4, 5]. لحساب مربع كل عدد صحيح، يجب أولاً تعريف دالة squareلعدد واحد (كما هو موضح هنا بلغة هاسكل ):
مربع س = س × سبعد ذلك، اتصل بـ:
>>> map square [ 1 , 2 , 3 , 4 , 5 ]مما ينتج عنه [1, 4, 9, 16, 25]، مما يدل على أن العملية mapقد مرت على القائمة بأكملها وطبقت الدالة squareعلى كل عنصر.
مثال مرئي
فيما يلي عرض لكل خطوة من خطوات عملية الربط لقائمة من الأعداد الصحيحة X = [0, 5, 8, 3, 2, 1]التي يتم ربطها بقائمة جديدة X'وفقًا للدالة :

يتم توفير هذا mapكجزء من مكتبة Haskell الأساسية (أي "المكتبة القياسية") ويتم تنفيذه على النحو التالي:
map :: ( a -> b ) -> [ a ] -> [ b ] map _ [] = [] map f ( x : xs ) = f x : map f xsتعميم
في لغة هاسكل، يتم تعميم الدالة متعددة الأشكال إلى دالة متعددة الأنماط ، والتي تنطبق على أي نوع ينتمي إلى فئة النوع .map::(a->b)->[a]->[b]fmap::Functorf=>(a->b)->fa->fbFunctor
يمكن تعريف مُنشئ النوع للقوائم على []أنه نسخة من Functorفئة النوع باستخدام mapالدالة من المثال السابق:
مثال Functor [] حيث fmap = mapومن الأمثلة الأخرى على Functorالحالات الأشجار:
-- بيانات شجرة ثنائية بسيطة: Tree a = Leaf a | Fork ( Tree a ) ( Tree a )مثال شجرة الدوال حيث fmap f ( Leaf x ) = Leaf ( f x ) fmap f ( Fork l r ) = Fork ( fmap f l ) ( fmap f r )ينتج عن تطبيق الخريطة على شجرة ما يلي:
>>> fmap square ( Fork ( Fork ( Leaf 1 ) ( Leaf 2 )) ( Fork ( Leaf 3 ) ( Leaf 4 ))) Fork ( Fork ( Leaf 1 ) ( Leaf 4 )) ( Fork ( Leaf 9 ) ( Leaf 16 ))لكل نسخة من Functorفئة النوع، fmapيكون ملزماً تعاقدياً باتباع قوانين الدالة:
fmap id ≡ id -- قانون الهوية fmap ( f . g ) ≡ fmap f . fmap g -- قانون التركيبحيث .يشير إلى تركيب الدوال في لغة هاسكل.
من بين استخدامات أخرى، يسمح هذا بتحديد العمليات على مستوى العناصر لأنواع مختلفة من المجموعات .
الخلفية النظرية للفئات
في نظرية الفئات ، الدالةيتكون من خريطتين: الأولى تُرسل كل كائن A من الفئة إلى كائن آخر FA ، والثانية تُرسل كل تشاكلإلى شكل آخر، والذي يعمل كتشاكل على الفئات (أي أنه يحترم بديهيات الفئة). بتفسير عالم أنواع البيانات كفئة Type ، حيث تكون التشاكلات دوالًا، فإن مُنشئ النوع Fالذي ينتمي إلى Functorفئة النوع هو الجزء الكائني من هذا المؤثر، و هو الجزء التشاكلي. قوانين المؤثر المذكورة أعلاه هي تحديدًا بديهيات المؤثر في نظرية الفئات لهذا المؤثر.fmap::(a->b)->Fa->Fb
يمكن أن تكون الدوال أيضًا كائنات في فئات، مع "تشاكلات" تسمى التحويلات الطبيعية . بالنظر إلى دالتين، تحول طبيعييتكون من مجموعة من التشكلات، واحد لكل عنصر A من الفئة D ، وهي "طبيعية" بمعنى أنها تعمل كـ"تحويل" بين الدالتين، دون الأخذ في الاعتبار العناصر التي تُطبق عليها الدالتان. تتوافق التحويلات الطبيعية مع الدوال من الشكل ، حيث يمثل متغير نوع مُكمّم عالميًا - لا يعرف شيئًا عن النوع الذي ينتمي إليه . يتم استيفاء بديهية الطبيعية لهذه الدوال تلقائيًا لأنها تُعرف باسم نظرية الحرية، اعتمادًا على كونها متعددة الأشكال بارامتريًا . [ 1 ] على سبيل المثال، ، الذي يعكس قائمة، هو تحويل طبيعي، وكذلك ، الذي يُسطّح شجرة من اليسار إلى اليمين، وحتى ، الذي يُرتب قائمة بناءً على دالة مقارنة مُعطاة.eta::Fa->Gaaetaareverse::Lista->ListaflattenInorder::Treea->ListasortBy::(a->a->Bool)->Lista->Lista
التحسينات
يُتيح الأساس الرياضي للخرائط إجراء عدد من التحسينات . ويضمن قانون التركيب أن كليهما
(map f . map g) listوmap (f . g) list
يؤدي إلى نفس النتيجة؛ أي،مع ذلك، فإن الصيغة الثانية أكثر كفاءة في الحساب من الصيغة الأولى، لأن كلتيهما mapتتطلبان إعادة بناء قائمة كاملة من الصفر. لذلك، ستحاول المترجمات تحويل الصيغة الأولى إلى الثانية؛ يُعرف هذا النوع من التحسين باسم دمج الخرائط ، وهو النظير الوظيفي لدمج الحلقات . [ 2 ]
يمكن تعريف دوال الخرائط، وغالبًا ما يتم تعريفها، من حيث الطي مثل foldr، مما يعني أنه يمكن للمرء إجراء دمج بين الخريطة والطي : foldr f z . map gوهو ما يعادل foldr (f . g) z.
إنّ تطبيق دالة map المذكورة أعلاه على القوائم المرتبطة أحادية الاتجاه ليس تكراريًا ذيليًا ، لذا قد يؤدي إلى تراكم عدد كبير من الإطارات على مكدس الاستدعاءات عند استدعائها مع قائمة كبيرة. توفر العديد من لغات البرمجة بديلًا لذلك دالة "reverse map"، وهي مكافئة لعكس قائمة map، ولكنها تكرارية ذيلية. إليك تطبيقًا يستخدم دالة fold -left.
reverseMap f = foldl ( \ ys x -> f x : ys ) []بما أن عكس قائمة مرتبطة بشكل فردي هو أيضًا عملية تكرارية ذيلية، يمكن دمج reverse و reverse-map لتنفيذ الخريطة العادية بطريقة تكرارية ذيلية، على الرغم من أنها تتطلب إجراء عمليتي مرور على القائمة.
مقارنة اللغات
نشأت دالة الخريطة في لغات البرمجة الوظيفية .
قدمت لغة Lisp دالة map تسمى maplist[ 3 ] في عام 1959، مع ظهور إصدارات مختلفة قليلاً في عام 1958. [ 4 ] هذا هو التعريف الأصلي لـ maplist، حيث تقوم هذه الدالة برسم دالة على قوائم الباقي المتتالية:
maplist[x;f] = [null[x] -> NIL;T -> cons[f[x];maplist[cdr[x];f]]]
maplistلا تزال هذه الوظيفة متاحة في لغات Lisp الأحدث مثل Common Lisp ، [ 5 ] على الرغم من أن الوظائف مثل mapcarأو الأكثر عمومية mapستكون مفضلة.
maplistيمكن كتابة عملية تربيع عناصر القائمة باستخدام صيغة S-expression على النحو التالي:
( maplist ( lambda ( l ) ( sqr ( car l ))) ' ( 1 2 3 4 5 ))باستخدام الدالة mapcar، سيُكتب المثال أعلاه على النحو التالي:
( mapcar ( function sqr ) ' ( 1 2 3 4 5 ))اليوم، تُدعم دوال الربط (أو يُمكن تعريفها) في العديد من لغات البرمجة الإجرائية ، والبرمجة كائنية التوجه ، واللغات متعددة الأنماط : في مكتبة C++ القياسية ، تُسمى `map` أو ` map()` ، وفي مكتبة LINQ الخاصة بلغة C# (3.0)، تُقدم كدالة إضافية تُسمى `map()` . تُعد دالة `map` أيضًا عملية شائعة الاستخدام في لغات البرمجة عالية المستوى مثل ColdFusion Markup Language (CFML)، وPerl ، و Python ، و Ruby ؛ وتُسمى هذه العملية `map()` في جميع هذه اللغات الأربع. كما يُوفر Ruby (من Smalltalk ) اسمًا بديلًا لـ `map( )`. تُوفر Common Lisp مجموعة من الدوال الشبيهة بدالة `map()`؛ وتُسمى الدالة التي تُطابق السلوك الموصوف هنا `map()` ( مما يُشير إلى الوصول باستخدام عملية CAR ). توجد أيضًا لغات ذات بنى نحوية تُوفر نفس وظائف دالة `map()`.std::transformstd::ranges::transformSelectmapcollectmapmapcar-car
تُعمم دالة map أحيانًا لقبول الدوال الثنائية (ذات وسيطين) التي تُطبق دالة يُحددها المستخدم على العناصر المتناظرة في قائمتين. تستخدم بعض اللغات أسماءً خاصة لهذا الغرض، مثل map2 أو zipWith . قد تحتوي اللغات التي تستخدم دوالًا متعددة الوسائط صريحة على إصدارات من map ذات عدد وسائط متغير لدعم هذه الدوال . تواجه دالة map مع قائمتين أو أكثر مشكلة التعامل مع القوائم ذات الأطوال المختلفة. تختلف اللغات في هذا الشأن؛ فبعضها يُصدر استثناءً، وبعضها يتوقف بعد طول أقصر قائمة ويتجاهل العناصر الزائدة في القوائم الأخرى، بينما يستمر بعضها حتى طول أطول قائمة، وبالنسبة للقوائم التي انتهت بالفعل، يُمرر قيمة افتراضية إلى الدالة تُشير إلى عدم وجود قيمة.
في اللغات التي تدعم الدوال من الدرجة الأولى والتقسيم الجزئي ، mapيمكن تطبيقها جزئيًا لرفع دالة تعمل على قيمة واحدة فقط إلى مكافئ عنصري يعمل على حاوية كاملة؛ على سبيل المثال، map squareهي دالة Haskell تقوم بتربيع كل عنصر من عناصر القائمة.
| لغة | رسم خريطة | قوائم الخريطة 2 | خريطة قوائم | ملحوظات | التعامل مع قوائم ذات أطوال مختلفة |
|---|---|---|---|---|---|
| APL | funclist | list1funclist2 | func/ list1list2list3list4 | إن قدرات معالجة المصفوفات في لغة APL تجعل عمليات مثل map ضمنية | خطأ في الطول إذا كانت أطوال القوائم غير متساوية أو 1 |
| لغة الشفرة الشائعة | (mapcar funclist) | (mapcar funclist1list2) | (mapcar funclist1list2 ...) | يتوقف بعد طول أقصر قائمة | |
| لغة سي++ | std::transform(list| std::views::transform( | std::transform(std::views::zip_transform( | std::views::zip_transform( | في رأس <algorithm>، تُعتبر begin و end و result مُكرِّرات، وتُكتب result بدءًا من result. | |
| سي شارب | ienum.Select(func)أو البندselect | ienum1.Zip(ienum2, func) | Selectهي دالة امتداد، و ienum هو نوع من IEnumerable، Zipوقد تم تقديمها في .NET 4.0. وبالمثل في جميع لغات .NET | يتوقف بعد انتهاء أقصر قائمة | |
| CFML | obj.map(func) | أين objهي مصفوفة أو بنية. funcتستقبل كمعاملات قيمة كل عنصر، وفهرسه أو مفتاحه، ومرجعًا إلى الكائن الأصلي. | |||
| كلوجر | (map funclist) | (map funclist1list2) | (map funclist1list2 ...) | يتوقف بعد انتهاء أقصر قائمة | |
| د | list.map!func | zip(list1, list2).map!func | zip(list1, list2, ...).map!func | يتم تحديد عملية الضغط بواسطة سياسة التوقف: الأقصر، أو الأطول، أو يتطلب نفس الطول | |
| إرلانغ | lists:map(Fun, List) | lists:zipwith(Fun, List1, List2) | zipwith3متوفر أيضًا | يجب أن تكون القوائم متساوية في الطول | |
| إكسير | Enum.map(list, fun) | Enum.zip(list1, list2) |> Enum.map(fun) | List.zip([list1, list2, ...]) |> Enum.map(fun) | يتوقف بعد انتهاء أقصر قائمة | |
| فا# | List.map funclist | List.map2 funclist1list2 | توجد دوال لأنواع أخرى ( Seq و Array ) | يطرح استثناءً | |
| بريق | list.map(list, func)yielder.map(yielder, func) | list.map2(list1, list2, func)yielder.map2(yielder1, yielder2, func) | يحذف العناصر الإضافية من القائمة الأطول | ||
| رائع | list.collect(func) | [list1list2].transpose().collect(func) | [list1list2...].transpose().collect(func) | ||
| هاسكل | map funclist | zipWith funclist1list2 | zipWithnfunclist1list2 ... | nيتوافق مع عدد القوائم؛ محدد مسبقًا حتىzipWith7 | يتوقف بعد انتهاء أقصر قائمة |
| هاكس | array.map(func)list.map(func)Lambda.map(iterable, func) | ||||
| ج | funclist | list1funclist2 | func/ list1, list2, list3 ,: list4 | تُتيح قدرات معالجة المصفوفات في لغة J إجراء عمليات مثل map بشكل ضمني. | خطأ في الطول إذا لم تتساوى أطوال القوائم |
| جافا 8+ | stream.map(func) | ||||
| جافا سكريبت 1.6، إي سي إم إيه سكريبت 5 | array#map(func) | List1 | List1 | تُمرر الدالة Array#map ثلاثة وسائط إلى الدالة func : العنصر، وفهرس العنصر، والمصفوفة. ويمكن حذف الوسائط غير المستخدمة. | يتوقف عند نهاية القائمة List1 ، ويقوم بتوسيع المصفوفات الأقصر بعناصر غير محددة إذا لزم الأمر. |
| جوليا | map(func, list) | map(func, list1, list2) | map(func, list1, list2, ..., listN) | خطأ: عدم تطابق الأبعاد | |
| لغة الاتصال | map(Closure, List) | map(Closure, List1, List2) | map(Closure, List1, List2, List3, ...) (up to seven lists) | يجب إنشاء مثيل لوسيط الإغلاق فقط . | فشل |
| ماثيماتيكا | func /@ list Map[func, list] | MapThread[func, {list1, list2}] | MapThread[func, {list1, list2, ...}] | يجب أن تكون القوائم متساوية في الطول | |
| ماكسيما | map(f, expr1, ..., exprn)maplist(f, expr1, ..., exprn) | تُرجع الدالة map تعبيرًا يكون عامل التشغيل الرئيسي فيه هو نفسه عامل التشغيل في التعبيرات الأخرى؛ بينما تُرجع الدالة maplist قائمةً. | |||
| أوكاميل | List.map funclist Array.map funcarray | List.map2 funclist1list2 | يُثير استثناء Invalid_argument | ||
| طبيب عام/طبيب عام | apply(func, list) | غير متوفر | |||
| بيرل | map blocklist map expr, list | في الكتلة أو التعبير، يحتوي المتغير الخاص $_ على كل قيمة من القائمة بالتناوب. | يقوم المساعد List::MoreUtils::each_arrayبدمج أكثر من قائمة حتى يتم استنفاد أطولها، ثم يملأ القوائم الأخرى بـundef. | ||
| PHP | array_map(callable, array) | array_map(callable, array1,array2) | array_map(callable, array1,array2, ...) | يجب أن يتطابق عدد المعاملات للدالة القابلة للاستدعاء مع عدد المصفوفات. | يُضيف عناصر فارغة إلى القوائم الأقصر |
| مقدمة | maplist(Cont, List1, List2). | maplist(Cont, List1, List2, List3). | maplist(Cont, List1, ...). | تُعتبر وسائط القائمة مدخلات أو مخرجات أو كليهما. ويشمل ذلك أيضًا zipWith و unzip و all | فشل صامت (ليس خطأً) |
| بايثون | map(func, list) | map(func, list1, list2) | map(func, list1, list2, ...) | تُرجع قائمة في بايثون 2 ومكرر في بايثون 3. | zip()وتتوقف map()(3.x) بعد انتهاء أقصر قائمة، بينما تقوم map()(2.x) و itertools.zip_longest()(3.x) بتوسيع القوائم الأقصر بعناصر None. |
| روبي | enum.collect {block} enum.map {block} | enum1.zip(enum2) | enum1.zip(enum2, ...) | enum is an Enumeration | تتوقف الدالة عند نهاية الكائن الذي تم استدعاؤها عليه (القائمة الأولى)؛ إذا كانت أي قائمة أخرى أقصر، فسيتم توسيعها بعناصر فارغة . |
| الصدأ | list1.into_iter().map(func) | list1.into_iter().zip(list2).map(func) | Iterator::mapتستحوذ كلتا الطريقتين Iterator::zipعلى المُكرِّر الأصلي وتُعيدان مُكرِّرًا جديدًا؛ Iterator::zipوتستدعي الطريقة داخليًا IntoIterator::into_iterالطريقة علىlist2 | يتوقف بعد انتهاء القائمة الأقصر | |
| S - R | lapply(list, func) | mapply(func, list1, list2) | mapply(func, list1, list2, ...) | يتم تدوير القوائم الأقصر | |
| سكالا | list.map(func) | (list1, list2) | (list1, list2, list3) | ملاحظة: لا يمكن أن يزيد العدد عن 3. | يتوقف بعد انتهاء القائمة الأقصر |
| مخطط (بما في ذلك الخداع والمكر ) | (map funclist) | (map funclist1list2) | (map funclist1list2 ...) | يجب أن تكون جميع القوائم بنفس الطول (يمتد معيار SRFI-1 ليشمل قوائم ذات أطوال مختلفة) | |
| أحاديث قصيرة | aCollection collect: aBlock | aCollection1 with: aCollection2 collect: aBlock | فشل | ||
| لغة الآلة القياسية | map funclist | ListPair.map func (list1, list2) ListPair.mapEq func (list1, list2) | بالنسبة للدالة map ذات الوسيطين، تأخذ الدالة func وسيطيها في شكل مجموعة. | ListPair.mapيتوقف بعد انتهاء أقصر قائمة، بينما ListPair.mapEqيثير UnequalLengthsاستثناءً | |
| سويفت | sequence.map(func) | zip(sequence1, sequence2).map(func) | يتوقف بعد انتهاء أقصر قائمة | ||
| XPath 3 XQuery 3 | list!blockfor-each(list,func) | for-each-pair(list1,list2,func) | في blockهذا السياق، .يحتوي العنصر على القيمة الحالية | يتوقف بعد انتهاء أقصر قائمة |
انظر أيضاً
مراجع
- ↑ في لغة غير صارمة تسمح بالاستدعاء الذاتي العام، مثل هاسكل، لا يصح هذا إلا إذا كان الوسيط الأول
fmapصارمًا. وادلر، فيليب (سبتمبر 1989). نظريات مجانية! (ملف PDF) . المؤتمر الدولي الرابع حول لغات البرمجة الوظيفية وهندسة الحاسوب. لندن: رابطة آلات الحوسبة . - ↑ "دمج الخرائط: جعل لغة هاسكل أسرع بنسبة 225%"
- ^ ج. مكارثي، ك. مالينج، س. راسل، ن. روتشستر، س. غولدبرغ، ج. سلاجل. دليل مبرمج LISP. مارس-أبريل 1959
- ↑ ج. مكارثي: لغة التلاعب بالرموز - مراجعات اللغة. مذكرة الذكاء الاصطناعي رقم 4، أكتوبر 1958
- ↑ الدوال MAPC، MAPCAR، MAPCAN، MAPL، MAPLIST، MAPCON في لغة ANSI Common Lisp
- الدوال ذات الرتبة العليا
- مقارنات لغات البرمجة
- التكرار في البرمجة
