Если Вы Гуглите для достойной ссылки JavaScript по данной теме, включаете ключевое слово "mdc" в свой запрос, и Ваши первые результаты будут от Центра разработки Mozilla. Я не несу офлайновых ссылок или книг со мной. Я всегда использую прием ключевого слова "mdc" для прямого получения до того, что я ищу. Например:
Google: вид mdc
массива JavaScript (в большинстве случаев можно опустить "JavaScript")
Обновление: Разработчик Mozilla Центр был переименован Разработчику Mozilla Сеть . Прием ключевого слова "mdc" все еще работает, но достаточно скоро мы можем иметь к , начинают использовать "mdn" вместо этого .
типичный список параметров для такой функции будет:
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
...
)
Как вы можете видеть, у него есть параметры START и END.
TEST - функция сравнения по умолчанию. Используйте (тестовый элемент funcall (aref vector i)). Часто есть также параметр KEY ...
LENGTH вызывается повторно для каждого рекурсивного вызова PRECEDERS.
Я бы сделал нерекурсивную версию и переместил бы два индекса по вектору: один для первого элемента и один для следующего пункта. Каждый раз, когда следующий элемент является EQL для элемента, который вы ищете, затем вставьте первый элемент в список результатов (если он не является там членом).
Для рекурсивной версии я бы написал вторую функцию, которая вызывается by PRECEDERS, который принимает две индексные переменные, начинающиеся с 0 и 1, и использует их. Я бы не стал называть ПОЗИЦИЕЙ. Обычно эта функция является локальной функцией через LABELS внутри PRECEDERS, но для облегчения написания вспомогательная функция может быть и снаружи.
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
(preceders-aux item vector start end test start (1+ start) nil))
(defun preceders-aux (item vector start end test pos0 pos1 result)
(if (>= pos1 end)
result
...
))
Помогает ли это?
Вот итеративная версия с использованием LOOP:
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
(let ((result nil))
(loop for i from (1+ start) below end
when (funcall test item (aref vector i))
do (pushnew (aref vector (1- i)) result))
(nreverse result)))
Ответ на ваше первое ОБНОВЛЕНИЕ.
первый вопрос:
см. Это
(if (foo)
(bar (+ 1 baz))
(bar baz))
Это то же самое, что:
(bar (if (foo)
(+ 1 baz)
baz))
или:
(let ((newbaz (if (foo)
(+ 1 baz)
baz)))
(bar newbaz))
Второй:
Почему бы и нет начать с I = 1?
См. также итеративную версию в моем другом ответе ...
Поскольку у вас уже есть работающее решение, я дополню решение Райнера Йосвига , в основном, чтобы сделать связанные стилистические комментарии.
(defun preceders (obj seq &key (start 0) (end (length seq)) (test #'eql))
(%preceders obj seq nil start end test))
Основная причина иметь отдельный помощник функция (которую я называю % PRECEDERS
, обычное соглашение для указания того, что функция является «частной» ) заключается в том, чтобы исключить необязательный аргумент результата. Использование необязательных аргументов таким образом в целом нормально, но необязательные аргументы и аргументы ключевого слова ужасно сочетаются друг с другом, и использование обоих в одной функции - чрезвычайно эффективный способ создания всевозможных сложных для отладки ошибок.
Это вопрос вкуса. чтобы сделать вспомогательную функцию глобальной (используя DEFUN
) или локальной (используя LABELS
). Я предпочитаю сделать его глобальным, так как это означает меньше отступов и упрощает интерактивную отладку. YMMV.
Возможная реализация вспомогательной функции:
(defun %preceders (obj seq result start end test)
(let ((pos (position obj seq :start start :end end :test test)))
;; Use a local binding for POS, to make it clear that you want the
;; same thing every time, and to cache the result of a potentially
;; expensive operation.
(cond ((null pos) (delete-duplicates (nreverse result) :test test))
((zerop pos) (%preceders obj seq result (1+ pos) end test))
;; I like ZEROP better than (= 0 ...). YMMV.
(t (%preceders obj seq
(cons (elt seq (1- pos)) result)
;; The other little bit of work to make things
;; tail-recursive.
(1+ pos) end test)))))
Кроме того, после всего этого, я думаю, я должен указать, что я также согласен с советом Райнера делать это с помощью явного цикла вместо рекурсии, при условии, что выполнение это рекурсивно не является частью упражнения.
РЕДАКТИРОВАТЬ: Я перешел на более распространенное соглашение «%» для вспомогательной функции. Обычно любое соглашение, которое вы используете, просто увеличивает тот факт, что вы явно экспортируете только функции, составляющие ваш общедоступный интерфейс, но некоторые стандартные функции и макросы используют завершающий "*" для обозначения функциональных возможностей.
Я изменил вещи, чтобы удалить повторяющиеся предшествующие элементы. с помощью стандартной функции DELETE-DUPLICATES
. Это потенциально может быть намного (т. Е. Экспоненциально) быстрее, чем повторное использование ADJOIN
или PUSHNEW
, поскольку он может использовать внутреннее представление хешированного набора, по крайней мере, для общих тестовых функций, таких как EQ
, EQL
и EQUAL
.
Я изменил вещи, чтобы удалить повторяющиеся предшествующие элементы. с помощью стандартной функции DELETE-DUPLICATES
. Это потенциально может быть намного (т. Е. Экспоненциально) быстрее, чем повторное использование ADJOIN
или PUSHNEW
, поскольку он может использовать внутреннее представление хешированного набора, по крайней мере, для общих тестовых функций, таких как EQ
, EQL
и EQUAL
.
Я изменил вещи, чтобы удалить повторяющиеся предшествующие элементы. с помощью стандартной функции DELETE-DUPLICATES
. Это потенциально может быть намного (т. Е. Экспоненциально) быстрее, чем повторное использование ADJOIN
или PUSHNEW
, поскольку он может использовать внутреннее представление хешированного набора, по крайней мере, для общих тестовых функций, таких как EQ
, EQL
и EQUAL
.
Я изменил вещи, чтобы удалить повторяющиеся предшествующие файлы, используя стандартную функцию DELETE-DUPLICATES
. Это потенциально может быть намного (т. Е. Экспоненциально) быстрее, чем повторное использование ADJOIN
или PUSHNEW
, поскольку он может использовать внутреннее представление хешированного набора, по крайней мере, для общих тестовых функций, таких как EQ
, EQL
и EQUAL
.
Я изменил вещи, чтобы удалить повторяющиеся предшествующие файлы, используя стандартную функцию DELETE-DUPLICATES
. Это потенциально может быть намного (т. Е. Экспоненциально) быстрее, чем повторное использование ADJOIN
или PUSHNEW
, поскольку он может использовать внутреннее представление хешированного набора, по крайней мере, для общих тестовых функций, таких как EQ
, EQL
и EQUAL
.
EQ
, EQL
и EQUAL
. по крайней мере, для общих тестовых функций, таких как EQ
, EQL
и EQUAL
. Немного измененный вариант версии цикла Райнера:
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
(delete-duplicates
(loop
for index from (1+ start) below end
for element = (aref vector index)
and previous-element = (aref vector (1- index)) then element
when (funcall test item element)
collect previous-element)))
Это больше использует директивы цикла, и среди прочего, доступ к каждому элементу вектора осуществляется только один раз (мы сохраняем предыдущий элемент в переменной previous-element).
Итерационная версия, предложенная Райнером, очень удобна, компактна и более эффективна, поскольку вы проходите последовательность только один раз; в отличие от рекурсивной версии, которая вызывает ( Редактировать: Извините, я совершенно ошибся в этом последнем предложении, см. Комментарий Райнера) позицию
на каждой итерации и, таким образом, каждый раз пересекает подпоследовательность.
Если требуется рекурсивная версия, другой подход - продвинуть начало
пока он не встретится с концом
, собирая результат по пути.
(defun precede (obj vec &key (start 0) (end (length vec)) (test #'eql))
(if (or (null vec) (< end 2)) nil
(%precede-recur obj vec start end test '())))
(defun %precede-recur (obj vec start end test result)
(let ((next (1+ start)))
(if (= next end) (nreverse result)
(let ((newresult (if (funcall test obj (aref vec next))
(adjoin (aref vec start) result)
result)))
(%precede-recur obj vec next end test newresult)))))
Конечно, это просто еще один способ выразить версию цикла
.
test:
[49]> (precede #\a "abracadabra")
(#\r #\c #\d)
[50]> (precede #\a "this is a long sentence that contains more characters")
(#\Space #\h #\t #\r)
[51]> (precede #\s "this is a long sentence that contains more characters")
(#\i #\Space #\n #\r)
Также , Мне интересно, Роберт, твой учитель сказал, почему он не говорит?