Различия между лексическими замыканиями Common Lisp и Scheme

В Common Lisp я могу оценить следующий фрагмент кода (на SBCL) без сообщения о какой-либо синтаксической ошибке:

(let ((x 0))
   (defun my-incf (y)
     (setf x (+ x y)))
   (defun my-decf (y)
     (setf x (- x y))))
MY-DECF

CL-USER> (my-incf 1)
1
CL-USER> (my-incf 1)
2
CL-USER> (my-decf 1)
1
CL-USER> (my-decf 1)
0

Когда я пытаюсь оценить соответствующий фрагмент кода Scheme (в DrRacket):

(let ((x 0))
  (define (my-incf y)
    (set! x (+ x y)))
  (define (my-decf y)
    (set! x (- x y))))

это сигнализирует о синтаксической ошибке.

begin (possibly implicit): no expression after a sequence of internal definitions in: (begin (define (my-incf y) (set! x (+ x y))) (define (my-decf y) (set! x (- x y))))

Кто-нибудь знает причину, по которой это нельзя сделать в Схеме?


person Paulo Tomé    schedule 26.06.2013    source источник
comment
Вам нужно будет посмотреть определение синтаксиса LET в DrRacket. Пример Common Lisp допустим, но я бы не стал использовать его в своем коде.   -  person Rainer Joswig    schedule 27.06.2013
comment
@Reiner Joswig Почему бы вам не использовать его в своем коде?   -  person Paulo Tomé    schedule 27.06.2013
comment
Вместо этого я бы использовал CLOS. Использование выше усложняет отладку и препятствует тому, чтобы форма DEFUN распознавалась компилятором как определение функции. Внутри LET DEFUN больше не является формой верхнего уровня.   -  person Rainer Joswig    schedule 27.06.2013
comment
@Rainer Joswig Не могли бы вы более подробно объяснить, как бы вы использовали CLOS для реализации приведенного выше кода?   -  person Paulo Tomé    schedule 27.06.2013
comment
разве это не очевидно? X — это слот в экземпляре CLOS. Тогда функции являются методами.   -  person Rainer Joswig    schedule 27.06.2013
comment
Конечно. Это просто.   -  person Paulo Tomé    schedule 27.06.2013
comment
Между прочим, что касается сходства того, что вы делаете, с тем, что вы можете сделать с помощью CLOS, Scheme изначально был изобретен как язык для изучения объектно-ориентированных идей — предположительно с использованием методов, аналогичных описанным ниже.   -  person Mars    schedule 28.06.2013
comment
Я бы не стал использовать ООП для решения простой проблемы модуля с глобальной переменной. Проблема функций, не являющихся формами верхнего уровня, может быть решена с помощью (defvar *counter* 0) и если функции ссылаются на *counter*. Подход ООП применяется, если модуль кричит, что я действительно должен быть объектом, поддерживающим множественное создание экземпляров, и все эти глобальные переменные должны быть моими переменными экземпляра. Если вы не ожидаете необходимости многократного создания экземпляров или каких-либо преимуществ наследования и тому подобного, придерживайтесь глобальных переменных. Динамические переменные — это способ Common Lispy делать глобальные переменные.   -  person Kaz    schedule 28.06.2013


Ответы (3)


Вы не можете определить привязки верхнего уровня за пределами верхнего уровня в Scheme. (И внутри let определенно находится за пределами верхнего уровня --- вместо этого у вас были внутренние определения, которые не экспортируются на верхний уровень.) Однако, используя define-values, вы все еще можете делать то, что вам нужно сделать:

(define-values (my-incf my-decf)
  (let ((x 0))
    (values (lambda (y)
              (set! x (+ x y))
              x)
            (lambda (y)
              (set! x (- x y))
              x))))

Однако вы все равно можете использовать внутренние определения, чтобы сделать ваш код более читабельным:

(define-values (my-incf my-decf)
  (let ((x 0))
    (define (my-incf y)
      (set! x (+ x y))
      x)
    (define (my-decf y)
      (set! x (- x y))
      x)
    (values my-incf my-decf)))

Лучшее из обоих миров. :-) В этом случае values отправляет внутренние определения my-incf и my-decf внешнему define-values, где и происходит настоящее определение верхнего уровня.

person Chris Jester-Young    schedule 26.06.2013
comment
Как учитель однажды сказал мне: Scheme — это современный Лисп. - person Paulo Tomé; 28.06.2013

Решение Криса - это то, что я бы использовал в этом случае, но вот еще одно в духе "Let over Lambda", которое может пригодиться, если вы увеличите количество операций над x:

(define inc-dec
  (let ((x 0))
    (lambda (msg y)
      (case msg
        ((incf) (set! x (+ x y)))
        ((decf) (set! x (- x y)))
        (else (error "wot?")))
      x)))


(inc-dec 'incf 1)
(inc-dec 'incf 1)
(inc-dec 'decf 1)
(inc-dec 'decf 1)
person uselpa    schedule 27.06.2013

Менее элегантно, но очень просто: определите переменные верхнего уровня, затем set! или setf лямбда-выражения из let, в зависимости от того, схема это или CL.

person Mars    schedule 28.06.2013