как сделать вопрос присвоения значения в lisp - PullRequest
3 голосов
/ 07 апреля 2011

Я изучаю обычный 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 ]

12 голосов
/ 07 апреля 2011

Вы можете использовать макрос ROTATEF, чтобы поменять местами значения в двух местах. В общем, ROTATEF вращает содержимое всех мест влево.Содержимое самого левого места помещается в самое правое место.Таким образом, его можно использовать более чем в двух местах.

4 голосов
/ 07 апреля 2011

Функция (а не макрос), заменяющая две специальные переменные, может принимать символы переменных в качестве аргументов. То есть вы указываете символы в вызове. Ниже приведена реализация такой функции подкачки:

(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 для определения глобальных / специальных переменных и в соответствии с соглашением использование наушников (звездочек) в их именах. Функция symbol-value предоставляет значение символа, в то время как set присваивает значение символу в результате вычисления его первого аргумента. values предназначен для того, чтобы функция возвращала оба значения из двух операторов set.

3 голосов
/ 07 апреля 2011

Правильно 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 есть ограничения, которых нет в Схеме.Вот почему это недопустимый вызов.

Например, это недопустимо в 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, это может быть ужасная причина для макроса и плохо написанного!)

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

Нет!Не делай этого.Используйте rotatef, как указывает Терье Нордерхауг.

1 голос
/ 12 февраля 2019

В дополнение к другим ответам, целевая проблема ОП - проблема (множественного) присвоения значений - может быть решена путем параллельного присвоения с использованием psetf:

(let ((a 21)
      (b 42))
  (psetf a b
         b a)
  (print (list a b)))
;; (42 21)
1 голос
/ 07 апреля 2011

Вы не можете использовать setf для построения лексической переменной tmp . Вы можете использовать let, как указано ниже:

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

на что ты надеешься.

...