سؤال

لقد كنت أتساءل منذ فترة طويلة عن سبب فائدة التقييم البطيء.لم يشرح لي أحد بعد بطريقة منطقية؛في الغالب ينتهي الأمر بالغليان إلى "ثق بي".

ملحوظة:لا أقصد الحفظ.

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

المحلول

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

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

نصائح أخرى

وهناك مثال مفيد لتقييم كسول هو استخدام quickSort:

quickSort [] = []
quickSort (x:xs) = quickSort (filter (< x) xs) ++ [x] ++ quickSort (filter (>= x) xs)

إذا كنا نريد الآن للعثور على الحد الأدنى من القائمة، يمكن أن نحدد

minimum ls = head (quickSort ls)

والذي يفرز أول القائمة ثم يأخذ العنصر الأول من القائمة. ومع ذلك، لأن التقييم كسول، ويحصل على حسابها فقط الرأس. على سبيل المثال، إذا أخذنا الحد الأدنى من قائمة [2, 1, 3,] فرز سريع وتصفية لأول مرة جميع العناصر التي تكون أصغر من اثنين. ثم يفعل فرز سريع على ذلك (اعادة قائمة المفرد [1]) وهو بالفعل ما يكفي. بسبب تقييم كسول، يتم فرز باقي أبدا، وتوفير الكثير من الوقت الحسابية.

وهذا بالطبع مثال بسيط جدا، ولكن الكسل يعمل بنفس الطريقة للبرامج التي تكون كبيرة جدا.

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

وأجد تقييم كسول مفيد لعدد من الأمور.

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

واللغات الصرفة ندعك السبب حول تعريفات الدالة باستخدام المنطق انتصافي.

foo x = x + 3

ومما يؤسف له في وضع غير كسول، تفشل المزيد من البيانات للعودة من في إعداد كسول، لذلك هذا هو أقل فائدة في لغات مثل ML. ولكن في لغة كسول يمكنك العقل بأمان حول المساواة.

وثانيا، ليست في حاجة إلى الكثير من الاشياء مثل 'تقييد قيمة' في ML بلغات كسول مثل هاسكل. وهذا يؤدي إلى decluttering كبير من بناء الجملة. ML مثل اللغات تحتاج إلى استخدام كلمات رئيسية مثل فار أو متعة. في هاسكل تنهار هذه الأمور لفكرة واحدة.

وثالثا، الكسل يتيح لك كتابة التعليمات البرمجية وظيفية للغاية التي يمكن فهمها في قطعة. في هاسكل ومن الشائع لكتابة وظيفة الجسم مثل:

foo x y = if condition1
          then some (complicated set of combinators) (involving bigscaryexpression)
          else if condition2
          then bigscaryexpression
          else Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...

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

في الممارسة العملية، فإننا نميل إلى استخدام الحراس والانهيار التي تزيد من ل:

foo x y 
  | condition1 = some (complicated set of combinators) (involving bigscaryexpression)
  | condition2 = bigscaryexpression
  | otherwise  = Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...

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

والخامس، والكسل يسمح لك لتحديد هياكل المراقبة جديدة في اللغة. لا يمكنك إرسال بريد جديد "إذا .. ثم .. آخر .. مثل بناء بلغة صارمة. إذا حاولت تعريف دالة مثل:

if' True x y = x
if' False x y = y

وبلغة صارمة ثم سيتم تقييم كل من فروع بغض النظر عن قيمة الشرط. تزداد الأمور سوءا عندما تفكر في الحلقات. تتطلب كل الحلول صارمة اللغة لتزويدك نوعا من الاقتباس أو بناء امدا صريح.

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

وهناك فرق بين العادي التقييم أجل تقييم كسول (كما هو الحال في هاسكل).

square x = x * x

وتقييم التعبير التالي ...

square (square (square 2))

... مع تقييم حريصة:

> square (square (2 * 2))
> square (square 4)
> square (4 * 4)
> square 16
> 16 * 16
> 256

... مع تقييم النظام الطبيعي:

> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * (square (square 2))
> ((2 * 2) * (square 2)) * (square (square 2))
> (4 * (square 2)) * (square (square 2))
> (4 * (2 * 2)) * (square (square 2))
> (4 * 4) * (square (square 2))
> 16 * (square (square 2))
> ...
> 256

... مع تقييم كسول:

> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * ((square 2) * (square 2))
> ((2 * 2) * (2 * 2)) * ((2 * 2) * (2 * 2))
> (4 * 4) * (4 * 4)
> 16 * 16
> 256

وذلك لأن تقييم كسول ينظر إلى شجرة بناء الجملة ولا شجرة التحولات ...

