سؤال

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

(define flip
  (let ((count 0))
    (lambda ()
      (if (= 0 count)
          (begin (set! count 1) count)
          (begin (set! count 0) count)))))

لماذا يلبي هذا الرمز بين 1 و 0؟ يتم إعطاء عدد قيمة 0 في كل مرة تسمى هذه الوظيفة! ما يعادل ثعبان سيكون:

class Flip:
    def __init__(self):
        pass
    def __call__(self):
        count = 0
        if count == 0:
            count = 1
            return count
        else:
            count = 0
            return count

هذا يعود نفس الشيء في كل مرة. انا مرتبك...

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

المحلول

لدي خبرة صغيرة مع محامرة الكتابة لغات وظيفية، لذلك ربما وصف قصير لكيفية تخزين / تم تمثيل هذه الوظيفة في الذاكرة بالترتيب. كل وظيفة يمكن اعتبارها تقريبا كزوج (E، F) حيث E هو مجموعة المتغيرات المجانية، و FE هو "رمز" الوظيفة نفسها. عند استدعاء الوظيفة، يستغرق الأمر القيم الموجودة في E وتدعية تلك الموجودة في المتغيرات في F، ثم تنفذ التعليمات البرمجية باستخدام هذه القيم.

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

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

نصائح أخرى

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

الوظيفة التي يتم استدعاؤها هي Lambda، وليس "الوجه". الوجه هو مجرد اسم قدمته لشركة Lambda التي عادت من (اسمحوا ...) التعبير.

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

لأن وظيفة الوجه الخاصة بك في الواقع إرجاع وظيفة (الذي يتم تعريفه داخل Lambda)

في كل مرة تقوم فيها بالاتصال بالوظيفة المرتجعة، فإنها تعدل بيئتها.

إذا كنت تفكر في ذلك يترك ينشئ البيئة (وتهيئة العد إلى 0) مرة واحدة فقط - عندما يتم إرجاع وظيفة Lambda إليك.

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

إذا كنت ترغب في وضع معلومات كاملا، فعليك أن تناسبك، يجب أن تقرأ عنه نموذج بيئية.

انها أشبه

class Flip:
    def __init__(self):
        self.count = 0
    def __call__(self):
        if self.count == 0:
            self.count = 1
            return self.count
        else:
            self.count = 0
            return self.count

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

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

سيكون الترجمة الحرفية أكثر إلى Python 3.x شيء من هذا القبيل (لاحظ أنه هو تقريبي فقط مثل Python ليس لديه let (محدود النطاق المحلي للإعلان) بناء جملة، وبيثون lambdaلا يمكن استخدام S لأنهم لا يأخذون بيانات):

count = 0

def flip():
    nonlocal count
    if count == 0:
        count = 1
        return count
    else:
        count = 0
        return count

# pretend count isn't in scope after this

المشكلة في التعليمات البرمجية الأصلية هي أنه له تأثير قوي على الأسلوب الضروري. سيكون الحل الاصطحام أكثر:

(define (flip)
  (let ((flag #t))
    (lambda ()
      (set! flag (not flag))
      (if flag 1 0))))

للإجابة على السؤال في التعليق OOBOO، تريد وظيفة تعرض وظيفة

(define make-flipper
  (lambda ()
    (let ((count 0))
      (lambda ()
    (let ((v count))
      (set! count (- 1 count))
      v)))))

;; test it
(let ((flip-1 (make-flipper)))
  (format #t "~s~%" (flip-1))  
  (format #t "~s~%" (flip-1))
  (format #t "~s~%" (flip-1))

  (let ((flip-2 (make-flipper)))
    (format #t "~s~%" (flip-2))
    (format #t "~s~%" (flip-2))
    (format #t "~s~%" (flip-2))))

يمكنك تغيير السرعة بشكل تافه! خط بحيث يصبح عدادا وليس زعما (أكثر فائدة).

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