Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Euler#14项目和Clojure中的备忘录_Clojure_Memoization - Fatal编程技术网

Euler#14项目和Clojure中的备忘录

Euler#14项目和Clojure中的备忘录,clojure,memoization,Clojure,Memoization,作为一个初学clojurian的人,我通过这些问题来学习语言。这绝对是提高技能和获得自信的好方法。我刚把我的答案写完。它工作得很好,但是为了让它高效地运行,我必须实现一些备忘录。我不能使用预先打包的memoize函数,因为我的代码是以这样的方式构造的,而且我认为不管怎么说,这都是一次很好的体验。我的问题是,是否有一种很好的方法将缓存封装在函数本身中,或者是否必须像以前那样定义一个外部缓存。另外,如果有任何让我的代码更地道的建议,我们将不胜感激 (use 'clojure.test) (def

作为一个初学clojurian的人,我通过这些问题来学习语言。这绝对是提高技能和获得自信的好方法。我刚把我的答案写完。它工作得很好,但是为了让它高效地运行,我必须实现一些备忘录。我不能使用预先打包的
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))))
(使用“clojure.test”)
(def mem(原子{}))
(带测试)
(定义链长度
([x](链长x x 0))
([起始值x c]
(如果让[e(last(find@mem x))]
(让[ret(+c e)]
(交换!成员关联开始值ret)
ret)
(如果(=c 1000000)
开始数
(让[l(链长c)]
(如果(>l最大值)
(重复(+1 c)l c)
(重复(+1 c)最大开始次数()()())))
(is(=837799(最长链)))

由于您希望在所有调用
链长
的调用之间共享缓存,因此将
链长
写为
(让[mem(atom{})](defn chain length…)
,这样它将只对
链长可见


在这种情况下,由于最长的链足够小,您可以使用朴素的递归方法定义
链长度
,并在此基础上使用Clojure的内置
记忆功能。

您可以在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捕获并可用于存储值。

这是一个惯用的(?)版本,使用普通的旧
内存化

(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)])))
<> >如果你想使用<代码> ReCue<代码>,先考虑<代码> map < /COD>或<代码>减少。他们经常做你想做的事情,有时做得更好/更快,因为他们利用了组块SEQs。


(inc x)
类似于
(+1 x)
,但
inc
的速度大约是前者的两倍。

谢谢!既然你已经解释过了,这似乎是显而易见的。我突然想到了使用简单递归和记忆的想法,但我认为这将是一种更好的学习体验和更“可伸缩性”引导。这基本上就是内置的memoize函数的工作方式。我认为这与我当前编写函数的方式不一样,因为递归调用总是有不同的参数,并且缓存值永远不会返回。这不是一个真正的问题,因为只需要使用其中一个参数。如果已知x,则可以将其缓存计数添加到状态参数中。我尝试了您的版本,但(最长链2 1000000)导致StackOverflower错误。100万来自Project Euler练习#14.Try
(最长链1 1000000)
它应该可以工作。它必须先缓存1的值。同样的错误。如果解决了,我会感到惊讶,因为使用(链长2),它调用(链长1)在第二种情况下,它也会被缓存。我想它在你的机器上工作吧?我尝试了Clojure版本1.1和1.2。是的,你是对的,duh.Hmm。但是它在我的机器上工作,Clojure 1.2.0处于最前沿。然后我尝试了
(最长链10000000)
,并得到了一个OutOfMemoryError错误(“超出了GC开销限制”)在我得到堆栈溢出之前。这是递归+记忆的不好的地方。你必须祈祷值被缓存得足够快,当你到达第一百万次迭代时,它有足够多的记忆部分结果,而不会杀死堆栈。在这一点上,你总是在调侃灾难。可能是我的JVM有更多的内存可以使用我的代码版本需要一个更大的JVM大小才能运行100万次。我只是想感谢您让我接触到project euler。我也在尝试学习clojure。非常次要的一点,但是(inc c)可能比(+1 c)更惯用。