我在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有一个基本的误解,这就是为什么这不起作用,任何帮助都会非常感激.
首先,当您将*board*定义为'(...)时,您已将其值设为文字对象.尝试修改文字数据的后果是未定义的,因此您需要避免这种情况.相反,将*board*定义为(copy-tree'(...)).这不是主要问题,但它是一个重要的问题.
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)