开发者

how to do value assignment issue in lisp

开发者 https://www.devze.com 2023-02-22 09:55 出处:网络
I am learning common lisp and tried to implement a swap value function to swap two variables\' value. Why the following does not work?

I am learning common lisp and tried to implement a swap value function to swap two variables' value. Why the following does not work?

(defun swap-value (a b)
           (setf tmp 0)
             (progn
              ((setf tmp a)
               (setf a b)
               (setf b tmp))))

Error info:

in: LAMBDA NIL
;     ((SETF TMP A) (SETF A B) (SETF B TMP))
; 
; caught ERROR:
;   illegal func开发者_StackOverflow社区tion call

;     (SB-INT:NAMED-LAMBDA SWAP-VALUE
;         (A B)


You can use the ROTATEF macro to swap the values of two places. More generally, ROTATEF rotates the contents of all the places to the left. The contents of the leftmost place is put into the rightmost place. It can thus be used with more than two places.


dfan is right, this isn't going to swap the two values.

The reason you are getting that error though is that this:

(progn
  ((setf tmp a)
   (setf a b)
   (setf b tmp)))

should be this:

(progn
  (setf tmp a)
  (setf a b)
  (setf b tmp))

The first progn has one s-expression in the body, and it's treated as an application of the function (setf tmp a). In Common Lisp, I think that only variables or lambda forms can be in the function position of an application. I could be wrong about the details here, but I know there are restrictions in CL that aren't in Scheme. That's why it's an illegal call.

For instance, this is illegal in CL and results in the same error:

CL-USER> ((if (< 1 2) #'+ #'*) 2 3)
; in: LAMBDA NIL
;     ((IF (< 1 2) #'+ #'*) 2 3)
; 
; caught ERROR:
;   illegal function call
; 
; compilation unit finished
;   caught 1 ERROR condition

You COULD write a swap as a macro (WARNING: I'm a Lisp noob, this might be a terrible reason for a macro and a poorly written one!)

(defmacro swap (a b)
  (let ((tmp (gensym)))
    `(progn
       (setf ,tmp ,a)
       (setf ,a ,b)
       (setf ,b ,tmp))))

Nope! Don't do this. Use rotatef as Terje Norderhaug points out.


A function (rather than macro) swapping two special variables can take the variable symbols as arguments. That is, you quote the symbols in the call. Below is an implementation of such a swap function:

(defvar *a* 1)
(defvar *b* 2)

(defun swap-values (sym1 sym2)
  (let ((tmp (symbol-value sym1)))
    (values
     (set sym1 (symbol-value sym2))
     (set sym2 tmp))))

? (swap-values '*a* '*b*) 
2
1

? *a*
2

Note the use of defvar to define global/special variables and the per convention use of earmuffs (the stars) in their names. The symbol-value function provides the value of a symbol, while set assigns a value to the symbol resulting from evaluating its first argument. The values is there to make the function return both values from the two set statements.


You can not use setf to build a lexical variable tmp. You can use let, as follow:

 (defun swap-value (a b)
       (let ((tmp 0))
         (setf tmp a)
         (setf a b)
         (setf b tmp))
       (values a b))

which will do you hope.


Complimentary to other answers, the OP's targeted problem - the (multiple) value assignment issue - can be solved by parallel assignment using psetf:

(let ((a 21)
      (b 42))
  (psetf a b
         b a)
  (print (list a b)))
;; (42 21)
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号