Я действительно новичок в общем Lisp и имею некоторые проблемы. Я работаю над функцией, которая дает x , y и массив с индексом для вертикального значения, возвращает NIL если есть диагональ элемента из (xy).

(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))
2
André Fazendeiro 24 Окт 2018 в 02:20

2 ответа

Лучший ответ

Первое и чрезвычайно важное: используйте автоматический отступ.

(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:

(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 не является числом. = уже возвращает T или NIL, которые мы можем проверить.

(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)

Шестое: нам не нужен LET для col, мы можем заменить его другим FOR в LOOP.

(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, используйте NEVER. По умолчанию возвращаемое значение LOOP теперь равно T. Возвращайте только тогда 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)))))
12
Rainer Joswig 24 Окт 2018 в 07:21

Согласно 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. Вы получаете сообщение об ошибке, что оператор должен быть именованной функцией или лямбда. let - это блок, поэтому начало (let ...) образует блок, так что вы можете иметь внутри множество выражений без лишних скобок.

6
Sylwester 23 Окт 2018 в 23:48
52959037