无法设置列表的元素

Der*_*rek 1 common-lisp

我在lisp上很新,我正在尝试制作一个基本的国际象棋游戏,但是我似乎在第一个障碍时失败了.每次我尝试运行该函数时(move... )都会收到错误:

*** - FUNCTION: undefined function (SETF GET-ELEMENT)
Run Code Online (Sandbox Code Playgroud)

这是代码:

(defparameter *board* '(
                       (? ? ? ? ? ? ? ?)
                       (? ? ? ? ? ? ? ?)
                       (? ? ? ? ? ? ? ?)
                       (? ? ? ? ? ? ? ?)
                       (? ? ? ? ? ? ? ?)
                       (? ? ? ? ? ? ? ?)
                       (? ? ? ? ? ? ? ?)
                       (? ? ? ? ? ? ? ?)
                       ))

(defun print-rows (board)
  "Print each element of LIST on a line of its own."
  (mapcar #'print board))

(defun get-element (x y table)
  (nth x (nth y table)))

(defun move (x1 x2 y1 y2)
  (setf (get-element x2 y2 *board*) (get-element x1 y1 *board*)))
Run Code Online (Sandbox Code Playgroud)

我很确定我对lisp有一个基本的误解,这就是为什么这不起作用,任何帮助都会非常感激.

Jos*_*lor 5

不要修改文字数据

首先,当您将*board*定义为'(...)时,您已将其值设为文字对象.尝试修改文字数据的后果是未定义的,因此您需要避免这种情况.相反,将*board*定义为(copy-tree'(...)).这不是主要问题,但它是一个重要的问题.

(get-element ...)不是一个地方

setf的第一个(和第三个,第五个等)参数是地方.使用当前代码,(get-element ...)不是一个地方.定义并不错(尽管对于随机二维访问,使用数组和aref可能更好).您可以通过几种方式使(get-element ...)成为一个可设置的位置.首先,你可以使get-element成为一个宏:

(defmacro get-element (x y table)
  `(nth ,x (nth ,y ,table)))
Run Code Online (Sandbox Code Playgroud)

这是一个非常快速的解决方案,但它需要付出代价,因为现在你不能做一些事情(例如'map'get-element'(1 2)'(3 4)(make-list 2:initial- element table)),因为get-element不再是一个函数.一个更好的选择就是通过利用(nth ...)一个可以与setf一起使用的地方来定义使这个工作的setf函数:

(defun (setf get-element) (value x y table)
  (setf (nth x (nth y table)) value))
Run Code Online (Sandbox Code Playgroud)

HyperSpec中描述了这种功能:

5.1.2.9其他复合形式为场所

对于运算符为符号f的任何其他复合形式,setf形式扩展为对名为(setf f)的函数的调用.新构造的函数形式中的第一个参数是newvalue,其余参数是place的其余元素.

这意味着(setf(get-element ...)值)变为(funcall#'(setf get-element)value ...),这正是我们刚刚定义的函数.

CL-USER> (defun (setf get-element) (value x y table)
           (setf (nth x (nth y table)) value))
; (SETF GET-ELEMENT)

CL-USER> (let ((board (copy-tree '((1 2 3)
                                   (4 5 6)))))
           (setf (get-element 1 1 board) 'x)
           board)
; ((1 2 3) (4 X 6))
Run Code Online (Sandbox Code Playgroud)