Улучшение функции шепелявости

Если Вы Гуглите для достойной ссылки JavaScript по данной теме, включаете ключевое слово "mdc" в свой запрос, и Ваши первые результаты будут от Центра разработки Mozilla. Я не несу офлайновых ссылок или книг со мной. Я всегда использую прием ключевого слова "mdc" для прямого получения до того, что я ищу. Например:

Google: вид mdc
массива JavaScript (в большинстве случаев можно опустить "JavaScript")

Обновление: Разработчик Mozilla Центр был переименован Разработчику Mozilla Сеть . Прием ключевого слова "mdc" все еще работает, но достаточно скоро мы можем иметь к , начинают использовать "mdn" вместо этого .

5
задан Pillsy 1 December 2009 в 15:08
поделиться

5 ответов

типичный список параметров для такой функции будет:

(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)))
8
ответ дан 18 December 2019 в 09:50
поделиться

Ответ на ваше первое ОБНОВЛЕНИЕ.

первый вопрос:

см. Это

(if (foo)
  (bar (+ 1 baz))
  (bar baz))

Это то же самое, что:

(bar (if (foo)
        (+ 1 baz)
        baz))

или:

(let ((newbaz (if (foo)
                 (+ 1 baz)
                 baz)))
  (bar newbaz))

Второй:

Почему бы и нет начать с I = 1?

См. также итеративную версию в моем другом ответе ...

1
ответ дан 18 December 2019 в 09:50
поделиться

Поскольку у вас уже есть работающее решение, я дополню решение Райнера Йосвига , в основном, чтобы сделать связанные стилистические комментарии.

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

5
ответ дан 18 December 2019 в 09:50
поделиться

Немного измененный вариант версии цикла Райнера:

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

2
ответ дан 18 December 2019 в 09:50
поделиться

Итерационная версия, предложенная Райнером, очень удобна, компактна и более эффективна, поскольку вы проходите последовательность только один раз; в отличие от рекурсивной версии, которая вызывает позицию на каждой итерации и, таким образом, каждый раз пересекает подпоследовательность. ( Редактировать: Извините, я совершенно ошибся в этом последнем предложении, см. Комментарий Райнера)

Если требуется рекурсивная версия, другой подход - продвинуть начало пока он не встретится с концом , собирая результат по пути.

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

Также , Мне интересно, Роберт, твой учитель сказал, почему он не говорит?

1
ответ дан 18 December 2019 в 09:50
поделиться