سؤال

لقد حاولت عدة مرات لفهم مفهوم متابعات و دعوة/cc.كل واحد كان محاولة فاشلة.هلا شرح لي هذه المفاهيم من الناحية المثالية مع أكثر واقعية أمثلة من هذه على ويكيبيديا أو في غير ذلك من الوظائف.

لدي خلفية في البرمجة على شبكة الإنترنت ، OOP.أفهم أيضا 6502 الجمعية قاصر randez-vous مع إرلانج.ومع ذلك لا زلت لا أستطيع أن أحدد الدعوة/cc.

هل كانت مفيدة؟

المحلول

وانظروا، لقد وجدت هذه اجتياز أفضل وصف نمط على هذا الموضوع.

وهنا هو تجريده من التفاصيل نسخ من تلك المادة:

<اقتباس فقرة>   

المؤلف: Marijn Haverbeke   تاريخ: 24 يوليو 2007

     

ومخطط وظيفة مكالمة مع التيار متابعة يجعل من الممكن للقبض على الحساب، حالة من مكدس الاستدعاءات كما انها كانت، واستئناف تلك الدولة نفسها في وقت لاحق. على رأس هذا بدائي، شكل مختلف من التعامل مع استثناء والحيل longjmp C تشبه يمكن تنفيذها.

function traverseDocument(node, func) {
  func(node);
  var children = node.childNodes;
  for (var i = 0; i < children.length; i++)
    traverseDocument(children[i], func);
}   

function capitaliseText(node) {
  if (node.nodeType == 3) // A text node
    node.nodeValue = node.nodeValue.toUpperCase();
}

traverseDocument(document.body, capitaliseText);
     

ويمكن أن تتحول هذه على النحو التالي: نحن نضيف حجة اضافية لكل وظيفة، والتي سيتم استخدامها لتمرير استمرار وظيفة و. هذا استمرار هو قيمة وظيفة تمثل الإجراءات التي يجب أن يحدث بعد وظيفة "يعود". و(الدعوة) كومة يصبح عفا عليها الزمن في الاسلوب استمرار عابرة - عندما تدعو وظيفة وظيفة أخرى، وهذا هو آخر شيء يفعل. بدلا من انتظار وظيفة دعا للعودة، فإنه يضع أي العمل الذي يريد القيام به بعد ذلك إلى استمرار، والذي يمرر إلى وظيفة.

function traverseDocument(node, func, c) {
  var children = node.childNodes;
  function handleChildren(i, c) {
    if (i < children.length)
      traverseDocument(children[i], func,
                       function(){handleChildren(i + 1, c);});
    else
      c();
  }
  return func(node, function(){handleChildren(0, c);});
}

function capitaliseText(node, c) {
  if (node.nodeType == 3)
    node.nodeValue = node.nodeValue.toUpperCase();
  c();
}

traverseDocument(document.body, capitaliseText, function(){});
     

وتخيل لدينا وثيقة huuuuge للاستفادة. مجرد عبور في آن واحد يأخذ خمس ثوان، وتجميد المتصفح لمدة خمس ثوان هو أسلوب سيء نوعا ما. النظر في هذا التعديل البسيط للcapitaliseText (لا تولي اهتماما لالقبيح العالمي):

var nodeCounter = 0;
function capitaliseText(node, c) {
  if (node.nodeType == 3)
    node.nodeValue = node.nodeValue.toUpperCase();

  nodeCounter++;
  if (nodeCounter % 20 == 0)
    setTimeout(c, 100);
  else
    c();
}
     

والآن، كل عشرين العقد، تمت مقاطعة حساب لمئة ميلي ثانية لإعطاء واجهة المتصفح لحظة للرد على إدخال المستخدم. وهناك شكل بدائي جدا من خيوط - حتى يمكنك تشغيل حسابات متعددة في نفس الوقت مثل هذا

     

