Вопрос

In Common Lisp I can evaluate the following snippet of code (in SBCL) without being signalled any syntax error:

(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

When I try to evaluate a corresponding Scheme snippet of code (in DrRacket):

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

it signals a syntax error.

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))))

Does anybody know the reason why this cannot be done in Scheme?

Это было полезно?

Решение

You can't define top-level bindings outside of the top-level, in Scheme. (And inside of a let is definitely outside of the top-level---what you had, instead, was internal definitions, which are not exported to the top-level.) However, using define-values, you can still do what you need to do:

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

However, you can still use internal definitions, to make your code more readable:

(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)))

Best of both worlds. :-) In this case, the values sends the internal my-incf and my-decf definitions to the outer define-values, which is where the real top-level definition happens.

Другие советы

Chris' solution is what I would use in this case, but here's another one in the spirit of "Let over Lambda" which can come in handy if you increase the number of operations on 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)

Less elegant, but very simple: Define top-level variables, then set! or setf them to lambdas from inside the let, depending on whether it's Scheme or CL.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top