9

新生児のクロジュリアンとして、私は言語を学ぶ方法としてプロジェクトオイラーの問題を経験することを勧められました。それは間違いなくあなたのスキルを向上させ、自信を得るのに最適な方法です。問題#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

これは、昔ながらの慣用句(?)バージョンです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ますが、約2倍の速度です。

于 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関数が1回だけ呼び出されていることがわかります。

したがって、「キャッシュ」はclojureによってキャプチャされ、値を格納するために使用できます。

于 2010-06-01T18:51:17.827 に答える