ويرتبط تطبيق أكثر فائدة عادة من هذا XMLHttpRequests، أو IFRAME مختلف وSCRIPT العلامة الخارقة استخدامها لمحاكاة لهم. تتطلب هذه دائما واحدة للعمل مع بعض نوع من آلية الدعوة مرة أخرى إلى التعامل مع البيانات التي يرسل الملقم مرة أخرى. في الحالات البسيطة، فإن وظيفة تافهة القيام به، أو عدد قليل من غلوبالس يمكن استخدامها لتخزين حالة احتساب التي يجب استؤنفت بعد يأتي البيانات مرة أخرى. مع الحالات المعقدة، على سبيل المثال عندما يتم استخدام البيانات بواسطة وظيفة يجب أن يعود بعض قيمة إلى المتصل به، استمرارا تبسيط الأمور إلى حد كبير. كنت مجرد تسجيل استمرار مثل هذه الدعوة مرة أخرى، وتتم متابعة الحساب الخاص بك عندما ينتهي الطلب.

نصائح أخرى

لمقارنته C، استمرار الحالي هو مثل الحالة الراهنة للالمكدس. وهو يحتوي على جميع وظائف انتظار نتيجة الدالة الحالية لإنهاء حتى يتمكنوا من استئناف التنفيذ. يستخدم المتغير التي تم التقاطها في استمرار الحالي مثل وظيفة، إلا أنه يأخذ القيمة المقدمة ويعود ذلك إلى المكدس الانتظار. يشبه هذا السلوك إلى C وظيفة longjmp حيث يمكنك العودة إلى الأجزاء السفلى من المكدس فورا .

(define x 0) ; dummy value - will be used to store continuation later

(+ 2 (call/cc (lambda (cc)
                (set! x cc)  ; set x to the continuation cc; namely, (+ 2 _)
                3)))         ; returns 5

(x 4) ; returns 6

واحد الفرق الرئيسي بين كومة C واستمرار هو أن استمرار يمكن استخدامها في أي لحظة في البرنامج، حتى إذا تم تغيير حالة المكدس. هذا يعني أنه يمكنك استعادة أساسا الإصدارات السابقة من المكدس واستخدامها مرارا وتكرارا، مما يؤدي إلى بعض تدفق برنامج فريد من نوعه.

(* 123 (+ 345 (* 789 (x 5)))) ; returns 7

  reason: it is because (x 5) replaces the existing continuation,
          (* 123 (+ 345 (* 789 _))), with x, (+ 2 _), and returns
          5 to x, creating (+ 2 5), or 7.

والقدرة على حفظ واستعادة حالة وجود برنامج لديها الكثير من القواسم المشتركة مع خاصية تعدد. في الواقع، يمكنك تنفيذ الخاص بك موضوع جدولة الخاصة باستخدام استمرارا، ولقد حاول توضيح <لأ href = "https://stackoverflow.com/questions/40632/i-just-dont-get-continuations/40736#40736 "> هنا .

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

مشيرا إلى الجمعية الخلفية ، استمرار الدولة التقاط تفاصيل مثل مؤشر التعليمة, سجلات كومة سياق (المؤشر), أن يكون حفظ و استعادة في الإرادة.

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

محاولة تصور هذا المفهوم في لغة مثل C, تخيل وجود حلقة واحدة كبيرة مع واحد switch(continuation_point) { case point1: ... } البيان, حيث كل case يتوافق مع استمرار-لنقطة حفظ ، حيث البرمجية داخل كل case يمكن أن يغير قيمة continuation_point و التخلي عن السيطرة على هذا continuation_point قبل breakجي من switch وإشراك التكرار التالي في الحلقة.

ما هو سياق سؤالك ؟ أي سيناريوهات معينة كنت مهتما ؟ أي برامج اللغة ؟ هو الخيط/الألياف المثال أعلاه كافية ؟

والشيء الذي ساعدني هو فكرة أن في اللغة التقليدية مع وظيفة يدعو لك ضمنيا تمرير استمرار أي وقت قمت بإجراء استدعاء دالة.

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

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

وعلى أساس تحرير تعليق:

