دالة من الرتبة العليا

في الرياضيات وعلوم الحاسوب ، الدالة ذات الرتبة العليا ( HOF ) هي دالة تقوم بواحدة على الأقل مما يلي:

  • تأخذ دالة واحدة أو أكثر كوسيطات (أي معلمة إجرائية ، وهي معلمة لإجراء هو نفسه إجراء) ،
  • تُرجع هذه الدالة دالةً كنتيجة لها.

جميع الدوال الأخرى هي دوال من الرتبة الأولى . في الرياضيات، تُسمى الدوال من الرتب العليا أيضًا بالمؤثرات أو الدوال الوظيفية . يُعد المؤثر التفاضلي في حساب التفاضل والتكامل مثالًا شائعًا، لأنه يحول الدالة إلى مشتقتها ، وهي أيضًا دالة. يجب عدم الخلط بين الدوال من الرتب العليا واستخدامات أخرى لكلمة "دالة وظيفية" في الرياضيات، انظر: الدالة الوظيفية (توضيح) .

في حساب لامدا غير المُنمذج ، تكون جميع الدوال من الرتبة العليا؛ أما في حساب لامدا المُنمذج ، الذي اشتُقت منه معظم لغات البرمجة الوظيفية ، فإن الدوال من الرتبة العليا التي تأخذ دالة واحدة كوسيط تكون قيمًا من النوع التالي:(τ1τ2)τ3{\displaystyle (\tau _{1}\to \tau _{2})\to \tau _{3}}.

أمثلة عامة

الدعم في لغات البرمجة

الدعم المباشر

لا تهدف الأمثلة إلى مقارنة لغات البرمجة أو التباين بينها، بل إلى أن تكون بمثابة أمثلة على بناء جملة الدوال من الرتبة العليا

في الأمثلة التالية، تأخذ الدالة ذات الرتبة العليا twiceدالةً، وتُطبّقها على قيمةٍ ما مرتين. إذا twiceكان لا بد من تطبيقها عدة مرات لنفس القيمة، fفمن الأفضل أن تُعيد دالةً بدلاً من قيمة. وهذا يتماشى مع مبدأ " عدم تكرار نفسك ".

APL

مرتين { ⍺⍺ ⍺⍺ }زائد ثلاثة { + 3 }g { زائد ثلاثة مرتين } g 7 13

أو بطريقة ضمنية:

مرتين 2زائد ثلاثة + 3g زائد ثلاثة ضعف g 7 13

لغة سي++

الاستخدام std::functionفي C++11 :

