将接受惰性序列作为输入的Clojure函数记忆化
当记忆函数的参数是序列时,如何进行记忆工作将接受惰性序列作为输入的Clojure函数记忆化,clojure,Clojure,当记忆函数的参数是序列时,如何进行记忆工作 (defn foo ([x] (println "Hello First") (reduce + x)) ([x y] (println "Hello Second") (reduce + (range x y)))) (def baz (memoize foo)) 传递一个参数: (一) (二) 通过2个参数: (一) (二) 当传递2个参数时,函数的第二次运行似乎是我所期望的 然而,使用向量似乎是可行的 (time (baz
(defn foo
([x] (println "Hello First") (reduce + x))
([x y] (println "Hello Second") (reduce + (range x y))))
(def baz (memoize foo))
传递一个参数:
(一)
(二)
通过2个参数:
(一)
(二)
当传递2个参数时,函数的第二次运行似乎是我所期望的
然而,使用向量似乎是可行的
(time (baz [1 2 3 5 3 5 7 4 6 7 4 45 6 7])) ;=> Hello First "Elapsed time: 0.294963 msecs"
(time (baz [1 2 3 5 3 5 7 4 6 7 4 45 6 7])) ;=> "Elapsed time: 0.068229 msecs"
memoize
可以处理序列,您只需将苹果与苹果进行比较memoize
在以前使用的散列映射中查找参数,结果是比较序列。比较长序列需要很长时间,无论它们是否为向量:
user> (def x (vec (range 1000000)))
;; => #'user/x
user> (def y (vec (range 1000000)))
;; => #'user/y
user> (time (= x y))
"Elapsed time: 64.351274 msecs"
;; => true
user> (time (baz x))
"Elapsed time: 67.42694 msecs"
;; => 499999500000
user> (time (baz x))
"Elapsed time: 73.231174 msecs"
;; => 499999500000
使用非常短的输入序列时,计时由函数内部的reduce
控制。但是对于很长的时间,你看到的大部分时间实际上是memoize
中的比较时间
因此,从技术上讲,
memoize
对所有序列都有效。但是“从技术上”工作并不意味着“有用”。正如你自己所发现的那样,使用昂贵的比较语义输入是无用的(实际上甚至是有害的)。您的第二个签名解决了这个问题。Hmm。如何处理这个问题是一件棘手的事情——您需要使用整个序列来确定身份,以便进行比较,但能够支持ISeq的优势之一是可能会导致懒惰。因此,我可以看到当前语义的论点——我们不希望为了潜在地启用缓存而增加调用的前期成本(以完全实现理论上可能是无限的序列)……在实现层,问题是当序列用作映射中的键时会发生什么。也就是说,序列是否以反映其内容的方式支持.hashCode
(因此O(n)需要评估),以反映其身份的方式支持,或者根本不支持?@CharlesDuffy-序列比较的任何实现(hash码等)只能优化不平等的情况。因此,矛盾的是,当memoize
被设计为有用时——当你在地图中有相等的键时——你最终会得到O(n)比较时间,不管实现是什么。故事的寓意——memoize
并非设计用于长序列作为参数。
(time (baz 1 1000000)) ;=> Hello Second "Elapsed time: 18.619768 msecs"
(time (baz 1 1000000)) ;=> "Elapsed time: 0.069684 msecs"
(time (baz [1 2 3 5 3 5 7 4 6 7 4 45 6 7])) ;=> Hello First "Elapsed time: 0.294963 msecs"
(time (baz [1 2 3 5 3 5 7 4 6 7 4 45 6 7])) ;=> "Elapsed time: 0.068229 msecs"
user> (def x (vec (range 1000000)))
;; => #'user/x
user> (def y (vec (range 1000000)))
;; => #'user/y
user> (time (= x y))
"Elapsed time: 64.351274 msecs"
;; => true
user> (time (baz x))
"Elapsed time: 67.42694 msecs"
;; => 499999500000
user> (time (baz x))
"Elapsed time: 73.231174 msecs"
;; => 499999500000