代码之家  ›  专栏  ›  技术社区  ›  dbyrne

Euler#14项目与Clojure中的记忆

  •  9
  • dbyrne  · 技术社区  · 14 年前

    作为一个初学clojurian的人 recommended to me Project Euler 问题作为学习语言的一种方式。这绝对是一个伟大的方式来提高你的技能和获得信心。我刚把我的答案写完 problem #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))))
    
    3 回复  |  直到 7 年前
        1
  •  3
  •   Brian    14 年前

    因为您希望缓存在 chain-length 链条长度 作为 (let [mem (atom {})] (defn chain-length ...)) 所以它只有 链条长度 .

    在这种情况下,由于最长的链足够小,您可以定义 链条长度 memoize 在那上面起作用。

        2
  •  2
  •   Brian Carper    14 年前

    这里有一个惯用的(?)版本,使用的是纯旧的 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 map reduce 第一。他们经常做你想做的事,有时做得更好/更快,因为他们利用了分块seq。

    (inc x) 就像 (+ 1 x) inc 速度是原来的两倍。

        3
  •  1
  •   Peter Tillemans    14 年前

    您可以在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函数只调用一次。