项目Euler#14和Clojure中的memoization

时间:2010-06-01 18:01:12

标签: clojure memoization

作为一个新手clojurian,recommended to me我通过Project Euler问题作为学习语言的一种方式。它绝对是提高技能和获得信心的好方法。我刚刚完成了对problem #14的回答。它工作正常,但要让它有效运行,我必须实现一些memoization。由于我的代码的结构方式,我无法使用预先打包的memoize函数,而且我认为无论如何都是一个很好的经验。我的问题是,是否有一种很好的方法将我的缓存封装在函数本身中,或者如果我必须像我所做的那样定义一个外部缓存。此外,任何使我的代码更惯用的提示都将受到赞赏。

(use 'clojure.test)

(def mem (atom {}))

(with-test
  (defn chain-length      
    ([x] (chain-length x x 0))     
    ([start-val x c]
      (if-let [e (last(find @mem x))]
        (let [ret (+ c e)]
          (swap! mem assoc start-val ret)
          ret)   
        (if (<= x 1)               
          (let [ret (+ c 1)]
            (swap! mem assoc start-val ret)
            ret)                  
          (if (even? x)            
            (recur start-val (/ x 2) (+ c 1))
            (recur start-val (+ 1 (* x 3)) (+ c 1)))))))
  (is (= 10 (chain-length 13))))

(with-test
  (defn longest-chain
    ([] (longest-chain 2 0 0))
    ([c max start-num]
      (if (>= c 1000000)
        start-num
        (let [l (chain-length c)]
          (if (> l max)
            (recur (+ 1 c) l c)
            (recur (+ 1 c) max start-num))))))
  (is (= 837799 (longest-chain))))

3 个答案:

答案 0 :(得分:3)

由于您希望在chain-length的所有调用之间共享缓存,因此您应将chain-length写为(let [mem (atom {})] (defn chain-length ...)),以便只有chain-length可见。< / p>

在这种情况下,由于最长链足够小,您可以使用朴素递归方法定义chain-length并使用Clojure的内置memoize函数。

答案 1 :(得分:2)

这是使用普通旧版memoize的惯用(?)版本。

(def chain-length
     (memoize
      (fn [n]
        (cond
         (== n 1)  1
         (even? n) (inc (chain-length (/ n 2)))
         :else     (inc (chain-length (inc (* 3 n))))))))

(defn longest-chain [start end]
  (reduce (fn [x y]
            (if (> (second x) (second y)) x y))
          (for [n (range start (inc end))]
            [n (chain-length n)])))

如果您有使用recur的冲动,请先考虑mapreduce。他们经常做你想做的事,有时做得更好/更快,因为他们利用了chunked seqs。

(inc x)(+ 1 x)类似,但inc大约快两倍。

答案 2 :(得分:1)

您可以在clojure中捕获周围环境:

(defn my-memoize [f] 
  (let [cache (atom {})] 
    (fn [x] 
      (let [cy (get @cache x)] 
        (if (nil? cy) 
          (let [fx (f x)] 
          (reset! cache (assoc @cache x fx)) fx) cy)))))


(defn mul2 [x] (do (print "Hello") (* 2 x)))
(def mmul2 (my-memoize mul2))  
user=> (mmul2 2)
Hello4
user=>  (mmul2 2) 
4

你看到mul2功能只被调用一次。

因此,“缓存”由clojure捕获,可用于存储值。