square (square (square 2))

           ||
           \/

           *
          / \
          \ /
    square (square 2)

           ||
           \/

           *
          / \
          \ /
           *
          / \
          \ /
        square 2

           ||
           \/

           *
          / \
          \ /
           *
          / \
          \ /
           *
          / \
          \ /
           2

... في حين أن تقييم النظام الطبيعى فقط لا التوسعات النصية.

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

وتقييم كسول المتعلقة CPU بنفس الطريقة التي جمع القمامة المتعلقة RAM. GC يسمح لك التظاهر بأن لديك كمية غير محدودة من الذاكرة، وبالتالي طلب العديد من الأشياء في الذاكرة ما تحتاج إليه. سوف وقت استعادة الأشياء غير صالحة للاستعمال تلقائيا. LE يسمح لك التظاهر بأن لديك الموارد الحاسوبية غير محدودة - يمكنك القيام به العديد من الحسابات ما تحتاج إليه. وقت فقط لن تنفيذ لا لزوم لها (لإعطاء الحالة) الحسابية.

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

وتخيل أن لديك قائمة بأرقام S وعدد N. تحتاج إلى العثور على الأقرب إلى رقم N عدد M من قائمة S. هل يمكن أن يكون سياقين: واحد N وبعض ائحة L من نانوثانية (الصناعات الاستخراجية لكل N في L قمت بالبحث أقرب M في S). إذا كنت تستخدم تقييم كسول، يمكنك فرز S وتطبيق البحث الثنائي للعثور على أقرب M إلى N. للحصول على خطوات لواحد N و O (قانون الجنسية (حجم (S)) جيد كسول فرز سيتطلب O (حجم (S)) * (حجم (S) + حجم (L))) خطوات موزعة بالتساوي L. إذا لم يكن لديك تقييم كسول لتحقيق الكفاءة المثلى لديك لتنفيذ الخوارزمية في كل سياق.

إذا كنت تعتقد سيمون بيتون جونز، وتقييم كسول ليس مهما <م> في حد ذاته ولكن فقط ك "قميص الشعر" التي أجبرت المصممين للحفاظ على لغة نقية. أجد نفسي متعاطفا مع وجهة النظر هذه.

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

فكر في برنامج تيك تاك تو.وهذا له أربع وظائف:

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

وهذا يخلق فصلاً واضحًا ولطيفًا بين الاهتمامات.وعلى وجه الخصوص، فإن وظيفة توليد الحركة ووظائف تقييم اللوحة هي الوظائف الوحيدة التي تحتاج إلى فهم قواعد اللعبة:يمكن إعادة استخدام وظائف شجرة النقل وminimax بالكامل.

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

لكن في اللغة الكسولة، يتم إنشاء عناصر شجرة النقل فقط استجابةً لمتطلبات الدالة minimax:لا يلزم إنشاء شجرة النقل بأكملها قبل أن نترك الحد الأدنى يتحرر من العنصر العلوي.لذلك لا يزال هيكلنا المعياري النظيف يعمل في لعبة حقيقية.

وهنا نقطتان أخريان لا أعتقد أنه تم طرحهما في المناقشة بعد.

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

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

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

