Reputation: 150614
I'm currently reading the book Land of LISP
, and I'm just working through the first chapter. In there, there is a little program written where the computer guesses numbers between 1 and 100. Its code is as follows:
(defparameter *small* 1)
(defparameter *big* 100)
(defun guess-my-number ()
(ash (+ *small* *big*) -1))
(defun smaller ()
(setf *big* (1- (guess-my-number)))
(guess-my-number))
(defun bigger ()
(setf *small* (1+ (guess-my-number)))
(guess-my-number))
(defun start-over ()
(defparameter *small* 1)
(defparameter *big* 100)
(guess-my-number))
So far, I understand what happens, and Using 'ash' in LISP to perform a binary search? helped me a lot in this. Nevertheless there's one thing left that puzzles me: As far as I have learned, you use setf
to assign values to variables, and defparameter
to initially define variables. I also have understood the difference between defparameter
and defvar
(at least I believe I do ;-)).
So now my question is: If I should use setf
to assign a value to a variable once it had been initialized, why does the start-over
function use defparameter
and not setf
? Is there a special reason for this, or is this just sloppiness?
Upvotes: 4
Views: 2338
Reputation: 139251
The function is just:
(defun start-over ()
(setf *small* 1)
(setf *big* 100)
(guess-my-number))
It is already declared to be a special global variable. No need to do it inside the function again and again.
You CAN use DEFPARAMETER
inside a function, but it is bad style.
DEFPARAMETER
is for declaring global special variables and optional documentation for them. Once. If you need to do it several times, it's mostly done when a whole file or system gets reloaded. The file compiler also recognizes it in top-level position as a special declaration for a dynamically bound variable.
Example:
File 1:
(defparameter *foo* 10)
(defun foo ()
(let ((*foo* ...))
...))
File 2:
(defun foo-start ()
(defparameter *foo* 10))
(defun foo ()
(let ((*foo* ...))
...))
If Lisp compiles File 1 fresh with compile-file
, the compiler recognizes the defparameter
and in the following let
we have a dynamic binding.
If Lisp compiles File 2 fresh with compile-file
, the compiler doesn't recognize the defparameter
and in the following let
we have a lexical binding. If we compile it again, from this state, we have a dynamic binding.
So here version 1 is better, because it is easier to control and understand.
In your example DEFPARAMETER
appears multiple times, which is not useful. I might ask, where is the variable defined and the answer would point to multiple source locations...
So: make sure that your program elements get mostly defined ONCE - unless you have a good reason not to do so.
Upvotes: 4
Reputation: 1541
For a beginner symbols, variables, etc. can be a bit surprising. Symbols are surprisingly featureful. Just to mention a few things you can ask a symbol for it's symbol-value, symbol-package, symbol-name, symbol-function etc. In addition symbols can have a varying amount of information declared about them, for example a type, that provides advice the compile might leverage to create better code. This is true of all symbols, for example *, the symbol you use for multiplication has a symbol-function that does that multiplication. It also has a symbol-value, i.e. the last value returned in the current REPL.
One critical bit of declarative information about symbols is if they are "special." (Yeah, it's a dumb name.) For good reasons it's good practice to declare all global symbols to be special. defvar
, defparameter
, and defconstant
do that for you. We have a convention that all special variables are spelled with * on the front and back, *standard-output*
for example. This convention is so common that some compilers will warn you if you neglect to follow it.
One benefit of declaring a symbol as special is that it will suppress the warning you get when you misspell a variable in your functions. For example (defun faster (how-much) (incf *speed* hw-much))
will generate a warning about hw-much
.
The coolest feature of a symbol that is special is that it is managed with what we call dynamic scoping, in contrast to lexical scope. Recall how * has the value of the last result in the REPL. Well in some implementations you can have multiple REPLs (each running in it's own thread) each will want to have it's own *, it's own *standard-output*
, etc. etc. This is easy in Common Lisp; the threads establish a "dynamic extent" and bind the specials that should be local to that REPL.
So yes, you could just setf *small*
in you example; but if you never declare it to be special then you are going to get warnings about how the compiler thinks you misspelled it.
Upvotes: 0
Reputation: 48745
So you have global variables. Those can be defined by defconstant
(for really non-chainging stuff), defparameter
(a constant that you can change) and defvar
(a variable that does not overwrite if you load
.
You use setf
to alter the state of lexical as well and global variables. start-over
could have used setf
since the code doesn't really define it but change it. If you would have replaced defparameter
with defvar
start-over
would stop working.
(defparameter *par* 5)
(setf *par* 6)
(defparameter *par* 5)
*par* ; ==> 5
(defvar *var* 5)
(setf *var* 6)
(defvar *var* 5)
*var* ; ==> 6
defconstant
is like defparameter
except once defined the CL implementation is free to inline it in code. Thus if you redefine a constant you need to redefine all functions that uses it or else it might use the old value.
(defconstant +c+ 5)
(defun test (x)
(+ x +c+))
(test 1) ; ==> 6
(defconstant +c+ 6)
(test 1) ; ==> 6 or 7
(defun test (x)
(+ x +c+))
(test 1) ; ==> 7
Upvotes: 3
Reputation: 3504
Normally, one would use defvar
to initialy define global variables. The difference between defvar
and defparameter
is subtle, cf. the section in the CLHS and this plays a role here: defparameter
(in contrast to defvar
) assigns the value anew, whereas defvar
would leave the old binding in place.
To address what to use: In general, defvar
and friends are used as top-level forms, not inside some function (closures being the most notable exception in the context of defun
). I would use setf
, not defparameter
.
Upvotes: 2