Lisp 中的错误:LET 绑定规范格式错误

Error in Lisp: The LET binding spec is malformed

我对普通的 Lisp 很陌生,遇到了一些困难。我正在研究一个给定 xyarray 以及垂直索引的函数value returns NIL 如果有来自 (x y) 的对角线元素。

(defun diagonal? (x y array)
    (loop for line from 0 to 19 do
        (let (col (aref array line)) (
            (if (= col -1) (return-from diagonal? t))
            (let (diag (= (abs (- x line)) (abs (- y col)))) (
                if (= diag T) (return-from diagonal? NIL))
            )
    )))
    return T
)

然而,当我尝试使用此功能时,出现以下错误:

; caught ERROR:
;   The LET binding spec (AREF ARRAY LINE) is malformed.

;     (SB-INT:NAMED-LAMBDA DIAGONAL?
;         (X Y ARRAY)
;       (BLOCK DIAGONAL?
;         (LOOP FOR LINE FROM 0 TO 19
;               DO (LET (COL #)
;                    (# #)))
;         RETURN
;         T))

根据 CLHS 一个 let 具有以下结构:

(let (var  (var2 expression))
  body ...)

这里第一个绑定没有值,但是写的一样:

(let ((var nil) (var2 expression))
  body ...)

您的绑定如下所示:

(let (col                  ; col initialized to nil OK
     (aref array line))    ; variable aref initialized to?
 ...)

您的变量 aref 应该只有一个表达式。事实上,您似乎缺少一组括号,使它看起来有点像 Clojure。也许应该是:

(let ((col (aref array line)))
  ...)

我还注意到你在同一行上有一个 ( 就好像你在制作一个块。这是行不通的,因为 ((if ....)) 不是有效的 Common Lisp 代码。您收到错误消息,即运算符应该是命名函数或 lambda。 let 是一个块,所以开头 (let ...) 构成一个块,因此您可以在其中包含许多表达式而无需额外的括号。

首先也是极其重要的一点:使用自动缩进。

(defun diagonal? (x y array)
  (loop for line from 0 to 19 do
        (let (col (aref array line)) (
                                      (if (= col -1) (return-from diagonal? t))
                                      (let (diag (= (abs (- x line)) (abs (- y col)))) (
                                                                                        if (= diag T) (return-from diagonal? NIL))
                                        )
                                      )))
  return T
  )

然后你的代码看起来很奇怪,行很长:永远不要把括号放在他们自己的行上,也不要用左括号结束一行。

改善:

(defun diagonal? (x y array)
  (loop for line from 0 to 19 do
        (let (col (aref array line))
          ((if (= col -1)
               (return-from diagonal? t))
           (let (diag (= (abs (- x line))
                         (abs (- y col))))
             (if (= diag T)
                 (return-from diagonal? NIL))))))
  return T)

第二:LET 需要一个绑定列表。单个绑定是一个变量或(variable value):

(defun diagonal? (x y array)
  (loop for line from 0 to 19 do
        (let ((col (aref array line)))
          ((if (= col -1)
               (return-from diagonal? t))
           (let ((diag (= (abs (- x line))
                          (abs (- y col)))))
             (if (= diag T)
                 (return-from diagonal? NIL))))))
  return T)

第三:LET 需要 Lisp 形式的主体。即零个或多个 Lisp 形式:

(defun diagonal? (x y array)
  (loop for line from 0 to 19 do
        (let ((col (aref array line)))
          (if (= col -1)
               (return-from diagonal? t))
          (let ((diag (= (abs (- x line))
                         (abs (- y col)))))
            (if (= diag T)
                (return-from diagonal? NIL)))))
  return T)

第四:= 需要数字作为参数。 T 不是数字。 = 已经 return s TNIL 我们可以测试。

(defun diagonal? (x y array)
  (loop for line from 0 to 19 do
        (let ((col (aref array line)))
          (if (= col -1)
              (return-from diagonal? t))
          (if (= (abs (- x line))
                 (abs (- y col)))
              (return-from diagonal? NIL))))
  return T)

第五:return T 不是有效的 Lisp 形式。我们可以直接 return T

(defun diagonal? (x y array)
  (loop for line from 0 to 19 do
        (let ((col (aref array line)))
          (if (= col -1)
              (return-from diagonal? t))
          (if (= (abs (- x line))
                 (abs (- y col)))
              (return-from diagonal? NIL))))
  T)

第六:我们不需要colLET,我们可以用LOOP中的另一个FOR替换它。

(defun diagonal? (x y array)
  (loop for line from 0 to 19
        for col = (aref array line)
        do
        (if (= col -1)
            (return-from diagonal? t))
        (if (= (abs (- x line))
               (abs (- y col)))
            (return-from diagonal? NIL))))
  T)

第七:多个IF可以写成单个COND.

(defun diagonal? (x y array)
  (loop for line from 0 to 19
        for col = (aref array line)
        do (cond ((= col -1)
                  (return-from diagonal? t))
                 ((= (abs (- x line))
                     (abs (- y col)))
                  (return-from diagonal? nil))))
  t)

八:for from 0 to n可以换成below (+ n 1)upto n

(defun diagonal? (x y array)
  (loop for line below 20
        for col = (aref array line)
        do (cond ((= col -1)
                  (return-from diagonal? t))
                 ((= (abs (- x line))
                     (abs (- y col)))
                  (return-from diagonal? nil))))
  t)

9: 因为 (RETURN-FROM ... T) returns 来自默认 returns T 的函数,我们可以用 UNTIL 子句替换它在循环中:

(defun diagonal? (x y array)
  (loop for line below 20
        for col = (aref array line)
        until (= col -1)
        when (= (abs (- x line))
                (abs (- y col)))
        do (return-from diagonal? nil))
  t)

第十:因为 col 只是迭代数组的值:

(defun diagonal? (x y array)
  (loop for line below 20
        for col across array
        until (= col -1)
        when (= (abs (- x line))
                (abs (- y col)))
        do (return-from diagonal? nil))
  t)

十一:@Coredump的建议,使用NEVERLOOP 的默认 return 值现在是 T。 Return 只有 nil,当 never 子句失败时。

(defun diagonal? (x y array)
  (loop for line below 20
        for col across array
        until (= col -1)
        never (= (abs (- x line))
                 (abs (- y col)))))