الخريطة (دالة من الرتبة العليا)

    في العديد من لغات البرمجة ، تُعتبر دالة 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'وفقًا للدالةو(x)=x+1{\displaystyle f(x)=x+1} :

    تطبيق خطوات معالجة وظيفة الخريطة
    عرض خطوات المعالجة عند تطبيق دالة الخريطة على قائمة

    يتم توفير هذا 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 -- قانون التركيب

    حيث .يشير إلى تركيب الدوال في لغة هاسكل.

    من بين استخدامات أخرى، يسمح هذا بتحديد العمليات على مستوى العناصر لأنواع مختلفة من المجموعات .

    الخلفية النظرية للفئات

    في نظرية الفئات ، الدالةF:جد{\displaystyle F:C\rightarrow D}يتكون من خريطتين: الأولى تُرسل كل كائن A من الفئة إلى كائن آخر FA ، والثانية تُرسل كل تشاكلو:أب{\displaystyle f:A\rightarrow B}إلى شكل آخرFو:FأFب{\displaystyle Ff:FA\rightarrow FB}، والذي يعمل كتشاكل على الفئات (أي أنه يحترم بديهيات الفئة). بتفسير عالم أنواع البيانات كفئة Type ، حيث تكون التشاكلات دوالًا، فإن مُنشئ النوع Fالذي ينتمي إلى Functorفئة النوع هو الجزء الكائني من هذا المؤثر، و هو الجزء التشاكلي. قوانين المؤثر المذكورة أعلاه هي تحديدًا بديهيات المؤثر في نظرية الفئات لهذا المؤثر.fmap::(a->b)->Fa->Fb

    يمكن أن تكون الدوال أيضًا كائنات في فئات، مع "تشاكلات" تسمى التحويلات الطبيعية . بالنظر إلى دالتينF،جي:جد{\displaystyle F,G:C\rightarrow D}، تحول طبيعيη:Fجي{\displaystyle \eta :F\rightarrow G}يتكون من مجموعة من التشكلاتηأ:Fأجيأ{\displaystyle \eta _{A}:FA\rightarrow GA}، واحد لكل عنصر A من الفئة D ، وهي "طبيعية" بمعنى أنها تعمل كـ"تحويل" بين الدالتين، دون الأخذ في الاعتبار العناصر التي تُطبق عليها الدالتان. تتوافق التحويلات الطبيعية مع الدوال من الشكل ، حيث يمثل متغير نوع مُكمّم عالميًا - لا يعرف شيئًا عن النوع الذي ينتمي إليه . يتم استيفاء بديهية الطبيعية لهذه الدوال تلقائيًا لأنها تُعرف باسم نظرية الحرية، اعتمادًا على كونها متعددة الأشكال بارامتريًا . [ 1 ] على سبيل المثال، ، الذي يعكس قائمة، هو تحويل طبيعي، وكذلك ، الذي يُسطّح شجرة من اليسار إلى اليمين، وحتى ، الذي يُرتب قائمة بناءً على دالة مقارنة مُعطاة.eta::Fa->Gaaetaareverse::Lista->ListaflattenInorder::Treea->ListasortBy::(a->a->Bool)->Lista->Lista

    التحسينات

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

    • (map f . map g) listو
    • map (f . g) list

    يؤدي إلى نفس النتيجة؛ أي،رسم خريطة(و)رسم خريطة(ز)=رسم خريطة(وز){\displaystyle \operatorname {map} (f)\circ \operatorname {map} (g)=\operatorname {map} (f\circ g)}مع ذلك، فإن الصيغة الثانية أكثر كفاءة في الحساب من الصيغة الأولى، لأن كلتيهما 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خريطة قوائمملحوظاتالتعامل مع قوائم ذات أطوال مختلفة
    APLfunclistlist1funclist2func/ list1list2list3list4إن قدرات معالجة المصفوفات في لغة APL تجعل عمليات مثل map ضمنيةخطأ في الطول إذا كانت أطوال القوائم غير متساوية أو 1
    لغة الشفرة الشائعة(mapcar funclist)(mapcar funclist1list2)(mapcar funclist1list2 ...)يتوقف بعد طول أقصر قائمة
    لغة سي++std::transform(begin, end, result, func)list| std::views::transform(result, func)std::transform(begin1, end1, begin2, result, func)std::views::zip_transform(func, list1, list2)std::views::zip_transform(func, list1, list2 ...)في رأس <algorithm>، تُعتبر begin و end و result مُكرِّرات، وتُكتب result بدءًا من result.
    سي شاربienum.Select(func)أو البندselectienum1.Zip(ienum2, func)Selectهي دالة امتداد، و ienum هو نوع من IEnumerable، Zipوقد تم تقديمها في .NET 4.0. وبالمثل في جميع لغات .NETيتوقف بعد انتهاء أقصر قائمة
    CFMLobj.map(func)أين objهي مصفوفة أو بنية. funcتستقبل كمعاملات قيمة كل عنصر، وفهرسه أو مفتاحه، ومرجعًا إلى الكائن الأصلي.
    كلوجر(map funclist)(map funclist1list2)(map funclist1list2 ...)يتوقف بعد انتهاء أقصر قائمة
    دlist.map!funczip(list1, list2).map!funczip(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 funclistList.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 funclistzipWith funclist1list2zipWithnfunclist1list2 ...nيتوافق مع عدد القوائم؛ محدد مسبقًا حتىzipWith7يتوقف بعد انتهاء أقصر قائمة
    هاكسarray.map(func)list.map(func)Lambda.map(iterable, func)
    جfunclistlist1funclist2func/ list1, list2, list3 ,: list4تُتيح قدرات معالجة المصفوفات في لغة J إجراء عمليات مثل map بشكل ضمني.خطأ في الطول إذا لم تتساوى أطوال القوائم
    جافا 8+stream.map(func)
    جافا سكريبت 1.6، إي سي إم إيه سكريبت 5array#map(func)List1.map(function(elem1,i){returnfunc(elem1, List2[i]); })List1.map(function(elem1,i){returnfunc(elem1, List2[i], List3[i], ...); })تُمرر الدالة 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 funcarrayList.map2 funclist1list2يُثير استثناء Invalid_argument
    طبيب عام/طبيب عامapply(func, list)غير متوفر
    بيرلmap blocklist map expr, listفي الكتلة أو التعبير، يحتوي المتغير الخاص $_ على كل قيمة من القائمة بالتناوب.يقوم المساعد List::MoreUtils::each_arrayبدمج أكثر من قائمة حتى يتم استنفاد أطولها، ثم يملأ القوائم الأخرى بـundef.
    PHParray_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).map {block}enum1.zip(enum2, ...).map {block} [enum1, enum2, ...].transpose.map {block}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 - Rlapply(list, func)mapply(func, list1, list2)mapply(func, list1, list2, ...)يتم تدوير القوائم الأقصر
    سكالاlist.map(func)(list1, list2).zipped.map(func)(list1, list2, list3).zipped.map(func)ملاحظة: لا يمكن أن يزيد العدد عن 3.يتوقف بعد انتهاء القائمة الأقصر
    مخطط (بما في ذلك الخداع والمكر )(map funclist)(map funclist1list2)(map funclist1list2 ...)يجب أن تكون جميع القوائم بنفس الطول (يمتد معيار SRFI-1 ليشمل قوائم ذات أطوال مختلفة)
    أحاديث قصيرةaCollection collect: aBlockaCollection1 with: aCollection2 collect: aBlockفشل
    لغة الآلة القياسيةmap funclistListPair.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 3list!blockfor-each(list,func)for-each-pair(list1,list2,func)في blockهذا السياق، .يحتوي العنصر على القيمة الحاليةيتوقف بعد انتهاء أقصر قائمة

    انظر أيضاً

    مراجع