Как делают меня memoize рекурсивная функция в Lisp?

В Java все находится в форме класса.

Если вы хотите использовать любой объект, тогда у вас есть две фазы:

  1. Объявить
  2. Инициализация

Пример:

  • Объявление: Object a;
  • Инициализация: a=new Object();

То же самое для концепции массива

  • Объявление: Item i[]=new Item[5];
  • Инициализация: i[0]=new Item();

Если вы не дают секцию инициализации, тогда возникает NullpointerException.

18
задан Rainer Joswig 22 January 2015 в 16:41
поделиться

8 ответов

Я предполагаю, что Вы используете язык Common LISP, который имеет отдельные пространства имен для имен переменной и имен функций. Чтобы к memoize функция, названная символом, необходимо изменить его функциональную привязку через средство доступа 'fdefinition':

(setf (fdefinition 'collatz-steps) (memoize #'collatz-steps))

(defun p14 ()
  (let ((mx 0) (my 0))
    (loop for x from 1 to 1000000
          for y = (collatz-steps x)
          when (< my y) do (setf my y mx x))
    mx))
10
ответ дан 30 November 2019 в 09:11
поделиться

Вот функция memoize, которая снова переплетает функцию символа:

(defun memoize-function (function-name)
  (setf (symbol-function function-name)
    (let ((cache (make-hash-table :test #'equal)))
         #'(lambda (&rest args)
             (multiple-value-bind 
                 (result exists)
                (gethash args cache)
               (if exists
                   result
                   (setf (gethash args cache)
                         (apply fn args)))))))

Вы затем сделали бы что-то вроде этого:

(defun collatz-steps (n)
  (if (= 1 n) 0
      (if (evenp n) 
          (1+ (collatz-steps (/ n 2)))
          (1+ (collatz-steps (1+ (* 3 n)))))))

(memoize-function 'collatz-steps)

я оставлю его до Вас для создания unmemoize-функции.

2
ответ дан 30 November 2019 в 09:11
поделиться

что-то вроде этого:

(setf collatz-steps (memoize lambda (n)
  (if (= 1 n) 0
    (if (evenp n) 
        (1+ (collatz-steps (/ n 2)))
        (1+ (collatz-steps (1+ (* 3 n))))))))

IOW: Ваша исходная (немемоизованная) функция является анонимной, и Вы только даете имя результату memoizing она.

2
ответ дан 30 November 2019 в 09:11
поделиться

Изменение "исходной" функции необходимо, потому что, как Вы говорите, нет никакого другого пути к рекурсивному вызову (вызовам), который будет обновлен для вызова мемоизованной версии.

, К счастью, способ, которым работает шепелявость, состоит в том, чтобы найти функцию по имени каждый раз, когда это нужно назвать. Это означает, что достаточно заменить привязку функции мемоизованной версией функции, так, чтобы рекурсивные вызовы автоматически искали и повторно вступили через memoization.

код huaiyuan показывает ключевой шаг:

(setf (fdefinition 'collatz-steps) (memoize #'collatz-steps))

Этот прием также работает в Perl. На языке как C, однако, мемоизованная версия функции должна быть кодирована отдельно.

Некоторые реализации шепелявости обеспечивают систему, названную "советом", который обеспечивает стандартизированную структуру для замены функций с расширенными версиями себя. В дополнение к функциональным обновлениям как memoization это может быть чрезвычайно полезно в отладке путем вставки печати отладки (или полностью остановки и предоставления continuable подсказки), не изменяя исходный код.

1
ответ дан 30 November 2019 в 09:11
поделиться

Только что я записал немного memoization стандартной программы для Схемы, которая использовала цепочку закрытий для отслеживания мемоизованное состояние:

(define (memoize op)
  (letrec ((get (lambda (key) (list #f)))
           (set (lambda (key item)
                  (let ((old-get get))
                    (set! get (lambda (new-key)
                                (if (equal? key new-key) (cons #t item)
                                    (old-get new-key))))))))
    (lambda args
      (let ((ans (get args)))
        (if (car ans) (cdr ans)
            (let ((new-ans (apply op args)))
              (set args new-ans)
              new-ans))))))

Это должно использоваться как так:

(define fib (memoize (lambda (x)
                       (if (< x 2) x
                           (+ (fib (- x 1)) (fib (- x 2)))))))

я уверен, что это может быть портировано Вашему фавориту, лексически определил объем разновидности Lisp легко.

0
ответ дан 30 November 2019 в 09:11
поделиться

Обратите внимание на несколько моментов:

(defun foo (bar)
   ... (foo 3) ...)

Выше - функция, которая вызывает сама себя.

В Common Lisp компилятор файлов может предполагать, что FOO не изменяется. Он НЕ будет вызывать обновленный FOO позже. Если вы измените привязку функции FOO, то вызов исходной функции все равно перейдет к старой функции.

Таким образом, запоминание саморекурсивной функции НЕ будет работать в общем случае. Особенно, если вы используете хороший компилятор.

Вы можете обойти это, чтобы всегда проходить через символ, например: (funcall 'foo 3)

(DEFVAR ...) - это форма верхнего уровня. Не используйте его внутри функций. Если вы объявили переменную, установите ее позже с помощью SETQ или SETF.

Для вашей проблемы я бы просто использовал хеш-таблицу для хранения промежуточных результатов.

1
ответ дан 30 November 2019 в 09:11
поделиться

Я бы, наверное, сделал что-то вроде:

(let ((memo (make-hash-table :test #'equal)))
  (defun collatz-steps (n)
    (or (gethash n memo)
    (setf (gethash n memo)
          (cond ((= n 1) 0)
            ((oddp n) (1+ (collatz-steps (+ 1 n n n))))
            (t (1+ (collatz-steps (/ n 2)))))))))

Это не красиво и функционально, но, в таком случае, это не очень хлопотно и работает. Недостатком является то, что у вас нет удобной непамятованной версии для тестирования, а очистка кэша граничит с "очень сложно".

0
ответ дан 30 November 2019 в 09:11
поделиться

Эта функция - именно та, которую Питер Норвиг приводит в качестве примера функции, которая кажется хорошим кандидатом для мемоизации, но таковой не является.

См. рисунок 3 (функция 'Hailstone') его оригинальной статьи о мемоизации ("Using Automatic Memoization as a Software Engineering Tool in Real-World AI Systems").

Поэтому я полагаю, что даже если вы заставите работать механику мемоизации, это не ускорит ее в данном случае.

1
ответ дан 30 November 2019 в 09:11
поделиться