#recursion #lisp #common-lisp
#рекурсия #lisp #common-lisp
Вопрос:
Только начал изучать LISP, и я пытаюсь понять, как написать следующую рекурсивную функцию.
Итак, я должен иметь
(DOT-PRODUCT '(1 2) '(3 4)))
Результат должен быть 11
Я написал следующее
(defun DOT-PRODUCT (a b)
(if (or (null a) (null b))
0
( (* (first a) (first b))
(DOT-PRODUCT (rest a) (rest b)))))
И, похоже, все работает; однако, он по-прежнему работает со списками разной длины. Я хочу, чтобы он просто работал со списками чисел одинаковой длины. Куда я должен добавить код, который возвращает «недопустимую длину», если у нас есть такой?
Ответ №1:
Простой способ — переписать функцию так, чтобы она проверяла разные случаи, используя условную форму cond
:
(defun dot-product (a b)
(cond ((null a) (if (null b) 0 (error "invalid length")))
((null b) (error "invalid length"))
(t ( (* (first a) (first b))
(dot-product (rest a) (rest b))))))
В первой ветви cond
, если первый аргумент равен NIL
, второй тоже должен быть NIL
, иначе генерируется ошибка. Во второй ветви мы уже знаем, что a
это не NIL
так, поэтому немедленно генерируется ошибка. Наконец, результат вычисляется.
Комментарии:
1. Спасибо за подробное объяснение и предложение по использованию cond вместо if . Это действительно помогает!
Ответ №2:
Умножьте соответствующие элементы списков X и Y:
(mapcar #'* X Y)
Добавьте элементы списка Z:
(reduce #' Z)
В совокупности: скалярное произведение:
(reduce #' (mapcar #'* X Y))
reduce
и mapcar
являются основой концепции «MapReduce«, которая является обобщением такого рода вещей, которые включают скалярные произведения, интегралы свертки и множество способов массирования и обобщения данных.
Ответ №3:
Можно повысить эффективность, введя накопительную переменную и превратив стандартную рекурсию в хвостовую рекурсию. В этом примере я использовал (labels)
для определения рекурсии:
(defun DOT-PRODUCT (a b)
(labels ((dp (x y accum)
(if (or (null x) (null y))
accum
(dp (rest x) (rest y) ( accum (* (first x) (first y)))))))
(if (= (length a) (length b))
(dp a b 0)
(error "Invalid length."))))