如何在lisp中进行值赋值问题

时间:2011-04-07 01:11:51

标签: lisp common-lisp

我正在学习常见的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)

5 个答案:

答案 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)