و"كسلان" تقييم وأداء العمليات عندما وكانت هناك حاجة إليهم. ومن المفيد عندما يكون سمة من سمات لغة البرمجة أو مكتبة لأنه من الصعب عموما لتنفيذ تقييم كسول بنفسك من مجرد precalculate كل شيء في خط الهجوم.

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

  2. فهو يتيح لك تحديد بنيات التحكم في التدفق في التعليمات البرمجية العادية على مستوى المستخدم، بدلاً من ترميزها بشكل ثابت في اللغة.(على سبيل المثال، جافا لديها for حلقات.هاسكل لديه for وظيفة.جافا لديها معالجة الاستثناءات؛لدى هاسكل أنواع مختلفة من الاستثناءات الأحادية.يحتوي C# على goto;لدى هاسكل الموناد المستمر...)

  3. يتيح لك فصل الخوارزمية عن توليد البيانات من الخوارزمية لاتخاذ القرار كم ثمن البيانات لتوليد.يمكنك كتابة دالة واحدة تولد قائمة لا نهائية من النتائج، ووظيفة أخرى تعالج أكبر قدر من هذه القائمة حسب ما تقرره.المزيد من التفاصيل، يمكنك الحصول عليها خمسة وظائف المولد و خمسة وظائف المستهلك، ويمكنك إنتاج أي مجموعة بكفاءة - بدلاً من ترميز 5 × 5 = 25 دالة يدويًا تجمع كلا الإجراءين في وقت واحد.(!) نعلم جميعًا أن الفصل أمر جيد.

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

النظر في هذا:

if (conditionOne && conditionTwo) {
  doSomething();
}

سيتم تنفيذ الأسلوب doSomething() فقط إذا كان الشرط One صحيحًا و الشرط اثنان صحيح.في حالة كون الشرط واحد خطأ، لماذا تحتاج إلى حساب نتيجة الشرط الثاني؟سيكون تقييم conditionTwo مضيعة للوقت في هذه الحالة، خاصة إذا كانت حالتك نتيجة لعملية ما.

هذا أحد الأمثلة على الاهتمام بالتقييم البطيء...

إحدى الفوائد الكبيرة للكسل هي القدرة على كتابة هياكل بيانات غير قابلة للتغيير بحدود مطفأة معقولة.مثال بسيط هو مكدس غير قابل للتغيير (باستخدام F#):

type 'a stack =
    | EmptyStack
    | StackNode of 'a * 'a stack

let rec append x y =
    match x with
    | EmptyStack -> y
    | StackNode(hd, tl) -> StackNode(hd, append tl y)

الكود معقول، لكن إلحاق مكدسين x وy يستغرق O(طول x) وقتًا في أفضل الحالات وأسوأها ومتوسطها.إن إلحاق مكدسين هو عملية متجانسة، فهي تمس جميع العقد في المكدس x.

يمكننا إعادة كتابة بنية البيانات كمكدس كسولة:

type 'a lazyStack =
    | StackNode of Lazy<'a * 'a lazyStack>
    | EmptyStack

let rec append x y =
    match x with
    | StackNode(item) -> Node(lazy(let hd, tl = item.Force(); hd, append tl y))
    | Empty -> y

lazy يعمل عن طريق تعليق تقييم التعليمات البرمجية في منشئه.بمجرد تقييمها باستخدام .Force(), ، يتم تخزين القيمة المرجعة مؤقتًا وإعادة استخدامها في كل مرة لاحقة .Force().

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

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

لا تتطلب بنية البيانات أعلاه إعادة حساب العقد عند كل اجتياز، لذا فهي تختلف بشكل واضح عن وحدات Vanilla IEnumerables في .NET.

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

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

ويستخدم هذا على سبيل المثال في في وظيفة تفعيل و أيضا في خوارزمية التعلم العكسي (I تتمكن من المشاركة سوى اثنين من الروابط، لذلك ستحتاج للبحث عن وظيفة learnPat في AI.Instinct.Train.Delta الوحدة النمطية نفسك). تقليديا على حد سواء تتطلب أكثر تعقيدا بكثير خوارزميات متكررة.

وهذا المقتطف ويبين الفرق بين التقييم كسول كسول ولا. بالطبع يمكن أن تكون هي نفسها الأمثل هذه الوظيفة فيبوناكسي واستخدام تقييم كسول بدلا من العودية، ولكن هذا من شأنه أن يفسد على سبيل المثال.

ودعونا نفترض أننا <م> MAY لديك لاستخدام الأرقام الأولى 20 عن شيء، مع عدم تقييم كسول وإلى أن تتولد جميع الأرقام 20 مقدما، ولكن، مع تقييم كسول أنها سوف تكون ولدت حسب الحاجة فقط. وبالتالي سوف تدفع فقط ثمن الحساب عند الحاجة.

وإخراج نموذج

Not lazy generation: 0.023373
Lazy generation: 0.000009
Not lazy output: 0.000921
Lazy output: 0.024205
import time

def now(): return time.time()

def fibonacci(n): #Recursion for fibonacci (not-lazy)
 if n < 2:
  return n
 else:
  return fibonacci(n-1)+fibonacci(n-2)

before1 = now()
notlazy = [fibonacci(x) for x in range(20)]
after1 = now()
before2 = now()
lazy = (fibonacci(x) for x in range(20))
after2 = now()


before3 = now()
for i in notlazy:
  print i
after3 = now()

before4 = now()
for i in lazy:
  print i
after4 = now()

print "Not lazy generation: %f" % (after1-before1)
print "Lazy generation: %f" % (after2-before2)
print "Not lazy output: %f" % (after3-before3)
print "Lazy output: %f" % (after4-before4)

وأشخاص آخرين أعطى بالفعل جميع الأسباب كبيرة، ولكن أعتقد أن ممارسة مفيدة للمساعدة في فهم المسائل لماذا الكسل هو محاولة وكتابة <لأ href = "http://en.wikipedia.org/wiki/Fixed_point_combinator" يختلط = "نوفولو"> نقطة ثابتة وظيفة بلغة صارمة.

في هاسكل، وهي وظيفة نقطة ثابتة فائقة سهلة:

fix f = f (fix f)

وهذا يوسع إلى

f (f (f ....

ولكن لهاسكل غير كسول، أن سلسلة لانهائية من حساب لا توجد مشكلة. ويتم تقييم "خارج إلى داخل"، ويعمل كل شيء رائع:

fact = fix $ \f n -> if n == 0 then 1 else n * f (n-1)

والأهم من ذلك، لا يهم لا أن fix يكون كسول، ولكن هذا <م> f يكون كسول. مرة واحدة كنت قد أعطيت بالفعل f صارمة، يمكنك إما رمي يديك في الهواء والتخلي، أو ايتا توسيعه وفوضى الاشياء. (وهذا هو الكثير مثل ما كان نوح قائلا عن كونها <م> مكتبة هذا الصارم / كسول، وليس لغة).

والآن تخيل كتابة نفس الوظيفة في سكالا صارم:

def fix[A](f: A => A): A = f(fix(f))

val fact = fix[Int=>Int] { f => n =>
    if (n == 0) 1
    else n*f(n-1)
}

ويمكنك بالطبع الحصول على تجاوز المكدس. إذا كنت تريد أن تعمل، تحتاج إلى جعل حجة f استدعاء كل حاجة:

def fix[A](f: (=>A) => A): A = f(fix(f))

def fact1(f: =>Int=>Int) = (n: Int) =>
    if (n == 0) 1
    else n*f(n-1)

val fact = fix(fact1)

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

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

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

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

bool Function(void) {
  if (!SubFunction1())
    return false;
  if (!SubFunction2())
    return false;
  if (!SubFunction3())
    return false;

(etc)

  return true;
}

وأو حل أكثر أناقة:

bool Function(void) {
  if (!SubFunction1() || !SubFunction2() || !SubFunction3() || (etc) )
    return false;
  return true;
}

وبمجرد البدء في استخدامه، سترى فرصا لاستخدامه أكثر فأكثر.

ودون تقييم كسول لن يسمح لك لكتابة شيء من هذا القبيل:

  if( obj != null  &&  obj.Value == correctValue )
  {
    // do smth
  }

ومن بين أمور أخرى، لغات كسول تسمح هياكل البيانات لانهائية متعددة الأبعاد.

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

والكسل مفيد لل نفسه هامش مشكلة ، ولكن من الجدير بالذكر اتصال coroutines المذكورة في هذا الرابط.

وتقييم كسلان هو المنطق انتصافي الرجل الفقير (والتي يمكن أن يتوقع، من الناحية المثالية، ليتم استنتاج خصائص رمز من خصائص أنواع والعمليات المعنية).

مثال حيث أنها تعمل بشكل جيد جدا: sum . take 10 $ [1..10000000000]. ونحن لا مانع يجري تخفيضها إلى مبلغ من 10 أرقام، بدلا من حساب الرقمية مباشرة وبسيطة واحدة فقط. دون تقييم كسول بالطبع هذا من شأنه أن يخلق قائمة عملاقة في الذاكرة فقط لاستخدام العناصر الأولى 10. سيكون بالتأكيد بطيئة للغاية، وربما تسبب خطأ خارج الذاكرة.

مثال حيث انها ليست كبيرة كما نود: sum . take 1000000 . drop 500 $ cycle [1..20]. التي سيتم جمعها في الواقع 000 1 000 أرقام، حتى لو كان في حلقة بدلا من قائمة. انه لا يزال <م> يجب يتم تخفيضها إلى حساب رقمي مباشر واحد فقط، مع عدد قليل الشرطية وبعض الصيغ. التي <م> لن يكون أفضل كثيرا ثم تلخيص 000 1 000 الأرقام. حتى لو كان في حلقة، وليس في قائمة (أي بعد الأمثل إزالة الغابات).


وشيء آخر هو، فإنه يجعل من الممكن لرمز في ذيل العودية سلبيات مودولو النمط، و<م> يعمل فقط .

وقوات التحالف. المتعلقة الجواب .

وإذا كان عن طريق "تقييم كسول" تقصد مثل في القيم المنطقية combound، كما في

   if (ConditionA && ConditionB) ... 

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

وإذا OTOH، يعني ما عرفت باسم "المهيآت كسول"، كما في:

class Employee
{
    private int supervisorId;
    private Employee supervisor;

    public Employee(int employeeId)
    {
        // code to call database and fetch employee record, and 
        //  populate all private data fields, EXCEPT supervisor
    }
    public Employee Supervisor
    { 
       get 
          { 
              return supervisor?? (supervisor = new Employee(supervisorId)); 
          } 
    }
}

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

الوظائف العليا

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

ودعونا العثور على أكبر عدد تحت 100،000 هذا القسمة على 3829.   للقيام بذلك، ونحن سوف عليك تصفية مجموعة من الاحتمالات التي نعرف   الحل يكمن.

largestDivisible :: (Integral a) => a  
largestDivisible = head (filter p [100000,99999..])  
    where p x = x `mod` 3829 == 0 
<اقتباس فقرة>   

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

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