9

作为一名 clojurian 新手,有人建议我通过Project Euler问题作为学习语言的一种方式。这绝对是提高技能和获得信心的好方法。我刚刚完成了对问题 #14的回答。它工作得很好,但为了让它有效地运行,我必须实现一些记忆。由于我的代码的结构方式,我无法使用预打包的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))))
4

3 回答 3

3

由于您希望缓存在 的所有调用之间共享chain-length,因此您将编写chain-length(let [mem (atom {})] (defn chain-length ...))仅对chain-length.

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

于 2010-06-01T18:23:29.173 回答
2

这是一个使用普通 old 的惯用(?)版本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先。他们经常做你想做的事,有时做得更好/更快,因为他们利用了分块序列。

(inc x)就像(+ 1 x),但inc大约快两倍。

于 2010-06-01T19:32:59.333 回答
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 捕获,可用于存储值。

于 2010-06-01T18:51:17.827 回答