Frage

I'm new to lisp and going through ANSI Common Lisp by Paul Graham and one of the exercises is to define a function like apply where any number printed out before it returns will be printed by default in octal.

I tried the following:

(let ((*print-base* 8))
  (defun like-apply (&rest args)
    (apply #'apply args)))

But it didn't work as expected:

(like-apply #'princ '(8)); returns 8 8 (expecting 10 8)

The following however works:

(defun apply8 (&rest args)
  (let ((*print-base* 8))
    (apply #'apply args)))

Returns correctly:

(apply8 #'princ '(8)); returns 10 8 (as expected)

So my question is why does the second example work, but not the first one? Both seem to manipulate the *print-base* variable.

War es hilfreich?

Lösung

Common Lisp uses let to bind both lexical and "special" (dynamic) variables. The behavior you expected was lexical and the behavior you observed was dynamic. The printer variables are all special, and so let creates a dynamic binding for them.

The printer variables are sometimes used in examples of why dynamic binding can be useful. For instance the fact that you can control the behavior of princ by binding *print-base* is enabled by dynamic binding, otherwise princ would reference the binding of *print-base* active when princ was defined.

This behavior is a major reason many Common Lisp programmers are adamant about the *earmuffs* naming convention for special variables. Note that defvar and defparameter both create special variables.

Andere Tipps

The behavior you observe is correct.

It will be instructive to compare

(let ((*print-base* 8))
  (defun f1 ()
    *print-base*))

with

(defun f2 ()
  (let ((*print-base* 8))
    *print-base*))

to discover that (f1) returns 10 while (f2) returns 8.

This is because *print-base* is bound around the definition of f1, so it is 8 while f1 is being defined but not while it is being executed.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top