Jak wygenerować zapamiętane funkcje rekurencyjne w Clojure?
Próbuję napisać funkcję, która zwraca zapamiętaną funkcję rekurencyjną w Clojure, ale mam problem, aby funkcja rekurencyjna zobaczyła swoje własne zapamiętane powiązania. Czy to dlatego, że nie ma stworzonego var? Ponadto, dlaczego nie mogę użyć memoize na lokalnym wiązaniu utworzonym za pomocą let?
Ten nieco nietypowy Kreator sekwencji Fibonacciego, który zaczyna się od określonej liczby, jest przykładem tego, co chciałbym zrobić:
(defn make-fibo [y]
(memoize (fn fib [x] (if (< x 2)
y
(+ (fib (- x 1))
(fib (- x 2)))))))
(let [f (make-fibo 1)]
(f 35)) ;; SLOW, not actually memoized
Użycie with-local-vars
wydaje się właściwym podejściem, ale nie dla mnie też nie. Chyba nie mogę zamknąć przez vars?
(defn make-fibo [y]
(with-local-vars [fib (fn [x] (if (< x 2)
y
(+ (@fib (- x 1))
(@fib (- x 2)))))]
(memoize fib)))
(let [f (make-fibo 1)]
(f 35)) ;; Var null/null is unbound!?!
Mógłbym oczywiście ręcznie napisać makro, które tworzy zamknięty atom i samodzielnie zarządzać memoizacją, ale miałem nadzieję zrobić to bez takich hackerów.
8 answers
To chyba działa:
(defn make-fibo [y]
(with-local-vars
[fib (memoize
(fn [x]
(if (< x 2)
y
(+ (fib (- x 2)) (fib (dec x))))))]
(.bindRoot fib @fib)
@fib))
with-local-vars
dostarcza tylko powiązania lokalne wątków dla nowo utworzonych VAR-ów, które są wyświetlane po opuszczeniu formularza with-local-vars
; stąd potrzeba .bindRoot
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-11 16:18:05
(def fib (memoize (fn [x] (if (< x 2)
x
(+ (fib (- x 1))
(fib (- x 2)))))))
(time (fib 35))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-11 16:13:01
Istnieje ciekawy sposób, aby to zrobić, który nie opiera się ani na rebindingu, ani na zachowaniu def
. Główną sztuczką jest obejście ograniczeń rekursji poprzez przekazanie funkcji jako argumentu sobie:
(defn make-fibo [y]
(let
[fib
(fn [mem-fib x]
(let [fib (fn [a] (mem-fib mem-fib a))]
(if (<= x 1)
y
(+ (fib (- x 1)) (fib (- x 2))))))
mem-fib (memoize fib)]
(partial mem-fib mem-fib)))
Potem:
> ((make-fibo 1) 50)
20365011074
Co tu się dzieje:
- funkcja rekurencyjna otrzymała nowy argument
mem-fib
. Będzie to zapamiętana wersjafib
, gdy zostanie zdefiniowana. - ciało
fib
jest owinięte w formęlet
, która redefiniuje wywołaniafib
tak, że przechodząmem-fib
w dół do następnego poziomu rekurencji. -
mem-fib
jest definiowane jako memoizedfib
- ... i zostanie przekazany przez
partial
jako pierwszy argument do siebie, aby uruchomić powyższy mechanizm.
Ta sztuczka jest podobna do tej używanej przez kombinator Y do obliczania punktu stałego funkcji w przypadku braku wbudowanego mechanizmu rekurencyjnego.
Biorąc pod uwagę, że def
"widzi" zdefiniowany symbol, nie ma praktycznego powodu, aby przejść do tego sposób, może poza tworzeniem anonimowych rekurencyjnych funkcji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-29 14:27:40
Oto najprostsze rozwiązanie:
(def fibo
(memoize (fn [n]
(if (< n 2)
n
(+ (fibo (dec n))
(fibo (dec (dec n))))))))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-21 18:34:08
Możesz zamknąć rekurencyjny, zapamiętany wzorzec funkcji w makrze, jeśli planujesz użyć go kilka razy.
(defmacro defmemo
[name & fdecl]
`(def ~name
(memoize (fn ~fdecl))))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-13 19:26:41
Twoja pierwsza wersja faktycznie działa, ale nie otrzymujesz wszystkich korzyści z memoizacji, ponieważ używasz algorytmu tylko raz.
Spróbuj tego:
user> (time (let [f (make-fibo 1)]
(f 35)))
"Elapsed time: 1317.64842 msecs"
14930352
user> (time (let [f (make-fibo 1)]
[(f 35) (f 35)]))
"Elapsed time: 1345.585041 msecs"
[14930352 14930352]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-11 14:15:21
Oto krzyżówka Y-combinator i Clojure ' s memoize
:
(defn Y-mem [f]
(let [mem (atom {})]
(#(% %)
(fn [x]
(f #(if-let [e (find @mem %&)]
(val e)
(let [ret (apply (x x) %&)]
(swap! mem assoc %& ret)
ret))))))))
Możesz to makrosugar:
(defmacro defrecfn [name args & body]
`(def ~name
(Y-mem (fn [foo#]
(fn ~args (let [~name foo#] ~@body))))))
Teraz za użycie:
(defrecfn fib [n]
(if (<= n 1)
n
(+' (fib (- n 1))
(fib (- n 2)))))
user=> (time (fib 200))
"Elapsed time: 0.839868 msecs"
280571172992510140037611932413038677189525N
Lub odległość Levenshteina :
(defrecfn edit-dist [s1 s2]
(cond (empty? s1) (count s2)
(empty? s2) (count s1)
:else (min (inc (edit-dist s1 (butlast s2)))
(inc (edit-dist (butlast s1) s2))
((if (= (last s1) (last s2)) identity inc)
(edit-dist (butlast s1) (butlast s2))))))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-22 22:50:08
Możesz wygenerować zapamiętane funkcje rekurencyjne w Clojure z wariantem kombinatora Y. Na przykład, kod factorial
brzmiałby:
(def Ywrap
(fn [wrapper-func f]
((fn [x]
(x x))
(fn [x]
(f (wrapper-func (fn [y]
((x x) y))))))))
(defn memo-wrapper-generator []
(let [hist (atom {})]
(fn [f]
(fn [y]
(if (find @hist y)
(@hist y)
(let [res (f y)]
(swap! hist assoc y res)
res))))))
(def Ymemo
(fn [f]
(Ywrap (memo-wrapper-generator) f)))
(def factorial-gen
(fn [func]
(fn [n]
(println n)
(if (zero? n)
1
(* n (func (dec n)))))))
(def factorial-memo (Ymemo factorial-gen))
Jest to szczegółowo wyjaśnione w tym artykule o Y combinator real life application: recursive memoization in clojure .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-14 19:57:15