استراتيجية التقييم
في لغة البرمجة ، استراتيجية التقييم هي مجموعة من القواعد لتقييم التعبيرات. [1] غالبًا ما يستخدم هذا المصطلح للإشارة إلى المفهوم الأكثر تحديدًا لاستراتيجية تمرير المعلمات [2] التي تحدد نوع القيمة التي يتم تمريرها إلى الدالة لكل معلمة ( استراتيجية الربط) [3] وما إذا كان سيتم تقييم المعلمات أم لا لاستدعاء دالة، وإذا كان الأمر كذلك فبأي ترتيب (ترتيب التقييم). [4] إن فكرة استراتيجية التخفيض متميزة، [5] على الرغم من أن بعض المؤلفين يخلطون بين المصطلحين ولم يتم الاتفاق على تعريف كل مصطلح على نطاق واسع. [6]
للتوضيح، تنفيذ استدعاء دالة f(a,b)
قد يقوم أولاً بتقييم الوسيطات a
و b
، ثم تخزين النتائج في مراجع أو مواقع في الذاكرة ref_a
و ref_b
، ثم تقييم نص الدالة مع تلك المراجع التي تم تمريرها. وهذا يمنح الدالة القدرة على البحث عن قيم الوسيطات، وتعديلها عبر التعيين كما لو كانت متغيرات محلية، وإرجاع القيم عبر المراجع. هذه هي استراتيجية تقييم الاتصال حسب المرجع. [7]
استراتيجية التقييم هي جزء من دلالات تعريف لغة البرمجة. بعض اللغات، مثل (PureScript)، لديها متغيرات مع استراتيجيات تقييم مختلفة. تدعم بعض اللغات التصريحية ، مثل سجل البيانات (Datalog)، استراتيجيات تقييم متعددة. تحدد بعض اللغات اصطلاح الاتصال .[ <span title="That page discusses low-level platform-specific details; is that what "calling convention" is meant to refer to here? (June 2023)">مطلوب توضيح</span> ]
جدول
عدلهذا جدول لاستراتيجيات التقييم واللغات التمثيلية حسب السنة المقدمة. يتم إدراج اللغات التمثيلية بترتيب زمني، بدءًا من اللغة (اللغات) التي قدمت الإستراتيجية وتليها اللغات البارزة التي تستخدم الإستراتيجية. [8] :434
استراتيجية التقييم | اللغات التمثيلية | سنة تقديمه لأول مرة |
---|---|---|
الاتصال حسب المرجع | فورتران 2، بي إل/آي | 1958 |
اتصل حسب القيمة | ALGOL ، C ، مخطط ، MATLAB [9] | 1960 |
اتصل بالاسم | الغول 60 ، سيمولا | 1960 |
الاتصال عن طريق استعادة النسخ | فورتران 4، آدا [10] | 1962 |
الدعوة بالتوحيد | برولوغ | 1965 [11] [12] |
اتصل حسب الحاجة | SASL, [13] هاسكل، آر [14] | 1971 [15] |
الاتصال عن طريق المشاركة | كلو ، جافا ، بايثون ، روبي ، جوليا | 1974 [16] |
الاتصال حسب المعلمات المرجعية | C++ ، PHP ، [17] C# ، [18] فيجوال بيسك. صافي [19] | 1985 [20] |
الاتصال بالرجوع إلى const | سي++ ، سي | 1985 [20] |
طلبات التقييم
عدلفي حين أن ترتيب العمليات يحدد شجرة بناء الجملة المجردة للتعبير، فإن ترتيب التقييم يحدد الترتيب الذي يتم به تقييم التعبيرات. مثل برنامج بايثون
def f(x):
print(x, end='')
return x
print(f(1) + f(2),end='')
المخرجات 123
بسبب ترتيب التقييم من اليسار إلى اليمين في بايثون، ولكن نفس البرنامج في OCaml:
let f x = print_int x; x ;;
print_int (f 1 + f 2)
المخرجات 213
بسبب ترتيب تقييم OCaml من اليمين إلى اليسار.
يظهر ترتيب التقييم بشكل رئيسي في التعليمات البرمجية مع آثار جانبية، ولكنه يؤثر أيضًا على أداء التعليمات البرمجية لأن الترتيب الصارم يمنع جدولة التعليمات. لهذا السبب، تركت معايير اللغة مثل C++ تقليديًا الترتيب غير محدد، على الرغم من أن لغات مثل Java وC# تحدد ترتيب التقييم من اليسار إلى اليمين [8] :240–241وقد أضاف معيار C++ 17 قيودًا على ترتيب التقييم. [21]
تقييم صارم
عدلالترتيب التطبيقي عبارة عن مجموعة من أوامر التقييم التي يتم فيها تقييم وسيطات الوظيفة بالكامل قبل تطبيق الوظيفة. [22] هذا له تأثير جعل الوظيفة صارمة ، أي أن نتيجة الوظيفة غير محددة إذا كانت أي من الوسائط غير محددة، لذلك يُطلق على تقييم الترتيب التطبيقي بشكل أكثر شيوعًا اسم التقييم الصارم . علاوة على ذلك، يتم تنفيذ استدعاء الوظيفة بمجرد مواجهته في إجراء ما، لذلك يطلق عليه أيضًا التقييم المتحمس أو التقييم الجشع . [23] [24] يشير بعض المؤلفين إلى التقييم الصارم باسم "الاستدعاء حسب القيمة" نظرًا لاستراتيجية ربط الاتصال حسب القيمة التي تتطلب تقييمًا صارمًا. [25]
تقوم Lisp و Eiffel وJava الشائعة بتقييم وسيطات الوظائف من اليسار إلى اليمين. C يترك الترتيب غير محدد. [26] يتطلب المخطط أن يكون أمر التنفيذ هو التنفيذ المتسلسل لتبديل غير محدد للوسائط. [27] وبالمثل، يترك OCaml الترتيب غير محدد، ولكنه عمليًا يقوم بتقييم الوسائط من اليمين إلى اليسار نظرًا لتصميم آلته المجردة . [28] كل هذه هي تقييم صارم.
تقييم غير صارم
عدلأمر التقييم غير الصارم هو أمر تقييم غير صارم، أي أن الدالة قد ترجع نتيجة قبل تقييم كافة الوسائط الخاصة بها بشكل كامل. [29] :46–47المثال النموذجي هو تقييم الترتيب العادي ، والذي لا يقوم بتقييم أي من الوسائط حتى تكون هناك حاجة إليها في نص الوظيفة. [30] يتميز تقييم الأمر العادي بخاصية أنه ينتهي دون خطأ عندما ينتهي أي أمر تقييم آخر دون خطأ. [31] يأتي اسم "الترتيب الطبيعي" من حساب التفاضل والتكامل لامدا، حيث سيجد تخفيض الترتيب الطبيعي شكلاً طبيعيًا إذا كان هناك واحد (إنها استراتيجية تخفيض "تطبيعية"). [32] تم تصنيف التقييم البطيء في هذه المقالة على أنه أسلوب ربط وليس كأمر تقييم. لكن هذا التمييز لا يتم اتباعه دائمًا، ويعرّف بعض المؤلفين التقييم البطيء بأنه تقييم النظام العادي أو العكس، [33] [34] أو يخلطون بين عدم الصرامة والتقييم البطيء. [29] :43–44
تستخدم التعبيرات المنطقية في العديد من اللغات شكلاً من أشكال التقييم غير الصارم يسمى تقييم الدائرة القصيرة ، حيث يقوم التقييم بتقييم التعبير الأيسر ولكن قد يتخطى التعبير الأيمن إذا كان من الممكن تحديد النتيجة - على سبيل المثال، في التعبير المنفصل (OR) حيث true
تمت مواجهته، أو في تعبير متصل (AND) حيث تمت مواجهة false
، وما إلى ذلك. [34] تستخدم التعبيرات الشرطية بالمثل تقييمًا غير صارم - حيث يتم تقييم فرع واحد فقط. [29]
مقارنة الترتيب التطبيقي وتقييم الطلب العادي
عدلباستخدام التقييم العادي، يتم تجاهل التعبيرات غير الضرورية مثل الحسابات الباهظة أو الأخطاء أو الحلقات اللا نهائية، مما يسمح للمستخدم بتحديد بنية تدفق التحكم بمعرفته. هذا يعني أن المستخدم يمكنه استخدام بنيات معقدة مثل الشروط والحلقات بشكل غير مقيّم، وهو أمر غير ممكن في التقييم التطبيقي الذي يستخدم مكدس الاستدعاءات. من المهم أن نلاحظ أن التقييم العادي كان يعاني تاريخيًا من ضعف في أدوات تصحيح الأخطاء بسبب تعقيدها النسبي.
استراتيجيات ملزمة صارمة
عدلاتصل حسب القيمة
عدلفي الاستدعاء بالقيمة (أو المرور بالقيمة)، يُربط قيمة التعبير المقدم كوسيط إلى المتغير المقابل في الدالة، عادةً عن طريق نسخ القيمة إلى منطقة ذاكرة جديدة. إذا كانت الدالة أو الإجراء قادرة على تعيين قيم لمعاملاتها، فإنها تعيين قيم فقط لمتغيرها المحلي، وبالتالي، أي شيء يتم تمريره إلى استدعاء الدالة يظل دون تغيير في نطاق النداء الأصلي بعد عودة الدالة. على سبيل المثال، في لغة باسكال، عند تمرير مصفوفة بالقيمة، سيؤدي ذلك إلى نسخ كامل المصفوفة، وأي تغييرات تطرأ على هذه المصفوفة ستكون غير مرئية للمتصل بالدالة بعد اكتمال تنفيذ الدالة.
program Main;
uses crt;
procedure PrintArray(a: Array of integer);
var
i: Integer;
begin
for i := Low(a) to High(a) do
Write(a[i]);
WriteLn();
end;
Procedure Modify(Row : Array of integer);
begin
PrintArray(Row); // 123
Row[1] := 4;
PrintArray(Row); // 143
end;
Var
A : Array of integer;
begin
A := [1,2,3]؛
PrintArray(A); // 123
Modify(A);
PrintArray(A); // 123
end.
الانجراف الدلالي
عدلبالمعنى الدقيق للكلمة، ضمن الاستدعاء حسب القيمة، لا يمكن أن تكون أي عمليات يتم إجراؤها بواسطة الروتين المستدعى مرئية للمتصل، إلا كجزء من القيمة المرجعة. [16] وهذا يعني شكلاً من أشكال البرمجة الوظيفية البحتة في دلالات التنفيذ. ومع ذلك، فإن الإطناب "استدعاء حسب القيمة حيث تكون القيمة مرجعًا" أصبح شائعًا في مجتمع Java على سبيل المثال. [35] بالمقارنة مع التمرير التقليدي حسب القيمة، فإن القيمة التي يتم تمريرها ليست قيمة كما يفهمها المعنى العادي للقيمة، مثل عدد صحيح يمكن كتابته كعدد صحيح، ولكنه مقبض مرجعي داخلي للتنفيذ. تظهر الطفرات في هذا المقبض المرجعي في المتصل. نظرًا للطفرة المرئية، يُشار إلى هذا الشكل من "الاتصال حسب القيمة" بشكل أكثر ملاءمة باسم الاتصال عن طريق المشاركة . [16]
في اللغات الوظيفية البحتة ، تكون القيم وهياكل البيانات غير قابلة للتغيير، لذلك لا توجد إمكانية لوظيفة لتعديل أي من وسيطاتها. على هذا النحو، لا يوجد عادةً فرق دلالي بين التمرير حسب القيمة والتمرير حسب المرجع أو المؤشر إلى بنية البيانات، وكثيرًا ما تستخدم التطبيقات الاتصال حسب المرجع داخليًا لتحقيق فوائد الكفاءة. ومع ذلك، توصف هذه اللغات عادةً بأنها لغات الاتصال حسب القيمة.
الاتصال حسب المرجع
عدلالاستدعاء حسب المرجع (أو التمرير حسب المرجع) هو إستراتيجية تقييم حيث ترتبط المعلمة بمرجع ضمني للمتغير المستخدم كوسيطة، بدلاً من نسخة من قيمته. هذا يعني عادة أن الدالة يمكنها تعديل (أي تعيين إلى ) المتغير المستخدم كوسيطة - وهو الشيء الذي سوف يراه المتصل به. وبالتالي يمكن استخدام الاتصال حسب المرجع لتوفير قناة اتصال إضافية بين الوظيفة المطلوبة ووظيفة الاستدعاء. يمكن أن يؤدي التمرير حسب المرجع إلى تحسين الأداء بشكل ملحوظ: استدعاء دالة ذات بنية متعددة الميجابايت كوسيطة لا يتطلب نسخ البنية الكبيرة، فقط المرجع إلى البنية (والتي تكون بشكل عام كلمة آلية وبضعة بايتات فقط). ومع ذلك، فإن لغة الاستدعاء حسب المرجع تجعل من الصعب على المبرمج تتبع تأثيرات استدعاء الوظيفة، وقد تؤدي إلى حدوث أخطاء خفية.
نظرًا للاختلاف في بناء الجملة، فإن الفرق بين الاتصال حسب المرجع (حيث يكون نوع المرجع ضمنيًا) والاتصال عن طريق المشاركة (حيث يكون نوع المرجع صريحًا) غالبًا ما يكون غير واضح للوهلة الأولى. اختبار بسيط هو ما إذا كان من الممكن كتابة وظيفة swap(a, b)
في اللغة. [35] على سبيل المثال في فورتران:
program Main
implicit none
integer :: a = 1
integer :: b = 2
call Swap(a, b)
print *, a, b ! 2 1
contains
subroutine Swap(a, b)
integer, intent(inout) :: a, b
integer :: temp
temp = a
a = b
b = temp
end subroutine Swap
end program Main
ولذلك، فإن نية فورتران inout
تنفذ الاستدعاء حسب المرجع؛ يمكن تحويل أي متغير ضمنيًا إلى مؤشر مرجعي. على النقيض من ذلك فإن أقرب ما يمكن الحصول عليه في Java هو:
class Main {
static class Box {
int value;
public Box(int value) {
this.value = value;
}
}
static void swap(Box a, Box b) {
int temp = a.value;
a.value = b.value;
b.value = temp;
}
public static void main(String[] args) {
Box a = new Box(1);
Box b = new Box(2);
swap(a, b);
System.out.println(String.format("%d %d", a.value, b.value));
}
}
// output: 2 1
حيث يجب استخدام نوع Box
صريح لتقديم المقبض. Java عبارة عن مكالمة من خلال المشاركة ولكنها ليست مكالمة حسب المرجع. [35]
الاتصال عن طريق استعادة النسخ
عدلالاستدعاء عن طريق استعادة النسخ - المعروف أيضًا باسم "النسخ الوارد"، و"نتيجة الاستدعاء حسب القيمة"، و"إرجاع الاستدعاء حسب القيمة" (كما هو مصطلح في مجتمع فورتران ) - هو شكل مختلف من الاستدعاء حسب المرجع. مع الاستدعاء عن طريق استعادة النسخ، يتم نسخ محتويات الوسيطة إلى متغير جديد محلي لاستدعاء الاستدعاء. قد تقوم الدالة بعد ذلك بتعديل هذا المتغير، بشكل مشابه للاستدعاء حسب المرجع، ولكن بما أن المتغير محلي، فإن التعديلات غير مرئية خارج استدعاء الاستدعاء أثناء المكالمة. عندما يعود استدعاء الدالة، يتم نسخ المحتويات المحدثة لهذا المتغير مرة أخرى للكتابة فوق الوسيطة الأصلية ("المستعادة"). [36]
تتشابه دلالات الاستدعاء عن طريق استعادة النسخ في كثير من الحالات مع الاستدعاء حسب المرجع، ولكنها تختلف عندما تستعار وسيطتان دالتان أو أكثر لبعضهما البعض (أي الإشارة إلى نفس المتغير في بيئة المتصل). ضمن الاستدعاء حسب المرجع، ستؤثر الكتابة إلى وسيطة واحدة على الوسيطة الأخرى أثناء تنفيذ الوظيفة. في حالة الاستدعاء عن طريق استعادة النسخ، لن تؤثر الكتابة إلى وسيطة واحدة على الأخرى أثناء تنفيذ الوظيفة، ولكن في نهاية الاستدعاء، قد تختلف قيم الوسيطتين، وليس من الواضح أي وسيطة سيتم نسخها مرة أخرى أولاً وبالتالي ما هي القيمة التي يتلقاها متغير المتصل. [37] على سبيل المثال، تحدد Ada أن تعيين النسخ لكل معلمة in out
أو out
يحدث بترتيب عشوائي. [38] من البرنامج التالي (غير قانوني في Ada 2012) [39] يمكن ملاحظة أن سلوك GNAT هو النسخ بالترتيب من اليسار إلى اليمين عند العودة:
with Ada.Text_IO; use Ada.Text_IO;
procedure Test_Copy_Restore is
procedure Modify (A, B : in out Integer) is
begin
A := A + 1;
B := B + 2;
end Modify;
X : Integer := 0;
begin
Modify(X, X);
Put_Line("X = " & Integer'Image(X));
end Test_Copy_Restore;
-- $ gnatmake -gnatd.E test_copy_restore.adb; ./test_copy_restore
-- test_copy_restore.adb:12:10: warning: writable actual for "A" overlaps with actual for "B" [-gnatw.i]
-- X = 2
إذا أعاد البرنامج 1، فسيتم النسخ من اليمين إلى اليسار، وتحت دلالات الاستدعاء حسب المرجع، سيرجع البرنامج 3.
عندما يتم تمرير المرجع إلى المتصل غير مهيأ (على سبيل المثال، معلمة out
في Ada بدلاً من معلمة in out
)، قد يُطلق على استراتيجية التقييم هذه اسم "استدعاء حسب النتيجة".
وقد اكتسبت هذه الاستراتيجية الاهتمام في المعالجة المتعددة واستدعاءات الإجراءات عن بعد ، [40] على عكس الاستدعاء حسب المرجع، فإنها لا تتطلب اتصالاً متكررًا بين سلاسل التنفيذ للوصول المتغير.
الاتصال عن طريق المشاركة
عدلالاتصال من خلال المشاركة (المعروف أيضًا باسم "التمرير عن طريق المشاركة" أو "الاتصال من خلال الكائن" أو "الاتصال من خلال مشاركة الكائنات") هو استراتيجية تقييم وسيطة بين الاتصال حسب القيمة والاستدعاء حسب المرجع. بدلاً من عرض كل متغير كمرجع، فإن فئة معينة فقط من القيم، تسمى "المراجع"، أو " الأنواع المعبأة "، أو "الكائنات"، لها دلالات مرجعية، وعناوين هذه المؤشرات هي التي يتم تمريرها إلى الوظيفة . مثل الاستدعاء حسب القيمة، فإن قيمة العنوان الذي تم تمريره هي نسخة، والتعيين المباشر لمعلمة الوظيفة يحل محل النسخة ولا يكون مرئيًا لوظيفة الاستدعاء. مثل الاستدعاء حسب المرجع، يكون تغيير هدف المؤشر مرئيًا لوظيفة الاستدعاء. تكون طفرات الكائن القابل للتغيير داخل الوظيفة مرئية للمتصل لأنه لا يتم نسخ الكائن أو استنساخه — فهو مشترك ، ومن هنا جاء الاسم "استدعاء عن طريق المشاركة". [16]
تمت ملاحظة هذه التقنية لأول مرة بواسطة باربرا ليسكوف في عام 1974 للغة CLU . [16] يتم استخدامه من قبل العديد من اللغات الحديثة مثل بايثون (القيم المشتركة تسمى "الكائنات")، [41] جافا (الكائنات)، روبي (الكائنات)، جافا سكريبت (الكائنات)، المخطط (هياكل البيانات مثل المتجهات)، [42] AppleScript (القوائم والسجلات والتواريخ وكائنات البرنامج النصي)، وOCaml و ML (المراجع والسجلات والمصفوفات والكائنات وأنواع البيانات المركبة الأخرى)، وMaple (الجداول والجداول)، و Tcl (الكائنات). [43] المصطلح "الاتصال عبر المشاركة" كما هو مستخدم في هذه المقالة ليس شائع الاستخدام؛ المصطلحات غير متناسقة عبر مصادر مختلفة. على سبيل المثال، في مجتمع Java، يقولون أن Java يتم استدعاؤها حسب القيمة. [35]
بالنسبة للكائنات غير القابلة للتغيير ، لا يوجد فرق حقيقي بين الاستدعاء بالمشاركة والاستدعاء حسب القيمة، إلا إذا كانت هوية الكائن مرئية في اللغة. يعد استخدام الاستدعاء من خلال المشاركة مع الكائنات القابلة للتغيير بديلاً لمعلمات الإدخال/الإخراج : لا يتم تعيين المعلمة إلى (لا تتم الكتابة فوق الوسيطة ولا يتم تغيير هوية الكائن)، ولكن يتم تغيير الكائن (الوسيطة). [44]
على سبيل المثال، في لغة Python، تكون القوائم قابلة للتغيير ويتم تمريرها من خلال الاتصال بالمشاركة، لذلك:
def f(a_list):
a_list.append(1)
m = []
f(m)
print(m)
المخرجات [1]
لأن طريقة append
تعدل الكائن الذي يتم استدعاؤها عليه.
وفي المقابل، فإن التعيينات داخل الوظيفة ليست ملحوظة للمتصل. على سبيل المثال، يربط هذا الكود الوسيطة الرسمية بكائن جديد، لكنه غير مرئي للمتصل لأنه لا يغير a_list
:
def f(a_list):
a_list = a_list + [1]
print(a_list) # [1]
m = []
f(m)
print(m) # []
الاتصال عن طريق العنوان
عدليعد الاتصال حسب العنوان أو التمرير حسب العنوان أو الاتصال/التمرير بواسطة المؤشر طريقة تمرير معلمة حيث يتم تمرير عنوان الوسيطة كمعلمة رسمية. داخل الوظيفة، يمكن استخدام العنوان (المؤشر) للوصول إلى قيمة الوسيطة أو تعديلها. على سبيل المثال، يمكن تنفيذ عملية المبادلة على النحو التالي في C: [45]
#include <stdio.h>
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 1;
int b = 2;
swap(&a, &b);
printf("%d %d", a, b); // 2 1
return 0;
}
يتعامل بعض المؤلفين مع &
كجزء من بناء جملة استدعاء swap
. ضمن طريقة العرض هذه، تدعم لغة C استراتيجية تمرير معلمة الاستدعاء حسب المرجع. [46] يتبنى مؤلفون آخرون وجهة نظر مختلفة مفادها أن التنفيذ المقدم swap
في لغة C هو مجرد محاكاة للاستدعاء حسب المرجع باستخدام المؤشرات. [47] ضمن طريقة العرض "المحاكاة" هذه، فإن المتغيرات القابلة للتغيير في لغة C ليست من الدرجة الأولى (أي أن قيم l ليست تعبيرات)، بل هي أنواع المؤشر. في هذا العرض، يعد برنامج المبادلة المقدم عبارة عن سكر نحوي لبرنامج يستخدم المؤشرات طوال الوقت، [48] على سبيل المثال هذا البرنامج (تمت إضافة read
assign
لتسليط الضوء على أوجه التشابه مع برنامج Java Box
call-by-sharing أعلاه ):
#include <stdio.h>
int read(int *p) {
return *p;
}
void assign(int *p, int v) {
*p = v;
}
void swap(int* a, int* b) {
int temp_storage; int* temp = &temp_storage;
assign(temp, read(a));
assign(a, read(b));
assign(b, read(temp));
}
int main() {
int a_storage; int* a = &a_storage;
int b_storage; int* b = &b_storage;
assign(a,1);
assign(b,2);
swap(a, b);
printf("%d %d", read(a), read(b)); // 2 1
return 0;
}
نظرًا لأن swap
في هذا البرنامج تعمل على المؤشرات ولا يمكنها تغيير المؤشرات نفسها، ولكن فقط القيم التي تشير إليها المؤشرات، يرى هذا الرأي أن استراتيجية التقييم الرئيسية لـ C تشبه إلى حد كبير استراتيجية الاتصال بالمشاركة.
تعمل لغة C++ على إرباك المشكلة بشكل أكبر من خلال السماح بإعلان swap
واستخدامها مع بناء جملة "مرجعي" خفيف الوزن للغاية: [49]
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 1;
int b = 2;
swap(a, b);
std::cout << a << b << std::endl; // 2 1
return 0;
}
من الناحية الدلالية، هذا يعادل أمثلة لغة C. على هذا النحو، يعتبر العديد من المؤلفين أن المكالمة حسب العنوان هي استراتيجية تمرير معلمة فريدة تختلف عن المكالمة حسب القيمة، والمكالمة حسب المرجع، والمكالمة حسب المشاركة.
في البرمجة المنطقية ، قد يتوافق تقييم التعبير ببساطة مع توحيد المصطلحات المعنية مع تطبيق شكل ما من أشكال الحل . ويجب تصنيف التوحيد على أنه استراتيجية ملزمة صارمة لأنه يتم تنفيذه بالكامل. ومع ذلك، يمكن أيضًا إجراء التوحيد على متغيرات غير محدودة، لذلك قد لا تلتزم الاستدعاءات بالضرورة بالقيم النهائية لجميع متغيراتها.
استراتيجيات ملزمة غير صارمة
عدلاتصل بالاسم
عدلالاستدعاء بالاسم هو إستراتيجية تقييم حيث لا يتم تقييم وسيطات الدالة قبل استدعاء الدالة - بل يتم استبدالها مباشرة في نص الدالة (باستخدام استبدال تجنب الالتقاط ) ثم يتم تركها ليتم تقييمها كلما ظهرت في وظيفة. إذا لم يتم استخدام الوسيطة في نص الدالة، فلن يتم تقييم الوسيطة أبدًا؛ وإذا تم استخدامه عدة مرات، فسيتم إعادة تقييمه في كل مرة يظهر فيها. (انظر جهاز جنسن للتعرف على تقنية البرمجة التي تستغل هذا.)
يُفضل أحيانًا تقييم المكالمة حسب الاسم على تقييم المكالمة حسب القيمة. إذا لم يتم استخدام وسيطة الدالة في الوظيفة، فإن الاتصال حسب الاسم سيوفر الوقت من خلال عدم تقييم الوسيطة، في حين أن الاستدعاء حسب القيمة سيقيمها بغض النظر. إذا كانت الحجة عبارة عن حساب غير منتهٍ، فإن الميزة تكون هائلة. ومع ذلك، عند استخدام الوسيطة function، غالبًا ما يكون الاتصال بالاسم أبطأ، ويتطلب آلية مثل thunk .
لغات .NET يمكن محاكاة الاتصال بالاسم باستخدام المفوضين أو معلمات <Expression<T
. يؤدي الأخير إلى إعطاء شجرة بناء جملة مجردة للوظيفة. توفر إيفل وكلاء يمثلون عملية يتم تقييمها عند الحاجة. يوفر Seed7 الاتصال بالاسم مع معلمات الوظيفة. يمكن لبرامج Java إجراء تقييم بطيء مماثل باستخدام تعبيرات lambda java.util.function.Supplier<T>
interface.
الاتصل حسب الحاجة
عدليعد الاتصال حسب الحاجة أحد أشكال الاتصال بالاسم، حيث، إذا تم تقييم وسيطة الوظيفة، يتم تخزين هذه القيمة للاستخدام اللاحق. إذا كانت الوسيطة نقية (أي خالية من الآثار الجانبية)، فإن هذا ينتج نفس النتائج مثل الاتصال بالاسم، مما يوفر تكلفة إعادة حساب الوسيطة.
لغة هاسكل هي لغة معروفة تستخدم تقييم المكالمات حسب الحاجة. نظرًا لأن تقييم التعبيرات قد يحدث بشكل تعسفي بعيدًا في الحساب، فإن هاسكل يدعم فقط الآثار الجانبية (مثل الطفرة ) عبر استخدام المونادات . وهذا يلغي أي سلوك غير متوقع من المتغيرات التي تتغير قيمها قبل تقييمها المتأخر.
في تنفيذ R للاستدعاء حسب الحاجة، يتم تمرير كافة الوسائط، مما يعني أن R يسمح بتأثيرات جانبية عشوائية.
التقييم البطيء هو التطبيق الأكثر شيوعًا لدلالات الاستدعاء حسب الحاجة، ولكن توجد اختلافات مثل التقييم المتفائل . . تنفذ لغات NET الاتصال حسب الحاجة باستخدام النوع Lazy<T>
.
يعد تقليل الرسم البياني بمثابة تنفيذ فعال للتقييم البطيء.
الاتصال عن طريق توسيع الماكرو
عدليشبه الاتصال بتوسيع الماكرو الاتصال بالاسم، ولكنه يستخدم الاستبدال النصي بدلاً من الاستبدال مع تجنب الالتقاط . لذلك قد يؤدي استبدال الماكرو إلى التقاط متغير، مما يؤدي إلى أخطاء وسلوك غير مرغوب فيه. تتجنب وحدات الماكرو الصحية هذه المشكلة عن طريق التحقق من وجود المتغيرات المظللة التي ليست معلمات واستبدالها.
اتصل بالمستقبل
عدل"الاستدعاء بالمستقبل"، والمعروف أيضًا باسم "الاستدعاء الموازي بالاسم" أو "التقييم المتساهل"، [50] هو استراتيجية تقييم متزامنة تجمع بين الدلالات غير الصارمة والتقييم المتلهف. تتطلب هذه الطريقة جدولة ومزامنة ديناميكية دقيقة ولكنها مناسبة للأجهزة المتوازية على نطاق واسع.
تخلق الإستراتيجية مستقبلًا (وعدًا) لجسم الوظيفة وكل وسيطة منها. يتم حساب هذه العقود الآجلة بالتزامن مع تدفق بقية البرنامج. عندما يتطلب المستقبل A قيمة مستقبل B آخر لم يتم حسابه بعد، فإن المستقبل A يحجب حتى ينتهي المستقبل B من الحوسبة ويصبح له قيمة. إذا كان المستقبل B قد انتهى بالفعل من الحوسبة، فسيتم إرجاع القيمة على الفور. يتم حظر الشروط الشرطية حتى يتم تقييم حالتها، ولا تقوم مؤشرات lambda بإنشاء العقود الآجلة حتى يتم تطبيقها بالكامل. [51]
إذا تم تنفيذه باستخدام عمليات أو سلاسل رسائل، فإن إنشاء مستقبل سيؤدي إلى إنشاء واحدة أو أكثر من العمليات أو سلاسل العمليات الجديدة (للوعود)، وسيؤدي الوصول إلى القيمة إلى مزامنة هذه مع الخيط الرئيسي، ويتوافق إنهاء حساب المستقبل مع قتل الوعود التي تحسبها قيمة. إذا تم تنفيذه باستخدام coroutine ، كما في . NET async/await ، يؤدي إنشاء مستقبل إلى استدعاء coroutine (وظيفة غير متزامنة)، والتي قد تستسلم للمتصل، وبالتالي يتم إرجاعها إلى وقت استخدام القيمة، وتعدد المهام بشكل تعاوني.
الإستراتيجية غير حتمية، حيث أن التقييم يمكن أن يحدث في أي وقت بين خلق المستقبل (أي عندما يتم إعطاء التعبير) واستخدام قيمة المستقبل. الإستراتيجية غير صارمة لأن نص الوظيفة قد يُرجع قيمة قبل تقييم الوسائط. ومع ذلك، في معظم التطبيقات، قد يظل التنفيذ عالقًا في تقييم وسيطة غير ضرورية. على سبيل المثال، البرنامج
f x = 1/x
g y = 1
main = print (g (f 0))
قد يكون لديك g
إنهاء قبل f
، والإخراج 1، أو قد يؤدي إلى خطأ بسبب التقييم 1/0
. [29]
يشبه الاتصال بالمستقبل الاتصال حسب الحاجة حيث يتم حساب القيم مرة واحدة فقط. مع التعامل الدقيق مع الأخطاء وعدم الإنهاء، ولا سيما إنهاء العقود الآجلة في منتصف الطريق إذا تم تحديد أنها لن تكون هناك حاجة إليها، فإن المكالمة بمستقبل لها أيضًا نفس خصائص الإنهاء مثل تقييم المكالمة حسب الحاجة. [51] ومع ذلك، قد يؤدي الاستدعاء حسب المستقبل عملًا تخمينيًا غير ضروري مقارنةً بالاستدعاء حسب الحاجة، مثل التقييم العميق لبنية البيانات البطيئة. [29] يمكن تجنب ذلك باستخدام العقود الآجلة البطيئة التي لا تبدأ الحساب حتى يتم التأكد من أن القيمة مطلوبة.
تقييم متفائل
عدلالتقييم المتفائل هو متغير الاستدعاء حسب الحاجة حيث يتم تقييم وسيطة الوظيفة جزئيًا بأسلوب الاستدعاء حسب القيمة لفترة معينة من الوقت (والتي يمكن تعديلها في وقت التشغيل ). بعد مرور ذلك الوقت، يتم إلغاء التقييم ويتم تطبيق الوظيفة باستخدام الاستدعاء حسب الحاجة. [52] يتجنب هذا الأسلوب بعض نفقات وقت تشغيل المكالمة حسب الحاجة مع الاحتفاظ بخصائص الإنهاء المطلوبة.
أنظر أيضا
عدل- شكل بيتا العادي
- مقارنة بين لغات البرمجة
- تقييم
- حساب التفاضل والتكامل لامدا
- قيمة المكالمة عن طريق الدفع
- التقييم الجزئي
مراجع
عدل- ^ Araki، Shota؛ Nishizaki، Shin-ya (نوفمبر 2014). "Call-by-name evaluation of RPC and RMI calculi". Theory and Practice of Computation. ص. 1. DOI:10.1142/9789814612883_0001. ISBN:978-981-4612-87-6. مؤرشف من الأصل في 2023-11-27. اطلع عليه بتاريخ 2021-08-21.
- ^ Turbak, Franklyn; Gifford, David (18 Jul 2008). Design Concepts in Programming Languages (بالإنجليزية). MIT Press. p. 309. ISBN:978-0-262-30315-6. Archived from the original on 2023-11-27.
- ^ Crank، Erik؛ Felleisen، Matthias (1991). Parameter-passing and the lambda calculus. ص. 2. CiteSeerX:10.1.1.23.4385. DOI:10.1145/99583.99616. ISBN:0897914198. S2CID:5782416.
- ^ Wilhelm, Reinhard; Seidl, Helmut (10 Nov 2010). Compiler Design: Virtual Machines (بالإنجليزية). Springer Science & Business Media. p. 61. ISBN:978-3-642-14909-2. Archived from the original on 2023-11-27.
- ^ Nita، Stefania Loredana؛ Mihailescu، Marius (2017). "Introduction". Practical Concurrent Haskell. ص. 3. DOI:10.1007/978-1-4842-2781-7_1. ISBN:978-1-4842-2780-0.
- ^ Pierce، Benjamin C. (2002). Types and Programming Languages. مطبعة معهد ماساتشوستس للتقانة. ص. 56. ISBN:0-262-16209-1. مؤرشف من الأصل في 2024-05-16.
- ^ Daniel P. Friedman؛ Mitchell Wand (2008). Essentials of Programming Languages (ط. third). Cambridge, MA: The MIT Press. ISBN:978-0262062794.
- ^ ا ب Scott، Michael Lee (2016). Programming language pragmatics (ط. Fourth). Waltham, MA: Elsevier. ISBN:9780124104778.
- ^ "Avoid Unnecessary Copies of Data - MATLAB & Simulink". www.mathworks.com. مؤرشف من الأصل في 2024-03-13. اطلع عليه بتاريخ 2023-01-28.
- ^ Hasti، Rebecca. "Parameter Passing". CS 536: Introduction to Programming Languages and Compilers. University of Wisconsin. مؤرشف من الأصل في 2023-11-27. اطلع عليه بتاريخ 2021-08-22.
- ^ J.A. Robinson (يناير 1965). "A Machine-Oriented Logic Based on the Resolution Principle". Journal of the ACM. ج. 12 ع. 1: 23–41. DOI:10.1145/321250.321253. S2CID:14389185.; Here: sect.5.8, p.32
- ^ J.A. Robinson (1971). "Computational logic: The unification computation". Machine Intelligence. ج. 6: 63–72. مؤرشف من الأصل في 2024-01-28.
- ^ Bundy، Alan؛ Wallen، Lincoln (1984). "SASL". Catalogue of Artificial Intelligence Tools. ص. 117. DOI:10.1007/978-3-642-96868-6_222. ISBN:978-3-540-13938-6.
Was probably the first language to systematically exploit the power of lazy evaluation.
- ^ Fay، Colin (30 يوليو 2018). "About lazy evaluation". R-bloggers. مؤرشف من الأصل في 2024-04-30. اطلع عليه بتاريخ 2021-08-21.
- ^ Wadsworth، Christopher P. (1971). Semantics and Pragmatics of the Lambda Calculus (PhD thesis). Oxford University.
- ^ ا ب ج د ه Liskov، Barbara؛ Atkinson، Russ؛ Bloom، Toby؛ Moss، Eliot؛ Schaffert، Craig؛ Scheifler، Craig؛ Snyder، Alan (أكتوبر 1979). "CLU Reference Manual" (PDF). Laboratory for Computer Science. Massachusetts Institute of Technology. ص. 14–15. مؤرشف (PDF) من الأصل في 2006-09-22. اطلع عليه بتاريخ 2011-05-19.
- ^ "PHP: Passing by Reference - Manual". www.php.net. مؤرشف من الأصل في 2024-06-23. اطلع عليه بتاريخ 2021-07-04.
- ^ Wagner, Bill (12 Apr 2023). "Passing Parameters - C# Programming Guide". Microsoft Docs (بالإنجليزية الأمريكية). Archived from the original on 2024-06-08. Retrieved 2023-09-10.
- ^ Dollard, Kathleen (15 Sep 2021). "Passing Arguments by Value and by Reference - Visual Basic". Microsoft Docs (بالإنجليزية الأمريكية). Archived from the original on 2023-12-18. Retrieved 2023-09-10.
- ^ ا ب "History of C++". en.cppreference.com. مؤرشف من الأصل في 2024-06-24. اطلع عليه بتاريخ 2022-06-11.
- ^ Filipek, Bartlomiej (16 Aug 2021). "Stricter Expression Evaluation Order in C++17". C++ Stories (بالإنجليزية الأمريكية). Archived from the original on 2024-05-22. Retrieved 2021-08-24.
- ^ Abelson، Harold؛ Sussman، Gerald Jay (1996). "Normal Order and Applicative Order". Structure and interpretation of computer programs (ط. 2nd). Cambridge, Massachusetts: مطبعة معهد ماساتشوستس للتقانة. ISBN:0-262-01153-0. مؤرشف من الأصل في 2005-03-02. اطلع عليه بتاريخ 2006-03-06. See also footnote Temp 576.
- ^ Reese, Richard M. (14 Oct 2015). Learning Java Functional Programming (بالإنجليزية). Packt Publishing Ltd. p. 106. ISBN:978-1-78528-935-4. Archived from the original on 2023-11-27.
- ^ Antani, Ved; Timms, Simon; Mantyla, Dan (31 Aug 2016). JavaScript: Functional Programming for JavaScript Developers (بالإنجليزية). Packt Publishing Ltd. p. 614. ISBN:978-1-78712-557-5. Archived from the original on 2023-11-28.
- ^ Wilhelm, Reinhard; Seidl, Helmut (10 Nov 2010). Compiler Design: Virtual Machines (بالإنجليزية). Springer Science & Business Media. p. 61. ISBN:978-3-642-14909-2. Archived from the original on 2023-11-27.
- ^ Seacord، Robert C. "EXP30-C. Do not depend on the order of evaluation for side effects". SEI CERT C Coding Standard. Carnegie Mellon University. مؤرشف من الأصل في 2024-02-23. اطلع عليه بتاريخ 2021-08-23.
- ^ Anglade، S.؛ Lacrampe، J. J.؛ Queinnec، C. (أكتوبر 1994). "Semantics of combinations in scheme" (PDF). ACM SIGPLAN Lisp Pointers. ج. VII ع. 4: 15–20. DOI:10.1145/382109.382669. S2CID:2987427. مؤرشف من الأصل (PDF) في 2024-03-18.
- ^ "Why are OCaml function arguments evaluated right-to-left?". OCaml (بالإنجليزية). 30 Nov 2017. Archived from the original on 2024-03-03.
- ^ ا ب ج د ه Tremblay، G. (أبريل 2000). "Lenient evaluation is neither strict nor lazy". Computer Languages. ج. 26 ع. 1: 43–66. CiteSeerX:10.1.1.137.9885. DOI:10.1016/S0096-0551(01)00006-6.
- ^ George، Lai (مارس 1987). Efficient evaluation of normal order through strictness information (MSc thesis). University of Utah. ص. 10. مؤرشف من الأصل في 2023-11-27.
- ^ Borning، Alan (Autumn 1999). "Applicative vs Normal Order Evaluation in Functional Languages" (PDF). CSE 505: Concepts of Programming Languages. University of Washington. مؤرشف من الأصل (PDF) في 2024-03-01. اطلع عليه بتاريخ 2021-08-23.
- ^ Mazzola, Guerino; Milmeister, Gérard; Weissmann, Jody (21 Oct 2004). Comprehensive Mathematics for Computer Scientists 2 (بالإنجليزية). Springer Science & Business Media. p. 323. ISBN:978-3-540-20861-7. Archived from the original on 2024-06-26.
- ^ Abelson، Harold؛ Sussman، Gerald Jay (1996). "Normal Order and Applicative Order". Structure and interpretation of computer programs (ط. 2nd). Cambridge, Massachusetts: مطبعة معهد ماساتشوستس للتقانة. ISBN:0-262-01153-0. مؤرشف من الأصل في 2005-03-02. اطلع عليه بتاريخ 2006-03-06. See also footnote Temp 576.
- ^ ا ب Sturm, Oliver (11 Apr 2011). Functional Programming in C#: Classic Programming Techniques for Modern Projects (بالإنجليزية). John Wiley and Sons. p. 91. ISBN:978-0-470-74458-1. Archived from the original on 2023-11-27.
- ^ ا ب ج د "Java is Pass-by-Value, Dammit!". 16 مايو 2001. مؤرشف من الأصل في 2015-03-22. اطلع عليه بتاريخ 2016-12-24.
- ^ Coenen، Frans. "PARAMETER PASSING". cgi.csc.liv.ac.uk. مؤرشف من الأصل في 2024-06-26. اطلع عليه بتاريخ 2024-01-22.
- ^ "Call by Reference, Aliasing Issues" (PDF). MPRI Course 2-36-1: Proof of Program (Lecture notes). ص. 53.
- ^ Ada 2022 Language Reference Manual (PDF)، 13 أكتوبر 2023، ص. 215، مؤرشف من الأصل (PDF) في 2023-04-23
- ^ Barnes، John (2013). Ada 2012 rationale: the language, the standard libraries (PDF). Heidelberg: Springer. ص. 15-16,87-88. ISBN:978-3-642-45210-9. مؤرشف من الأصل (PDF) في 2024-02-05.
- ^ Thurlow، Robert (مايو 2009). "RPC: Remote Procedure Call Protocol Specification Version 2". tools.ietf.org. IETF. مؤرشف من الأصل في 2013-09-03. اطلع عليه بتاريخ 2018-04-07.
- ^ Lundh، Fredrik. "Call by Object". Effbot.org. مؤرشف من الأصل في 2011-05-19. اطلع عليه بتاريخ 2011-05-19.
- ^ Jones، Rhys Price (2010). "Is Scheme call-by-value?". CS 145 Programming Languages Lab 9: Parameter Passing. George Washington University. مؤرشف من الأصل في 2014-10-16. اطلع عليه بتاريخ 2024-01-20.
- ^ "Tcl Library Procedures - Tcl_Obj manual page". www.tcl.tk. مؤرشف من الأصل في 2023-12-02.
- ^ "CA1021: Avoid out parameters". Microsoft. 15 نوفمبر 2016. مؤرشف من الأصل في 2013-10-05.
- ^ Leo, Ray (Nov 1996). Little C++ (Made Easy) (بالإنجليزية). LeoSudo Inc. pp. 79–80. ISBN:978-0-9654634-1-6. Archived from the original on 2024-06-25.
- ^ Dandamudi, Sivarama P. (15 Jul 2005). Guide to Assembly Language Programming in Linux (بالإنجليزية). Springer Science & Business Media. p. 232. ISBN:978-0-387-25897-3. Archived from the original on 2024-06-25.
- ^ Srivastava, S. K.; Srivastava, Deepali (6 Jun 2018). C in Depth (بالإنجليزية). BPB Publications. p. 206. ISBN:978-93-87284-94-4. Archived from the original on 2024-06-25.
- ^ "Mutable Variables and Reference Types". okmij.org. اطلع عليه بتاريخ 2024-01-20.
- ^ Vermeir, Dirk (28 Jun 2011). Multi-Paradigm Programming using C++ (بالإنجليزية). Springer Science & Business Media. pp. 10–11. ISBN:978-1-4471-0311-0.
- ^ McCollin، Thomas Gwynfryn؛ Morell، Tobias. "A Game of Paradigms: A Usability Study of Functional Idioms in Gameplay Programming" (PDF). Aalborg University. ص. 6. اطلع عليه بتاريخ 2022-01-11.
- ^ ا ب Schauser، Klaus E.؛ Goldstein، Seth C. (1995). How much non-strictness do lenient programs require? (PDF). ص. 216–225. DOI:10.1145/224164.224208. ISBN:0897917197. S2CID:2045943. اطلع عليه بتاريخ 2022-01-07.
- ^ Ennals، Robert؛ Jones، Simon Peyton (أغسطس 2003). "Optimistic Evaluation: a fast evaluation strategy for non-strict programs".
قراءة متعمقة
عدل- Baker-Finch، Clem؛ King، David؛ Hall، Jon؛ Trinder، Phil (10 مارس 1999). "An Operational Semantics for Parallel Call-by-Need" (ps). Research Report. Faculty of Mathematics & Computing, The Open University. ج. 99 ع. 1.
- Ennals، Robert؛ Peyton Jones، Simon (2003). "Optimistic Evaluation: A Fast Evaluation Strategy for Non-Strict Programs" (PDF). International Conference on Functional Programming. ACM Press.
- Ludäscher، Bertram (24 يناير 2001). "CSE 130 lecture notes". CSE 130: Programming Languages: Principles & Paradigms.
- Pierce، Benjamin C. (2002). Types and Programming Languages. مطبعة معهد ماساتشوستس للتقانة. ISBN:0-262-16209-1.
- Sestoft، Peter (2002). Mogensen، T؛ Schmidt، D؛ Sudborough، I. H. (المحررون). Demonstrating Lambda Calculus Reduction (PDF). Lecture Notes in Computer Science. Springer-Verlag. ج. 2566. ص. 420–435. ISBN:3-540-00326-6.
{{استشهاد بكتاب}}
:|صحيفة=
تُجوهل (مساعدة) - "Call by Value and Call by Reference in C Programming". Call by Value and Call by Reference in C Programming explained. مؤرشف من الأصل في 2013-01-21.
روابط خارجية
عدل- مصور هندسة التفاعل التفاعلي عبر الإنترنت، والذي يقوم بتنفيذ آلة تعتمد على الرسم البياني للعديد من استراتيجيات التقييم الشائعة.