Во-первых, синтаксис не является ни обычным шрифтом, ни схемой, это два основных диалекта, которые обычно используются.
В синтаксисе Common-Lisp это работает как ожидалось:
(defun matrix-add (m1 m2)
(labels
((matrix-add-row (r1 r2 res-row)
(if (and (not (null r1)) (not (null r2)))
(matrix-add-row (cdr r1) (cdr r2)
(cons (+ (car r1) (car r2)) res-row))
(reverse res-row)))
(matrix-add-each (m1 m2 res)
(if (and (not (null m1)) (not (null m2)))
(let ((res-row (matrix-add-row (car m1) (car m2) ())))
(matrix-add-each (cdr m1) (cdr m2) (cons res-row res)))
(reverse res))))
(matrix-add-each m1 m2 ())))
> (matrix-add `((1 2) (3 4)) `((10 20) (30 40)))
((11 22) (33 44))
Несколько вещей более многословны, чем здесь требуется.
Во-первых, в общем lisp nil равен false, а and
возвращает последний аргумент, который не равен false.
> (and () `(1))
NIL
> (and `(1) `(1))
(1)
> (and `(1) `(3))
(3)
> (and `(1) ())
NIL
так что в вашем случае, в общем случае, вам не нужны все ненулевые тесты.
Во-вторых, обычно (в большей степени в схеме, чем в lisp) используют хвостовые рекурсивные функции, а не аккумуляторные, поэтому вместо добавления двух списков без использования библиотечных функций вы обычно берете два списка и возвращаете список, чьи head - сумма глав двух списков, и хвост которой является результатом добавления элементов каждого списка хвостов. Таким образом, вместо того, чтобы принимать три аргумента (вход, вход, аккумулятор), а затем инвертировать аккумулятор, вы должны написать функцию как два аргумента и вернуть часть результата, основанную на этих двух аргументах.
(defun matrix-add-row (r1 r2)
(if (and r1 r2)
(cons (+ (car r1) (car r2)) (matrix-add-row (cdr r1) (cdr r2)))
()))
> (matrix-add-row `(1 2 3 4) `(10 20 30 40))
(11 22 33 44)
Но этот шаблон применения функции к машине списков и применения функции к остальному списку очень распространен, поэтому для него есть набор библиотечных функций - семейство карт .
Таким образом, вы будете склонны использовать функции карты для работы с простыми списками, а не писать свои собственные. Макрос считывателя #'
(сокращение от function
) извлекает функцию из символа, поэтому для применения функции +
к элементам двух списков можно использовать mapcar
:
> (mapcar #'+ `(1 2 3 4) `(10 20 30 40))
(11 22 33 44)
Это устраняет сложность написания контрольной панели для рекурсивного применения, может быть более эффективным и выражает намерение более высокого уровня.
Поскольку вам больше не нужен аккумулятор, вам не нужно определять matrix-add-each, а вместо этого можно просто вернуть результат применения функции add-row к каждой строке в матрице:
(defun matrix-add (m1 m2)
(flet ((matrix-add-row (r1 r2) (mapcar #'+ r1 r2)))
(mapcar #'matrix-add-row m1 m2)))
или вы можете даже использовать лямбду, вместо того, чтобы определять функцию отдельно, хотя читать разделенную версию для начала может быть немного легче:
(defun matrix-add (m1 m2)
(mapcar (lambda (r1 r2) (mapcar #'+ r1 r2)) m1 m2))
Хотя для «реальной» функции вы можете проверить, что матрицы имеют одинаковый размер, а строки одинакового размера, что будет сложнее, чем однострочная функция. Но для реального матричного кода вы можете использовать массивы, а не списки.
Как только вы начнете думать с точки зрения функций над списками и функций, применяющих функции над списками, вы начнете понимать, что вам нужно выполнять гораздо меньше работы, чтобы заставить Lisp выполнять то, что вы хотите.
Как отмечалось в комментариях, defun
создает глобальную привязку функции, flet
и labels
в локальной области видимости.