Reputation: 249
I have to define a LISP function that assigns to a parameter the result of another function, like this:
(defun assign-param (x)
(defparameter param (generic-funct x)))
the result of assign-param is PARAM
, at the first call. If i do another call, the function assign-param returns again PARAM
, but this way:
Warning: (DEFVAR PARAM) defined more than once in
C:\Users\****\Desktop\****\****.lisp.
PARAM
I tried using defvar
instead of defparameter
with the same result.
Is there a way to avoid this warning message?
Is there a way to auto-generate other parameters if one already exists? (ex: if param is already in memory, assign to param1, param2...)
Upvotes: 2
Views: 630
Reputation: 58578
Try:
(defvar *param*) ;; at the top level: define the variable
;; Now actually just a pure assignment in the function now
(defun assign-param (x)
(setf *param* (generic-funct x)))
I added the *
"earmuffs", because I can't in good conscience introduce a dynamic variable without; my good name is attached to this answer.
Really, why though? Why hide an assignment side effect behind a function. Its almost certainly better to document the *param*
variable and let the caller do the assignment itself.
Wrapping a Lisp dynamic variable behind this sort of access wall only cripples it.
Lisp lets us do this:
;; locally re-bind variable
(let ((*param* (some-value)))
(code)
...))
the (code)
body will execute with *param*
having the value from (some-value)
. When the let
terminates, the previous value is restored, no matter how it terminates. The variable is still global: the altered value is seen inside functions called out of the body, not just by forms that are lexically scoped in the let
, as with a lexical variable.
(The reason *param*
is treated this way is that the defvar
and defparameter
forms specially mark the symbol for this type of binding and scoping. It becomes a "special variable". And that is why we use *...*
earmuffs: to create a separate namespace which loudly identifies these non-lexically-scoped variables, preventing us from accidentally converting lexical variables in our code into special variables.)
If we hide the existence of *param*
, behind a function, we lose this flexibility; we have reduced it to a dumb global that can only be clobbered with calls to assign-param
.
The assign-param
function says to me (even after being fixed to use setf
with defvar
on the outside), "I don't understand Lisp and I'm scared of global variables from my experience with other languages".
Upvotes: 2
Reputation: 60014
defvar
and defparameter
are
"top-level" forms.
There is no reason to use them inside a function.
What you might want to do is something like
(defvar *param*)
(defun assign-param (x)
(setq *param* (generic-funct x))
'*param*) ; so that `assign-param` returns the symbol instead of the new value, like `defvar`
However, your last question about param1
, param2
&c makes little
sense (yes, it is possible to do it, and, no, you do not want to do it).
You appear to want to have a set of global parameters, set dynamically from, say, environment or a config file.
There are two main way to do this: hash tables and packages.
Simple and straighforward; associate any value with any string:
(defvar *parameters* (make-hash-table :test 'equal))
(defun param-value (param-name)
(gethash param-name *parameters*))
(defun (setf param-value) (new-value param-name)
(setf (gethash param-name *parameters*) new-value))
(param-value "foo")
==> NIL; NIL
(setf (param-value "foo") "bar")
==> "bar"
(param-value "foo")
==> "bar"; T
(defun delete-param (param-name)
(remhash param-name *parameters*))
Note that you should modify param-value
to signal an error if the second value of gethash
is nil
, i.e., the parameter does not exist.
More involved, but you can associate more "stuff" with your parameter objects:
(defvar *parameters* (make-package "parameters" :use ()))
(defun param-value (param-name)
(multiple-value-bind (param status) (find-symbol param-name *parameters*)
(unless status
(error "Parameter %s does not exist" param-name))
(symbol-value param)))
(defun (setf param-value) (new-value param-name)
(setf (symbol-value (intern param-name *parameters*)) new-value))
(defun delete-param (param-name)
(multiple-value-bind (param status) (find-symbol param-name *parameters*)
(when status
(unintern param))))
The major advantage of this approach is the integration of read
with the packages which would make config file parsing easier.
A minor advantage is that you can store not just a value, but also a function and a property list in your parameter.
Upvotes: 6