clojure Atom 示例代码:为什么 let 和 def 之间有区别?
我阅读了文档 https://clojure.org/reference/atoms 并尝试了有关斐波那契的代码。
根据下面的输出,在测试 3 中,使用 def
所花费的时间与预期一样短。但在测试2中,使用let
而不是def
重新定义fib需要相当长的时间。我想知道为什么 let
的工作方式与 def
不同?
(defn fib "original fib function" [n]
(if (<= n 1)
n
(+ (fib (dec n)) (fib (- n 2)))))
(defn memorize [f]
(let [mem (atom {})]
(fn [& args]
(if-let [e (find @mem args)]
(val e)
(let [ret (apply f args)]
(swap! mem assoc args ret)
ret)))))
(deftest test-memorize
(testing "test memoize 1 - using fib"
(println "test 1")
(is (> (time (fib 35)) 0)))
(testing "test memoize 2 - uising `(let [fib (memorize fib)])"
(println "test 2")
(let [fib (memorize fib)]
(is (> (time (fib 35)) 0))))
(testing "test memoize 3 - using `(def fib (memorize fib))"
(println "test 3")
(def fib (memorize fib))
(is (> (time (fib 35)) 0)))
输出:
$ lein test :only app.model-test/test-memorize
lein test app.model-test
test 1
"Elapsed time: 1418.226187 msecs"
test 2
"Elapsed time: 1391.479784 msecs"
test 3
"Elapsed time: 0.215439 msecs"
Ran 1 tests containing 3 assertions.
0 failures, 0 errors.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
...为什么让我们不像DEF那样工作? ... 因为它们的目的不同。
def
创建或更新的值,一个全局var。它不会创建本地绑定。 (将定义
的球拍进行比较,在顶级使用来创建本地绑定的函数。) https://clojure.org/reference/special_forms#def正在重新定义 global
fib
的值是返回的值,请记住fib。当用
计算FIB 33因此。 FIB的递归实现创建了一棵计算树。通过重新定义(FIB 35)调用
记忆的代码将调用以前命名fib
。原始,fib呼叫,按名称,fib
现在是记忆的版本,用于计算fib 34。fib 34调用的计算fib
fibfib
在顶部注入记忆工作,第一个调用(fib 35)
是将树修剪为一个分支。对于
,让
做同样的事情将需要Clojure使用动态范围而不是词汇范围。观察
fib
的一些方法正在顶部重新定义。运行测试后,(DOC FIB)
,请看到您的文档字符串不存在。因为fib
不再定义为您编写的函数。交换LET和DEF测试的顺序。然后,将获得回忆的FIB。
如果您使用
fn
来定义FIB,则在其中呼叫fib的调用将始终转向自身,因为它正在使用
fn <
fib
fn <fib
/代码>表格。... why let does NOT work as the same way as def? ... Because they serve different purposes.
def
creates, or updates the value of, a global var. It does not creat a local binding. (Compare to Racket wheredefine
is used at the top level to create global bindings and within functions for local bindings.) https://clojure.org/reference/special_forms#deflet
creates local bindings. https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/letIs redefining the global value of
fib
to be the value returned bymemorize fib
. When the called with(fib 35)
the memorize code will call what used to be namedfib
. That, original, fib calls, by name,fib
which is now the memoized version, of fib to calculate fib 34. The calculation of fib 34 callsfib
to calculate fib 33. So when(fib 34)
returns and(fib 35)
calls(fib 33)
, but that value is cached already. The recursive implementation of fib creates a tree of computation. By redefiningfib
at the top to inject memoization the work, on the first call to(fib 35)
is trimming the tree to a single branch.For
let
to do the same thing would require Clojure to use dynamic scope instead of lexical scope.Some ways to observe that
fib
is being redefined at the top. After running you tests,(doc fib)
and see that your documentation string is not there. Becausefib
is no longer defined as the function you wrote.Swap the order of the let and def tests. Then let will be getting the memoized fib.
If you define fib using
fn
as such:The call to fib inside will always go to itself, because it is using the local binding for
fib
from thefn
form.正如香农·塞弗伦斯已经回答的那样。
def
创建一个全局变量。let
创建本地绑定。哪种效果对您的函数有这种差异?
情况 2 (let):
您创建一个新函数(fib 的记忆版本)并将其在本地绑定到名称 fib。然后你调用
fib
。fib
递归调用fib
,但在defn fib ...
内部,fib 指的是全局定义的 fib,它不会被记忆。所以实际上,没有缓存。对
defn fib
的影响:情况 3 (def):
您创建
fib
的记忆版本,然后全局重新声明fib 的值是新的记忆版本。您调用
fib
。Fib
调用自身,但现在它指的是新定义的版本,该版本已被记忆。对 defn fib 的影响:
As Shannon Severance already answered.
def
creates a global var.let
creates a local binding.Which effect has this difference on your function?
Case 2 (let):
You create a new function (a memoized version of fib) and bind it locally to the name fib. Then you call
fib
.fib
callsfib
recursively, but insidedefn fib ...
fib refers to the globally defined fib, which is not memoized. So effectively, there is no caching.Effect on
defn fib
:Case 3 (def):
You create a memoized version of
fib
, then you globally redeclarefib
's value to be the new memoized version. You callfib
.Fib
calls itself, but now it refers to the new defined version, which is memoized.Effect on
defn fib
: