Я выполнил упражнение 5 из Graham Common Lisp, глава 5, для которого требуется функция, принимающая объект X и вектор V и возвращающая список всех объектов, непосредственно предшествующих X в V.
Это работает так:
> (preceders #\a "abracadabra")
(#\c #\d #r)
Я сделал рекурсивную версию:
(defun preceders (obj vec &optional (result nil) &key (startt 0))
(let ((l (length vec)))
(cond ((null (position obj vec :start startt :end l)) result)
((= (position obj vec :start startt :end l) 0)
(preceders obj vec result
:startt (1+ (position obj vec :start startt :end l))))
((> (position obj vec :start startt :end l) 0)
(cons (elt vec (1- (position obj vec :start startt :end l)))
(preceders obj vec result
:startt (1+ (position obj vec
:start startt
:end l))))))))
Это работает правильно, но мои учителя дают мне следующую критику:
Это многократно вызывает длину. Не так уж и плохо с векторами, но все же ненужно. Более эффективный и более гибкий (для пользователя) код состоит в том, чтобы определить это как другие функции обработки последовательности. Используйте параметры ключевых слов :start и :end так же, как это делают другие функции последовательности, с теми же начальными значениями по умолчанию. length должен вызываться не более одного раза.
Я консультируюсь с учебником по Common Lisp и google, но, похоже, в этом немного помогает: я не знаю, что он имеет в виду, используя параметры ключевого слова :start и :end , и я понятия не имею, как вызывать length только раз. Я был бы признателен, если бы вы, ребята, могли дать мне некоторое представление о том, как улучшить мой код, чтобы он соответствовал требованиям, опубликованным моим учителем.
ОБНОВИТЬ:
Теперь я придумал следующий код:
(defun preceders (obj vec
&optional (result nil)
&key (start 0) (end (length vec)) (test #'eql))
(let ((pos (position obj vec :start start :end end :test test)))
(cond ((null pos) result)
((zerop pos) (preceders obj vec result
:start (1+ pos) :end end :test test))
(t (preceders obj vec (cons (elt vec (1- pos)) result)
:start (1+ pos) :end end :test test)))))
Я получаю эту критику:
Когда у вас есть сложный рекурсивный вызов, который повторяется одинаково более чем в одной ветви, часто бывает проще выполнить вызов сначала, сохранить его в локальной переменной, а затем использовать переменную в гораздо более простом операторе IF. или КОНД.
Кроме того, для моей итеративной версии функции:
(defun preceders (obj vec)
(do ((i 0 (1+ i))
(r nil (if (and (eql (aref vec i) obj)
(> i 0))
(cons (aref vec (1- i)) r)
r)))
((eql i (length vec)) (reverse r))))
Я получаю критику
Начать DO с лучшей точки и убрать повторяющийся › 0 тест