سؤال

أنا أعمل حاليًا من خلال SICP باستخدام Guile كلغة أساسية للتمارين. لقد وجدت سلوكًا غريبًا أثناء تنفيذ التمارين في الفصل 3.5. لقد استنسخت هذا السلوك باستخدام Guile 1.4 و Guile 1.8.6 و Guile 1.8.7 على مجموعة متنوعة من المنصات وأنا متأكد من أنه ليس خاصًا بإعدادتي.

يعمل هذا الرمز بشكل جيد (ويحسب ه):

  (define y (integral (delay dy) 1 0.001))
  (define dy (stream-map (lambda (x) x) y))
  (stream-ref y 1000)

الرمز التالي ينبغي إعطاء نتيجة متطابقة:

  (define (solve f y0 dt)
    (define y (integral (delay dy) y0 dt))
    (define dy (stream-map f y))
    y)
  (stream-ref (solve (lambda (x) x) 1 0.001) 1000)

لكنه يعطي رسالة الخطأ:

standard input:7:14: While evaluating arguments to stream-map in expression (stream-map f y):
standard input:7:14: Unbound variable:
y ABORT: (unbound-variable)

لذلك عند تضمينه في تعريف الإجراء ، لا يعمل (تحديد y ...) ، بينما يعمل خارج الإجراء في البيئة العالمية عند الاستراحة بشكل جيد.

ماذا أفعل خطأ هنا؟ يمكنني نشر الرمز الإضافي (أي ، تعريفات التكامل ، الدفق الخريطة وما إلى ذلك) إذا لزم الأمر أيضًا. باستثناء التعليمات البرمجية المعتمدة على النظام لـ Cons-Stream ، فهي جميعها في الكتاب. إن تنفيذي الخاص لـ Cons-Tream for Guile هو كما يلي:

(define-macro (cons-stream a b)
  `(cons ,a (delay ,b)))
هل كانت مفيدة؟

المحلول

الفرق الرئيسي بين ما يحدث عند تقييم التعريفات الواحدة تلو الأخرى في الاستبدال وعندما تضعها في الداخل solve هو أنه في الحالة الأولى ، يتم تقييمها بالتتابع ، وبالتالي ال y التعبير (stream-map <some-function> y) يشير إلى النطاق بالفعل ، بينما مع التعاريف الداخلية أو letrec, ، ليس متاحا بعد.

بشكل ممتع ، مخطط معهد ماساتشوستس للتكنولوجيا ، والذي استخدمته عند مرور SICP ، لم يكن لديه مثل هذه المشكلة في ذلك الوقت ولا يزال يعامل letrec ويحدد الداخلية بشكل مختلف:

;; this is an error
(letrec ((xs '(1 2 3)) (ys (map (lambda (x) (+ x 1)) xs))) ys)

;; this is still an error (and is treated as such by Guile),
;; yet evaluates to (2 3 4) in MIT Scheme
(let () (define xs '(1 2 3)) (define ys (map (lambda (x) (+ x 1)) xs)) ys)

لست متأكدًا من "التقرير المنقح عن مخطط اللغة الخوارزمي" أو R2Rs ، ولكن من بين R3Rs على التعريفات الداخلية كان من المفترض أن تكون مكافئة لـ letrec. يبدو أن خصوصية بيئة معهد ماساتشوستس للتكنولوجيا قد أثرت على الكتاب ... أو ربما يكون ذلك هو العكس.

نصائح أخرى

لا يمكن أن يكون لديك تعريف داخلي يعتمد على بعضكما البعض ؛ تنص مواصفات اللغة صراحة على هذا (R5RS 5.2.2):

... يجب أن يكون من الممكن تقييم كل منها التعبير من كل تعريف داخلي في أ هيئة دون تعيين أو الإشارة إلى قيمة أي عامل يجري تعريفها.

يمكنك التفكير في هذا كما لو أن المترجم المترجم يقوم بجمع جميع التعريفات وتقييمها قبل الجسم بترتيب عشوائي. نظرًا لأن الطلب عشوائي ، لا يمكن أن يكون هناك أي ترابط إذا كنت تتوقع أن يعمل.

حتى أن هناك حاشية مرفقة بتعريف الحل (#71) تقول إنها لن تعمل على جميع المخططات.

يجب عليك كتابة الكود بحيث يكون تعريف واحد بوضوح شديد في نطاق الآخر ، كما هو الحال مع المتداخلة يتيح:

(define (solve f y0 dt)
  (let ((y (integral (delay dy) y0 dt)))
    (let ((dy (stream-map f y)))
      y)))

باتباع الفكرة في التعليقات (في إشارة إلى الاقتباس من R5RS 4.2.2) ، لقد قمت الآن بلف تعريفات "" التعريفات "y" و "dy" داخل (lambda () ...)س:

  (define (solve f y0 dt)
    (define (y) (integral (delay (dy)) y0 dt))
    (define (dy) (stream-map f (y)))
    (y))

هذا يتأكد من أن <init> يمكن تقييم جزء من كل تعريف دون الإشارة إلى المتغيرات الدائرية المحددة نظرًا لأن التعاريف هي إجراءات بدلاً من التعبيرات مع متغيرات أخرى كما في الحالة الأصلية.

الآن أصبح الرمز أبطأ كثيرًا (حيث سيتم لف الوظائف بشكل متكرر) ويجب زيادة حجم المكدس ، ولكن الأعمال التالية وتنتج النتيجة الصحيحة:

  (debug-set! stack 2000000)
  (stream-ref (solve (lambda (x) x) 1 0.001) 1000)

مع تعديل مماثل ، يعمل رمز مثال Michał بمجرد تحديد الإجراءات بدلاً من المتغيرات:

  (let ()
    (define (xs) '(1 2 3))
    (define (ys) (map (lambda (x) (+ x 1)) (xs)))
    (ys))

يعمل على Guile 1.8.6.

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