دالة من الرتبة العليا
في الرياضيات وعلوم الحاسوب ، الدالة ذات الرتبة العليا ( HOF ) هي دالة تقوم بواحدة على الأقل مما يلي:
- تأخذ دالة واحدة أو أكثر كوسيطات (أي معلمة إجرائية ، وهي معلمة لإجراء هو نفسه إجراء) ،
- تُرجع هذه الدالة دالةً كنتيجة لها.
جميع الدوال الأخرى هي دوال من الرتبة الأولى . في الرياضيات، تُسمى الدوال من الرتب العليا أيضًا بالمؤثرات أو الدوال الوظيفية . يُعد المؤثر التفاضلي في حساب التفاضل والتكامل مثالًا شائعًا، لأنه يحول الدالة إلى مشتقتها ، وهي أيضًا دالة. يجب عدم الخلط بين الدوال من الرتب العليا واستخدامات أخرى لكلمة "دالة وظيفية" في الرياضيات، انظر: الدالة الوظيفية (توضيح) .
في حساب لامدا غير المُنمذج ، تكون جميع الدوال من الرتبة العليا؛ أما في حساب لامدا المُنمذج ، الذي اشتُقت منه معظم لغات البرمجة الوظيفية ، فإن الدوال من الرتبة العليا التي تأخذ دالة واحدة كوسيط تكون قيمًا من النوع التالي:.
أمثلة عامة
- تُعدّ دالة map ، الموجودة في العديد من لغات البرمجة الوظيفية، مثالاً على الدوال عالية الرتبة. تأخذ هذه الدالة وسيطين: دالة f ومجموعة من العناصر، وتُعيد في النهاية مجموعة جديدة مُطبّق عليها الدالة f على كل عنصر من عناصر المجموعة.
- تتيح دالة الفرز ، التي تأخذ دالة مقارنة كمعامل، للمبرمج فصل خوارزمية الفرز عن مقارنات العناصر المراد فرزها. وتُعد دالة الفرز القياسية في لغة C مثالاً على ذلك.
qsort - دالة التصفية ، التي تأخذ مجموعة ودالة تُرجع قيمة صحيحة أو خاطئة، وتُرجع مجموعة جديدة تحتوي على عناصر مجموعة المعلمات حيث أرجعت الدالة القيمة الصحيحة.
- طي (بما في ذلك foldl و foldr )
- مسح ضوئي
- يتقدم
- تركيب الدوال
- اندماج
- أتصل مرة أخرى
- اجتياز الأشجار
- تستخدم قواعد مونتاجو ، وهي نظرية دلالية للغة الطبيعية، وظائف من الرتبة العليا
الدعم في لغات البرمجة
الدعم المباشر
لا تهدف الأمثلة إلى مقارنة لغات البرمجة أو التباين بينها، بل إلى أن تكون بمثابة أمثلة على بناء جملة الدوال من الرتبة العليا
في الأمثلة التالية، تأخذ الدالة ذات الرتبة العليا 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 )) -- 13MATLAB
دالة النتيجة = مرتين ( 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 )) # يطبع 13R
مرتين <- \ ( 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.
انظر أيضاً
- وظيفة من الدرجة الأولى
- المنطق التوافقي
- البرمجة على مستوى الوظائف
- البرمجة الوظيفية
- حساب كابا - شكلية للدوال تستبعد الدوال ذات الرتبة الأعلى
- نمط الاستراتيجية
- الرتب العليا
مراجع
- ↑ "PHP: دوال الأسهم - دليل المستخدم" . www.php.net . تم الاطلاع عليه بتاريخ 1 مارس 2021 .
- البرمجة الوظيفية
- حساب التفاضل والتكامل لامدا
- الدوال ذات الرتبة العليا
- البرامج الفرعية
- مقارنات لغات البرمجة
