在clojure 1.1中,所有调用都是动态的,这意味着您可以在REPL重新定义函数,它将自动包含在正在运行的程序中。这对于dotrace这样的东西也很好。
在clojure 1.2中,许多调用似乎是静态链接的,如果我想替换一个函数,有时我必须找到它被调用的所有位置,并将#‘放在它们前面。
更糟糕的是,我不能预测我需要在哪里做这件事。
有没有可能回到以前的默认动态链接?也许如果你需要额外的速度,你可以在生产应用程序中重新打开它,但对于开发,我更喜欢1.1的行为。
我希望有像*warn-on-reflection*这样的编译器选项。
编辑:
我对发生了什么感到困惑。更具体地说,这里有两个函数。我更喜欢第二个人的行为。我如何才能让第一个像第二个一样,就像我在1.1中所做的那样?
user> (clojure-version)
"1.2.0"
user> (defn factorial[n] (if (< n 2) n (* n (factorial (dec n)))))
#'user/factorial
user> (require 'clojure.contrib.trace)
user> (clojure.contrib.trace/dotrace (factorial) (factorial 10))
TRACE t1670: (factorial 10)
TRACE t1670: => 3628800
user> (defn factorial[n] (if (< n 2) n (* n (#'factorial (dec n)))))
#'user/factorial
user> (clojure.contrib.trace/dotrace (factorial) (factorial 10))
TRACE t1681: (factorial 10)
TRACE t1682: | (factorial 9)
TRACE t1683: | | (factorial 8)
TRACE t1684: | | | (factorial 7)
TRACE t1685: | | | | (factorial 6)
TRACE t1686: | | | | | (factorial 5)
TRACE t1687: | | | | | | (factorial 4)
TRACE t1688: | | | | | | | (factorial 3)
TRACE t1689: | | | | | | | | (factorial 2)
TRACE t1690: | | | | | | | | | (factorial 1)
TRACE t1690: | | | | | | | | | => 1
TRACE t1689: | | | | | | | | => 2
TRACE t1688: | | | | | | | => 6
TRACE t1687: | | | | | | => 24
TRACE t1686: | | | | | => 120
TRACE t1685: | | | | => 720
TRACE t1684: | | | => 5040
TRACE t1683: | | => 40320
TRACE t1682: | => 362880
TRACE t1681: => 3628800
3628800编辑(针对整个问题,并更改标题):
Joost在下面指出,这里实际发生的是factorial中的self调用正在被优化。我不明白为什么会这样做,因为你不能做那么多递归的自调用而不破坏堆栈,但它解释了观察到的行为。也许这与匿名自我呼叫有关。
我问这个问题的最初原因是,我正在尝试编写http://www.learningclojure.com/2011/03/hello-web-dynamic-compojure-web.html,我对我必须键入#‘才能获得预期行为的地方数量感到恼火。这一点和dotrace让我认为一般的动态行为已经消失了,在某些地方工作的即时重新定义必须通过一些聪明的技巧来完成。
回想起来,这似乎是一个奇怪的结论,但现在我只是感到困惑(哪个更好!)。这一切有什么参考资料吗?我希望能有一个通用的理论来说明什么时候可以工作,什么时候不能工作。
发布于 2011-03-23 06:35:29
我想你搞错了。在clojure 1.2中,当然可以重定义函数,并且调用代码将调用新的定义。在1.3中,这看起来可能会有一些变化,但1.3还没有完全修复。
发布于 2011-03-24 08:07:05
Clojure中的一切都是完全动态的,但您必须注意何时使用Var,以及何时使用函数,即该Var的当前值。
在您的第一个示例中:
(defn factorial [n] (if (< n 2) n (* n (factorial (dec n)))))factorial符号被解析为变量#'user/factorial,然后由编译器进行求值以获得其当前值,即已编译的函数。此计算仅在编译函数时发生一次。第一个示例中的factorial是定义函数时的Var #'user/factorial的值。
在第二个示例中:
(defn factorial [n] (if (< n 2) n (* n (#'factorial (dec n)))))您已经显式请求了Var #'user/factorial。调用Var与取消引用Var并调用其(函数)值具有相同的效果。此示例可以更明确地编写为:
(defn factorial [n] (if (< n 2) n (* n ((deref (var factorial)) (dec n)))))Var宏(我在很久以前编写的)使用binding将clojure.contrib.trace/dotrace临时重新绑定到不同的值。它不会更改任何函数的定义。相反,它创建了一个新函数,该函数调用原始函数并打印跟踪行,然后将该函数绑定到Var。
在您的第一个示例中,由于原始函数是使用factorial函数的值编译的,因此dotrace不起作用。在第二个示例中,每次调用factorial函数都会查找#'user/factorial变量的当前值,因此每次调用都会看到dotrace创建的备用绑定。
发布于 2011-03-24 23:12:34
为了让人们不会对这些问题感到困惑,我在这里解释一下与web开发相关的“问题”。
这是Ring的局限性,而不是Clojure的局限性(实际上,这是Java Jetty库的局限性)。通常情况下,您始终可以重新定义函数。但是,不能重新定义提供给Jetty服务器进程的处理程序。您的函数正在更新,但是Jetty服务器看不到这些更新。在这种情况下,提供一个var作为处理程序是要解决的问题。
但请注意,var并不是真正的处理程序。必须为Jetty服务器提供一个代理,因此Ring使用proxy创建一个代理来关闭您的处理程序。这就是为什么为了让处理程序动态更新,它需要是var而不是fn。
https://stackoverflow.com/questions/5398569
复制相似问题