استيراد std ؛auto twice = []( const std :: function < int ( int ) >& f ) -> auto { return [ f ]( int x ) -> int { return f ( f ( x )); }; };auto plusThree = []( int i ) -> int { return i + 3 ; };int main () { auto g = twice ( plusThree );std :: println ( "{}" , g ( 7 )); // 13 }

أو باستخدام دوال لامدا العامة التي يوفرها C++14:

استيراد std ؛auto twice = []( const auto & f ) -> auto { return [ f ]( int x ) -> int { return f ( f ( x )); }; };auto plusThree = []( int i ) -> int { return i + 3 ; };int main () { auto g = twice ( plusThree );std :: println ( "{}" , g ( 7 )); // 13 }

سي شارب

باستخدام المندوبين فقط:

باستخدام النظام ؛public class Program { public static void Main ( string [] args ) { Func < Func < int , int > , Func < int , int >> twice = f => x => f ( f ( x ));Func < int , int > plusThree = i => i + 3 ;var g = twice ( plusThree );Console.WriteLine ( g ( 7 ) ); // 13 } }

أو بشكل مكافئ، باستخدام الطرق الثابتة:

باستخدام النظام ؛public class Program { private static Func < int , int > Twice ( Func < int , int > f ) { return x => f ( f ( x )); }private static int PlusThree ( int i ) => i + 3 ;public static void Main ( string [] args ) { var g = Twice ( PlusThree );Console.WriteLine ( g ( 7 ) ); // 13 } }

كلوجر

( تعريف مرتين [ f ] ( دالة [ x ] ( f ( f x ))))( تعريف زائد ثلاثة [ i ] ( + i 3 ))( def g ( twice plus-three ))( println ( g 7 ) ; 13

لغة ترميز ColdFusion (CFML)

دالة مرتين = دالة ( f ) { إرجاع دالة ( x ) { إرجاع f ( f ( x )); }; };plusThree = function ( i ) { return i + 3 ; };g = مرتين ( زائد ثلاثة );writeOutput ( g ( 7 )); // 13

لغة الشفرة الشائعة

( defun twice ( f ) ( lambda ( x ) ( funcall f ( funcall f x )))) ( defun plus-three ( i ) ( + i 3 )) ( defvar g ( twice #' plus-three )) ( print ( funcall g 7 ))

د

استيراد std.stdio : writeln ؛الاسم المستعار مرتين = ( f ) => ( int x ) => f ( f ( x ));alias plusThree = ( int i ) => i + 3 ;void main () { auto g = twice ( plusThree );writeln ( g ( 7 )); // 13 }

دارت

دالة عددية ( عدد صحيح ) مرتين ( دالة عددية ( عدد صحيح ) f ) { إرجاع ( x ) { إرجاع f ( f ( x )); }; }دالة الجمع الثلاثي ( int i ) { return i + 3 ; }void main () { final g = twice ( plusThree ); print ( g ( 7 )); // 13 }

إكسير

في لغة Elixir، يمكنك مزج تعريفات الوحدات النمطية والوظائف المجهولة

defmodule Hof do def twice ( f ) do fn ( x ) -> f . ( f . ( x )) end end endplus_three = fn ( i ) -> i + 3 endg = Hof.twoce ( plus_three )IO . puts g . ( 7 ) # 13

بدلاً من ذلك، يمكننا أيضاً التركيب باستخدام الدوال المجهولة البحتة.

مرتين = fn ( f ) -> fn ( x ) -> f . ( f . ( x )) نهاية نهايةplus_three = fn ( i ) -> i + 3 endg = ضعف . ( زائد ثلاثة )IO . puts g . ( 7 ) # 13

إرلانغ

or_else ([], _) -> false ; or_else ([ F | Fs ], X ) -> or_else ( Fs , X , F ( X )).or_else ( Fs , X , false ) -> or_else ( Fs , X ); or_else ( Fs , _, { false , Y }) -> or_else ( Fs , Y ); or_else (_, _, R ) -> R .or_else ([ fun erlang : is_integer / 1 , fun erlang : is_atom / 1 , fun erlang : is_list / 1 ], 3 . 23 ).

في مثال Erlang هذا، تأخذ الدالة ذات الرتبة العليا or_else/2قائمة من الدوال ( Fs) ووسيطًا ( X). تُقيّم الدالة Fباستخدام الوسيط Xكوسيط. إذا أعادت الدالة Fخطأً، فسيتم تقييم الدالة التالية في القائمة . إذا أعادت Fsالدالة قيمة صحيحة ، فسيتم تقييم الدالة التالية في القائمة باستخدام الوسيط . إذا أعادت الدالة قيمة خاطئة، فستُعيد الدالة ذات الرتبة العليا القيمة . لاحظ أن و و يمكن أن تكون دوالًا. يُعيد المثال القيمة .F{false, Y}FsYFRor_else/2RXYRfalse

فا#

لنفترض أن f مرتين = f >> flet plus_three = (+) 3ليكن g = ضعف زائد ثلاثةg 7 |> printf "%A" // 13

يذهب

الحزمة الرئيسيةاستيراد "fmt"func twice ( f func ( int ) int ) func ( int ) int { return func ( x int ) int { return f ( f ( x )) } }func main () { plusThree := func ( i int ) int { return i + 3 }g := مرتين ( زائد ثلاثة )fmt.Println ( g ( 7 ) ) // 13 }

لاحظ أنه يمكن تعريف الدالة الحرفية إما باستخدام معرف ( twice) أو بشكل مجهول (يتم تعيينها إلى متغير plusThree).

رائع

دالة twice = { f , x -> f ( f ( x )) } دالة plusThree = { it + 3 } دالة g = twice . curry ( plusThree ) println g ( 7 ) // 13

هاسكل

مرتين :: ( عدد صحيح -> عدد صحيح ) -> ( عدد صحيح -> عدد صحيح ) مرتين f = f . fplusThree :: Int -> Int plusThree = ( + 3 )main :: IO () main = print ( g 7 ) -- 13 where g = twice plusThree

ج

بشكل صريح،

مرتين = ظرف : 'uu y'plusthree =. فعل : 'y + 3' g =. plusthree مرتين g 7 13

أو ضمنيًا،

مرتين =. ^: 2زائد ثلاثة = . +& 3 غ =. زائد ثلاثة ضعف غ 7 13

جافا (1.8+)

باستخدام واجهات وظيفية فقط:

استيراد java.util.function.* ;class Main { public static void main ( String [] args ) { Function < IntUnaryOperator , IntUnaryOperator > twice = f -> f . andThen ( f );IntUnaryOperator plusThree = i -> i + 3 ;var g = twice.apply ( plusThree ) ;System.out.println ( g.applyAsInt ( 7 ) ) ; // 13 } }

أو بشكل مكافئ، باستخدام الطرق الثابتة:

استيراد java.util.function.* ;class Main { private static IntUnaryOperator twice ( IntUnaryOperator f ) { return f . andThen ( f ); }private static int plusThree ( int i ) { return i + 3 ; }public static void main ( String [] args ) { var g = twice ( Main :: plusThree );System.out.println ( g.applyAsInt ( 7 ) ) ; // 13 } }

جافا سكريبت

باستخدام دوال الأسهم:

"استخدام صارم" ؛const twice = f => x => f ( f ( x ));const plusThree = i => i + 3 ;const g = twice ( plusThree );console.log ( g ( 7 ) ) ; // 13

أو باستخدام الصيغة الكلاسيكية:

"استخدام صارم" ؛دالة مرتين ( f ) { إرجاع دالة ( x ) { إرجاع f ( f ( x )); }; }دالة plusThree ( i ) { return i + 3 ; }const g = twice ( plusThree );console.log ( g ( 7 ) ) ; // 13

جوليا

julia> function twice ( f ) function result ( x ) return f ( f ( x )) end return result end twice (دالة عامة ذات طريقة واحدة)julia> plusthree ( i ) = i + 3 plusthree ( دالة عامة ذات طريقة واحدة)julia> g = twice ( plusthree ) (::var"#result#3"{typeof(plusthree)}) (دالة عامة ذات طريقة واحدة)julia> g ( 7 ) 13

كوتلين

دالة مرتين ( f : ( عدد صحيح ) -> عدد صحيح ): ( عدد صحيح ) -> عدد صحيح { إرجاع { f ( f ( it )) } }دالة plusThree ( i : Int ) = i + 3fun main () { val g = twice ( :: plusThree )println ( g ( 7 )) // 13 }

لغة لوا

دالة مرتين ( f ) إرجاع دالة ( x ) إرجاع f ( f ( x )) نهاية نهايةدالة plusThree ( i ) تُرجع i + 3 نهايةlocal g = twice ( plusThree )print ( g ( 7 )) -- 13

MATLAB

دالة النتيجة = مرتين ( f ) النتيجة = @( x ) f ( f ( x )); نهايةplusthree = @( i ) i + 3 ;g = ضعف ( زائد ثلاثة )disp ( g ( 7 )); % 13

أوكاميل

ليكن f( x ) مرتين = f ( fx )let plus_three = (+) 3let () = let g = twice plus_three inprint_int ( g 7 ); (* 13 *) print_newline ()

PHP

<?phpdeclare ( strict_types = 1 );دالة مرتين ( دالة قابلة للاستدعاء $f ) : إغلاق { إرجاع دالة ( عدد صحيح $x ) استخدام ( $f ) : عدد صحيح { إرجاع $f ( $f ( $x )); }; }دالة plusThree ( int $i ) : int { return $i + 3 ; }$g = twice ( 'plusThree' );echo $g ( 7 ), " \n " ; // 13

أو مع جميع الدوال في متغيرات:

<?phpdeclare ( strict_types = 1 );$twice = fn ( callable $f ) : Closure => fn ( int $x ) : int => $f ( $f ( $x ));$plusThree = fn ( int $i ) : int => $i + 3 ;$g = $twice ( $plusThree );echo $g ( 7 ), " \n " ; // 13

لاحظ أن الدوال السهمية تلتقط ضمنيًا أي متغيرات تأتي من النطاق الأصل، [ 1 ] بينما تتطلب الدوال المجهولة الكلمة useالمفتاحية للقيام بنفس الشيء.

بيرل

استخدم الوضع الصارم ؛ استخدم التحذيرات ؛sub twice { my ( $f ) = @_ ; sub { $f -> ( $f -> ( @_ )); }; }sub plusThree { my ( $i ) = @_ ; $i + 3 ; }my $g = twice ( \& plusThree );print $g -> ( 7 ), "\n" ; # 13

أو مع جميع الدوال في متغيرات:

استخدم الوضع الصارم ؛ استخدم التحذيرات ؛my $twice = sub { my ( $f ) = @_ ; sub { $f -> ( $f -> ( @_ )); }; };my $plusThree = sub { my ( $i ) = @_ ; $i + 3 ; };my $g = $twice -> ( $plusThree );print $g -> ( 7 ), "\n" ; # 13

بايثون

دالة ` twice` تأخذ وسيطًا من نوع `F` ، وتعيد الدالة ` result`. تأخذ وسيطًا من نوع `x` ، وتعيد الدالة `result`. تُرجع الدالة `f` الدالة ` f` مع الوسيط ` x` . تُرجع الدالة `result` .plus_three : Callable [ int ] = lambda i : i + 3g : int = twice ( plus_three )print ( g ( 7 )) # يطبع 13

تُستخدم صيغة المُزخرفات في بايثون غالبًا لاستبدال دالة بنتيجة تمرير تلك الدالة عبر دالة من رتبة أعلى. على سبيل المثال، gيمكن تنفيذ الدالة بشكل مكافئ كما يلي:

@twice def g ( i : int ) -> int : return i + 3print ( g ( 7 )) # يطبع 13

R

مرتين <- \ ( f ) \ ( x ) f ( f ( x ))دالة plusThree <- function ( i ) i + 3g <- مرتين ( زائد ثلاثة )> g ( 7 ) [ 1 ] 13

راكو

sub twice ( Callable:D $f ) { return sub { $f ( $f ( $^x )) }; } sub plusThree ( Int:D $i ) { return $i + 3 ; } my $g = twice ( &plusThree ); قل $g ( 7 ); # 13

في راكو، جميع كائنات الكود عبارة عن دوال مغلقة، وبالتالي يمكنها الإشارة إلى متغيرات "معجمية" داخلية من نطاق خارجي لأن المتغير المعجمي "مغلق" داخل الدالة. كما يدعم راكو صيغة "الكتلة المدببة" لتعبيرات لامدا التي يمكن إسنادها إلى متغير أو استدعاؤها بشكل مجهول.

روبي

def twice ( f ) - > ( x ) { f.call ( f.call ( x ) ) } endplus_three = -> ( i ) { i + 3 }g = مرتين ( زائد ثلاثة )puts g . call ( 7 ) # 13

الصدأ

fn twice ( f : impl Fn ( i32 ) -> i32 ) -> impl Fn ( i32 ) -> i32 { move | x | f ( f ( x )) }fn plus_three ( i : i32 ) -> i32 { i + 3 }fn main () { let g = twice ( plus_three );println! ( "{}" , g ( 7 )) // 13 }

سكالا

كائن رئيسي { دالة مرتين ( f : عدد صحيح => عدد صحيح ): عدد صحيح => عدد صحيح = f تكوين fدالة plusThree ( i : Int ): Int = i + 3def main ( args : Array [ String ]): Unit = { val g = twice ( plusThree )print ( g ( 7 )) // 13 } }

مخطط

( define ( compose f g ) ( lambda ( x ) ( f ( g x ))))( define ( twice f ) ( compose f f ))( define ( plus-three i ) ( + i 3 ))( عرّف g ( ضعف زائد ثلاثة ))( عرض ( g 7 )) ؛ 13 ( عرض " \n " )

سويفت

func twice ( _ f : @escaping ( Int ) -> Int ) -> ( Int ) -> Int { return { f ( f ( $0 ) ) } }let plusThree = { $0 + 3 }ليكن g = ضعف ( زائد ثلاثة )print ( g ( 7 )) // 13

تي سي إل

قم بتعيين {{ f x } {تطبيق $f [تطبيق $f $x ]}} قم بتعيين plusThree {{ i } {إرجاع [تعبير $i + 3 ]}}# النتيجة: 13 puts [apply $twice $plusThree 7 ]

يستخدم Tcl الأمر apply لتطبيق دالة مجهولة (منذ الإصدار 8.6).

XACML

يحدد معيار XACML وظائف من الدرجة العليا في المعيار لتطبيق وظيفة على قيم متعددة لمجموعات السمات.

قاعدة السماح بالدخول { السماح الشرط أي من أي (دالة [ سلسلة يساوي المواطنات ، المواطنات المسموح بها ) }

يمكن العثور على قائمة الدوال ذات الرتبة العليا في XACML هنا .

XQuery

declare function local:twice ( $ f , $ x ) { $ f ( $ f ( $ x )) };declare function local:plusthree ( $ i ) { $ i + 3 };local:twice ( local:plusthree # 1 , 7 ) (: 13 :)

البدائل

مؤشرات الدوال

تتيح مؤشرات الدوال في لغات مثل C و C++ و Fortran و Pascal للمبرمجين تمرير مراجع إلى الدوال. يحسب كود C التالي تقريبًا لتكامل دالة عشوائية:

#include <stdio.h>double square ( double x ) { return x * x ; }double cube ( double x ) { return x * x * x ; }/* حساب تكامل الدالة f() ضمن الفترة [a,b] */ double integral ( double f ( double x ), double a , double b , int n ) { int i ; double sum = 0 ; double dt = ( b - a ) / n ; for ( i = 0 ; i < n ; ++ i ) { sum += f ( a + ( i + 0.5 ) * dt ); } return sum * dt ; }int main () { printf ( "%g \n " , integral ( square , 0 , 1 , 100 )); printf ( "%g \n " , integral ( cube , 0 , 1 , 100 )); return 0 ; }

تستخدم دالة qsort من مكتبة C القياسية مؤشر دالة لمحاكاة سلوك دالة من الرتبة العليا.

وحدات الماكرو

يمكن استخدام وحدات الماكرو لتحقيق بعض تأثيرات الدوال عالية الرتبة. مع ذلك، يصعب على وحدات الماكرو تجنب مشكلة التقاط المتغيرات؛ وقد ينتج عنها كميات كبيرة من التعليمات البرمجية المكررة، مما يصعب على المترجم تحسينها. عمومًا، لا تُعتبر وحدات الماكرو ذات كتابة قوية، على الرغم من أنها قد تُنتج تعليمات برمجية ذات كتابة قوية.

تقييم الكود الديناميكي

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

  • عادة ما يكون رمز الوسيط المراد تنفيذه غير مكتوب بشكل ثابت ؛ تعتمد هذه اللغات بشكل عام على الكتابة الديناميكية لتحديد سلامة وشكل الكود المراد تنفيذه.
  • عادةً ما تُقدَّم الوسيطة كسلسلة نصية، وقد لا تُعرف قيمتها إلا عند تشغيل البرنامج. يجب إما ترجمة هذه السلسلة أثناء تنفيذ البرنامج (باستخدام الترجمة الفورية ) أو تقييمها بالتفسير ، مما يُضيف بعض العبء الإضافي عند التشغيل، ويؤدي عادةً إلى إنتاج كود أقل كفاءة.

أشياء

في لغات البرمجة كائنية التوجه التي لا تدعم الدوال عالية الرتبة، يمكن أن تكون الكائنات بديلاً فعالاً. تعمل توابع الكائن بشكل أساسي كالدوال، وقد يقبل التابع كائنات كمعاملات ويعيد كائنات كقيم مرجعية. مع ذلك، غالبًا ما تُضيف الكائنات عبئًا إضافيًا على وقت التشغيل مقارنةً بالدوال البحتة، بالإضافة إلى شيفرة نمطية إضافية لتعريف الكائن وإنشائه وتوابعه. توفر اللغات التي تسمح بالكائنات أو الهياكل القائمة على المكدس (بدلاً من الكومة ) مرونة أكبر في هذا الأسلوب.

مثال على استخدام سجل بسيط قائم على المكدس في لغة فري باسكال مع دالة تُرجع دالة أخرى:

مثال على البرنامج ؛type int = integer ; Txy = record x , y : int ; end ; Tf = function ( xy : Txy ) : int ; function f ( xy : Txy ) : int ; begin Result : = xy.y + xy.x ; end ;دالة g ( دالة : Tf ) : Tf ؛ بداية النتيجة := دالة ؛ نهاية ؛فار أ : Tf ; ص ص : Txy = ( س : 3 ; ص : 7 ) ;begin a := g ( @ f ) ; // إرجاع دالة إلى "a" writeln ( a ( xy )) ; // يطبع 10 end .

a()تأخذ الدالة Txyسجلاً كمدخل وتعيد القيمة الصحيحة لمجموع حقول السجل x( y3 + 7).

إلغاء الوظيفة

يمكن استخدام إلغاء الوظائف لتنفيذ وظائف من الدرجة العليا في اللغات التي تفتقر إلى وظائف من الدرجة الأولى :

// هياكل بيانات الدوال غير الوظيفية template < typename T > struct Add { T value ; }; template < typename T > struct DivBy { T value ; }; template < typename F , typename G > struct Composition { F f ; G g ; };// تطبيقات الدوال غير الوظيفية template < typename F , typename G , typename X > auto apply ( Composition < F , G > f , X arg ) { return apply ( f . f , apply ( f . g , arg )); }template < typename T , typename X > auto apply ( Add < T > f , X arg ) { return arg + f . value ; }template < typename T , typename X > auto apply ( DivBy < T > f , X arg ) { return arg / f . value ; }// قالب دالة التركيب من الرتبة العليا < typename F , typename G > Composition < F , G > compose ( F f , G g ) { return Composition < F , G > { f , g }; }int main ( int argc , const char * argv []) { auto f = compose ( DivBy < float > { 2.0f }, Add < int > { 5 }); apply ( f , 3 ); // 4.0f apply ( f , 9 ); // 7.0f return 0 ; }

في هذه الحالة، تُستخدم أنواع مختلفة لتشغيل وظائف مختلفة عبر تحميل الوظائف الزائد . للوظيفة المحملة في هذا المثال التوقيع التالي auto apply.

انظر أيضاً

مراجع

  1. "PHP: دوال الأسهم - دليل المستخدم" . www.php.net . تم الاطلاع عليه بتاريخ 1 مارس 2021 .