我正在阅读clojure web开发书,它告诉我传递处理程序(定义为bellow)var对象而不是函数本身,因为函数将动态更改(这就是wrap-reload所做的)。
这本书说:
"请注意,我们必须从处理程序创建一个var才能使用此中间件 上班。这是确保包含当前的Var对象所必需的 处理程序函数返回。如果我们使用处理程序而不是应用程序 只能看到函数的原始值,并且不会反映出更改。" 我真的不明白这意味着什么,变量类似于c指针?
(ns ring-app.core
(:require [ring.adapter.jetty :as jetty]
[ring.util.response :as response]
[ring.middleware.reload :refer [wrap-reload]]))
(defn handler [request]
(response/response
(str "<html>/<body> your IP is: " (:remote-addr request)
"</body></html>")))
(defn wrap-nocache [handler]
(fn [request]
(-> request
handler
(assoc-in [:headers "Pragma"] "no-cache"))))
这是处理程序调用:
(defn -main []
(jetty/run-jetty
(wrap-reload (wrap-nocache (var handler)))
{:port 3001
:join? false}))
答案 0 :(得分:16)
是的,var类似于C指针。记录很少。
假设您按如下方式定义fred
:
(defn fred [x] (+ x 1))
这里实际上有3件事。首先,fred
是一个符号。符号fred
(无引号)与关键字:fred
(由引导:
字符标记)和字符串"fred"
(由双引号标记)之间存在差异引用两端)。对于Clojure,每个人都由4个字符组成;即,关键字的冒号和字符串的双引号都不包含在它们的长度或构成中:
> (name 'fred)
"fred"
> (name :fred)
"fred"
> (name "fred")
"fred"
唯一的区别是如何解释它们。字符串用于表示任何类型的用户数据。关键字旨在以可读的形式表示程序的控制信息(与“幻数”相反,例如1 =左,2 =右,我们只使用关键字:left
和:right
。 / p>
符号意味着指向事物,就像在Java或C中一样。如果我们说
(let [x 1
y (+ x 1) ]
(println y))
;=> 2
然后x
指向值1,y
指向值2,我们看到打印结果。
(def ...)
表单引入了 不可见 第三个元素var
。所以,如果我们说
(def wilma 3)
我们现在要考虑3个对象。 wilma
是一个符号,指向var
,后者又指向值3
。当我们的程序遇到符号wilma
时, 评估 会找到var
。同样,var是 评估 以产生值3.所以它就像是C中指针的2级间接。因为符号和 var是“自动评估”,这会自动且不可见地发生,你不必考虑var(事实上,大多数人并不真正意识到隐形中间步骤甚至存在)。
对于上面的函数fred
,存在类似的情况,除了var指向匿名函数(fn [x] (+ x 1))
而不是像3
一样的值wilma
。
我们可以将var的自动评估“短路”:
> (var wilma)
#'clj.core/wilma
或
> #'wilma
#'clj.core/wilma
其中阅读器宏#'
(pound-quote)是调用(var ...)
特殊表单的简写方式。请记住,像var
这样的特殊表单是内置的编译器,如'if'或'def',并且与常规函数不同。 var
特殊表单会返回附加到符号var
的{{1}}对象。 clojure REPL使用相同的速记打印wilma
对象,因此两个结果看起来都一样。
一旦我们有了var对象,就会禁用自动评估:
var
如果我们想要达到> (println (var wilma))
#'clj.core/wilma
指向的值,我们需要使用wilma
:
var-get
同样的事情适用于fred:
> (var-get (var wilma))
3
> (var-get #'wilma)
3
其中> (var-get #'fred)
#object[clj.core$fred 0x599adf07 "clj.core$fred@599adf07"]
> (var-get (var fred))
#object[clj.core$fred 0x599adf07 "clj.core$fred@599adf07"]
的东西是Clojure将函数对象表示为字符串的方式。
关于Web服务器,它可以通过#object[clj.core$fred ...]
函数或其他方式告知提供的值是处理函数还是指向处理函数的var。
如果您键入以下内容:
var?
双重自动评估将产生处理程序函数对象,该对象将传递给(jetty/run-jetty handler)
。相反,如果您键入:
run-jetty
然后指向处理函数对象的(jetty/run-jetty (var handler))
将传递给var
。然后,run-jetty
必须使用run-jetty
语句或等效语句来确定收到的内容,如果收到if
而不是函数,则调用(var-get ...)
。因此,每次通过var
都会返回(var-get ...)
当前指向的对象。因此,var
的作用类似于C中的全局指针,或者是Java中的全局“引用”变量。
如果将函数对象传递给var
,它会将“本地指针”保存到函数对象中,并且外部世界无法更改本地指针所指的内容。
您可以在此处找到更多详细信息:
答案 1 :(得分:8)
希望这个小例子能让你走上正轨:
> (defn your-handler [x] x)
#'your-handler
> (defn wrap-inc [f]
(fn [x]
(inc (f x))))
> #'wrap-inc
> (def your-app-with-var (wrap-inc #'your-handler))
#'your-app-with-var
> (def your-app-without-var (wrap-inc your-handler))
#'your-app-without-var
> (your-app-with-var 1)
2
> (your-app-without-var 1)
2
> (defn your-handler [x] 10)
#'your-handler
> (your-app-with-var 1)
11
> (your-app-without-var 1)
2
对此的直觉是,在创建处理程序时使用var时,实际上是在传递一个&#34;容器&#34;通过定义具有相同名称的var,可以在某种程度上改变其内容。如果你不使用var(比如在your-app-without-var
中),你传递的是#&#34; container&#34;的当前值,不能以任何方式重新定义。
答案 2 :(得分:7)
已经有几个好的答案。只想添加这个警告:
(defn f [] 10)
(defn g [] (f))
(g) ;;=> 10
(defn f [] 11)
;; -Dclojure.compiler.direct-linking=true
(g) ;;=> 10
;; -Dclojure.compiler.direct-linking=false
(g) ;;=> 11
因此,当direct linking打开时,通过var的间接替换为直接静态调用。与处理程序的情况类似,但随后使用每个 var调用,除非您明确引用var,例如:
(defn g [] (#'f))