我正在学习常见的lisp,并尝试实现交换值函数来交换两个变量的值。为什么以下不起作用?
(defun swap-value (a b)
(setf tmp 0)
(progn
((setf tmp a)
(setf a b)
(setf b tmp))))
错误信息:
in: LAMBDA NIL
; ((SETF TMP A) (SETF A B) (SETF B TMP))
;
; caught ERROR:
; illegal function call
; (SB-INT:NAMED-LAMBDA SWAP-VALUE
; (A B)
答案 0 :(得分:12)
您可以使用ROTATEF宏来交换两个位置的值。更一般地说,ROTATEF将所有位置的内容旋转到左侧。的内容 最左边的地方放在最右边的地方。因此它可以在两个以上的地方使用。
答案 1 :(得分:4)
交换两个特殊变量的函数(而不是宏)可以将变量符号作为参数。也就是说,引用调用中的符号。下面是这种交换函数的实现:
(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
注意使用defvar
来定义全局/特殊变量以及名称中 earmuffs (星星)的每个惯例用法。 symbol-value
函数提供符号的值,而set
为评估其第一个参数得到的符号赋值。 values
用于使函数返回两个set
语句中的两个值。
答案 2 :(得分:3)
dfan是对的,这不会交换两个值。
你得到这个错误的原因是:
(progn
((setf tmp a)
(setf a b)
(setf b tmp)))
应该是这样的:
(progn
(setf tmp a)
(setf a b)
(setf b tmp))
第一个progn
在体内有一个s表达式,并且它被处理了
作为函数(setf tmp a)
的应用。在Common Lisp中,我
认为只有变量或lambda
形式可以在函数中
申请的位置。我在这里的细节可能是错的,
但我知道CL中有一些不在Scheme中的限制。那是
为什么这是非法的电话。
例如,这在CL中是非法的并导致相同的错误:
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
<击> 您可以将交换写为宏(警告:我是Lisp noob,这个 可能是宏观和写得不好的一个可怕的原因!)
(defmacro swap (a b)
(let ((tmp (gensym)))
`(progn
(setf ,tmp ,a)
(setf ,a ,b)
(setf ,b ,tmp))))
击> <击> 撞击>
都能跟得上!不要这样做。使用rotatef
作为Terje Norderhaug指出。
答案 3 :(得分:1)
您不能使用 setf 来构建词法变量 tmp 。您可以使用let,如下所示:
(defun swap-value (a b)
(let ((tmp 0))
(setf tmp a)
(setf a b)
(setf b tmp))
(values a b))
你会希望。
答案 4 :(得分:1)
除其他答案外,OP的目标问题((多个)值分配问题)可以使用psetf
通过并行分配来解决:
(let ((a 21)
(b 42))
(psetf a b
b a)
(print (list a b)))
;; (42 21)