【问题标题】:Error in Lisp: The LET binding spec is malformedLisp 中的错误:LET 绑定规范格式错误
【发布时间】:2018-10-23 23:20:27
【问题描述】:

我对普通的 Lisp 很陌生,并且遇到了一些困难。我正在研究一个函数,它给出了 xy 和一个 array,其垂直值的索引返回 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))

【问题讨论】:

    标签: lisp common-lisp sbcl clisp


    【解决方案1】:

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

    (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 不是数字。 = 已经返回 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 表单。我们可以直接返回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)
    

    第六:col 不需要LET,我们可以在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)
    

    第九个:由于(RETURN-FROM ... T)从一个默认显式返回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 的默认返回值现在是 T。只有当never 子句失败时才返回nil

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

    【讨论】:

    • 第十一个?使用“从不”而不是“return-from”?
    【解决方案2】:

    根据CLHSlet具有如下结构:

    (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 代码。你得到的错误是 operator 应该是一个命名函数或 lambda。 let 是一个块,所以开头 (let ...) 构成一个块,因此您可以在其中包含许多表达式而无需额外的括号。

    【讨论】:

      猜你喜欢
      • 2012-09-12
      • 1970-01-01
      • 1970-01-01
      • 2019-05-26
      • 1970-01-01
      • 1970-01-01
      • 2015-11-10
      • 1970-01-01
      • 2012-10-04
      相关资源
      最近更新 更多