Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use defparameter instead of setf?

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?

like image 656
Golo Roden Avatar asked Mar 19 '23 13:03

Golo Roden


2 Answers

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.

like image 186
Rainer Joswig Avatar answered Mar 31 '23 13:03

Rainer Joswig


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
like image 40
Sylwester Avatar answered Mar 31 '23 14:03

Sylwester