واستمرار هو حالة إعدام كاملة. في أي لحظة التنفيذ يمكنك تقسيم البرنامج الى قسمين (في الوقت المناسب، وليس الفضاء) - وهو الذي كان يدير إلى هذه النقطة، وعلى كل ما يحدث للتشغيل من هنا. و"استمرار الحالي" هو "كل ما يجري لتشغيل من هنا" (يمكنك التفكير في الأمر وكأنه نوع من وظيفة من شأنها أن تفعل كل شيء من شأنه أن بقية البرنامج قد فعلت). وبالتالي فإن وظيفة لك تزويد call/cc يحصل مرت استمرار الحالية عند تم استدعاء call/cc. وظيفة يمكن استخدامها استمرار في العودة تنفيذا للبيان call/cc (على الأرجح على الرغم من أنها سوف تمر استمرار في جميع أنحاء لشيء آخر، لأنه إذا كان استخدامه مباشرة يمكن أن تفعل عودة بسيطة بدلا من ذلك).

وعندما كنت في محاولة لفهم الدعوة / سم مكعب، لقد وجدت هذا <وأ href = "http://community.schemewiki.org/؟call-with-current-continuation-for-C-programmers" يختلط = "نوفولو noreferrer "> الاتصال مع المتداول متابعة ل-C-المبرمجين الصفحة ومفيدة.

أفضل شرح رأيت في بول غراهام الكتاب ، على Lisp.

تخيل السيناريو الخاص بك هو لعبة فيديو المرحلة.دعوة/cc هو مكافأة المرحلة.

parellel between bonus stage and call/cc

في أقرب وقت كما كنت على اتصال به أنت نقلها إلى مرحلة المكافأة (أيتعريف وظيفة مرت حجة على الدعوة/cc [f في هذه الحالة]).

مكافأة مراحل مختلفة من المراحل العادية لأنه عادة ما يكون عنصر (أيالحجة الدالة التي تم تمريرها إلى استدعاء/cc) أنه إذا كنت على اتصال به تفقد و يتم نقلها مرة أخرى إلى مرحلة طبيعية.

parellel between exit bonus stage and call/cc function args

لذلك لا يهم إذا كان هناك العديد من args, عندما تصل إلى واحد منهم أكثر.بحيث تصل إلى الإعدام (arg 42) ثم يعود إلى مجموع (+ 42 10).

أيضا هناك بعض الملاحظات يستحق أن يلاحظ:

  • ليست كل الوظائف يمكن استخدامها مع الدعوة/cc.لأنه يتوقع استمرار (التي هي وظيفة) ، لا يمكن أن يكون و من هذا القبيل:(define f (lambda (k) (+ k 42)) لأنه لا يمكن sum a وظيفة.
  • أيضا لا يمكن أن يكون (define f (lambda (k) (f 42 10))) لأن استمرار تتوقع إلا حجة واحدة.
  • قد تنتهي دون touching أي الخروج في هذه الحالة وظيفة العائدات مثل أي وظيفة عادية (مثلا ، (define f (lambda (k) 42) التشطيبات عوائد 42).

وهناك مستويات متعددة لفهم الدعوة / سم مكعب. تحتاج أولا إلى فهم المصطلحات وكيفية عمل آلية. ثم فهم كيف ومتى يستخدم الاتصال / سم مكعب في "الحياة الحقيقية" وهناك حاجة البرمجة.

ويمكن الوصول إلى المستوى الأول من خلال دراسة CPS، ولكن هناك البدائل.

لمستوى الثاني أوصي الكلاسيكية التالية فريدمان.

ودانيال P. فريدمان. "تطبيقات الإستمرار: دعوة دروس". 1988 مبادئ لغات البرمجة (POPL88). يناير 1988.

ونلقي نظرة على الوصف وتنفيذ دعوة / سم مكعب لFScheme: <لأ href = "http://blogs.msdn.com/b/ashleyf/archive/2010/02/11/turning-your-brain -inside-خارج مع continuations.aspx "يختلط =" نوفولو "> http://blogs.msdn.com/b/ashleyf/archive/2010/02/11/turning-your-brain-inside-out-with -continuations.aspx

ونموذج استعملتها لفهم استمرارا من وجهة نظر حتمية هو أنه نسخة من الدعوة كومة جنبا إلى جنب مع مؤشر التعليمة التالية.

ودعوة / سم مكعب يدعو وظيفة (مرت كحجة) مع استمرار كحجة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top