我在lisp非常新,我正在尝试制作一个基本的国际象棋游戏,但是我似乎在第一道障碍时失败了。每次我尝试运行函数(move... )
时都会收到错误:
*** - FUNCTION: undefined function (SETF GET-ELEMENT)
以下是代码:
(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*)))
我很确定我对lisp有一个根本的误解,这就是为什么这不起作用,任何帮助都会非常感激。
答案 0 :(得分:5)
首先,当您将 * board * 定义为'(...)时,您已将其值设为文字对象。尝试修改文字数据的后果是未定义的,因此您需要避免这种情况。相反,将 * board * 定义为(copy-tree'(...))。这不是主要问题,但它是一个重要的问题。
setf 的第一个(以及第三个,第五个等)参数是places。使用您当前的代码,(get-element ...)不是一个地方。定义并不严重(尽管对于随机二维访问,使用数组和 aref 可能更好)。你可以通过几种方式使(get-element ...)成为一个可以设置的地方。首先,您可以将 get-element 设为宏:
(defmacro get-element (x y table)
`(nth ,x (nth ,y ,table)))
这是一个非常快速的解决方案,但它需要付出代价,因为现在你无法做出类似(map' get-element' (1 2)'(3 4)(制作清单2:初始元素表)),因为 get-element 不再是函数。一个更好的选择就是定义 setf 函数,通过利用(第n ...) 这个地方的事实来实现这个功能。可以与 setf :
一起使用(defun (setf get-element) (value x y table)
(setf (nth x (nth y table)) value))
HyperSpec中描述了这种功能:
5.1.2.9 Other Compound Forms as Places
对于运算符为符号f的任何其他复合形式, setf form扩展为对名为(setf f)的函数的调用。该 新构造的函数形式中的第一个参数是newvalue和 其余的参数是其余的元素。
这意味着(setf(get-element ...)值)会变成(funcall#'(setf get-element)值...),并且这正是我们刚刚定义的功能。
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))