我正在浏览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"))))
Run Code Online (Sandbox Code Playgroud)
这是处理程序调用:
(defn -main []
(jetty/run-jetty
(wrap-reload (wrap-nocache (var handler)))
{:port 3001
:join? false}))
Run Code Online (Sandbox Code Playgroud)
Ala*_*son 20
是的,var类似于C指针.记录很少.
假设您定义fred如下:
(defn fred [x] (+ x 1))
Run Code Online (Sandbox Code Playgroud)
这里实际上有三件事.首先,fred是一个象征.符号fred(无引号)和关键字:fred(由前导:字符标记)和字符串"fred"(两端用双引号标记)之间存在差异.对于Clojure,每个人都由4个字符组成; 即,关键字的冒号和字符串的双引号都不包含在它们的长度或组成中:
> (name 'fred)
"fred"
> (name :fred)
"fred"
> (name "fred")
"fred"
Run Code Online (Sandbox Code Playgroud)
唯一的区别是如何解释它们.字符串用于表示任何类型的用户数据.关键字意味着以可读的形式表示程序的控制信息(与"幻数"相反,例如1 =左,2 =右,我们只使用关键字:left和:right.
符号意味着指向事物,就像在Java或C中一样.如果我们说
(let [x 1
y (+ x 1) ]
(println y))
;=> 2
Run Code Online (Sandbox Code Playgroud)
然后x指向值1,y指向值2,我们看到打印结果.
的(def ...)形式引入了一个隐形第三元件,所述var.所以,如果我们说
(def wilma 3)
Run Code Online (Sandbox Code Playgroud)
我们现在要考虑3个对象.wilma是一个符号,指向a var,后者又指向该值3.当我们的程序遇到符号时wilma,会对其进行评估以找到符号var.同样,变量被评估得值3.因此,它是像C的指针的2级间接由于两个符号和风险价值是"自动计算",这个自动和无形地发生,你不我必须考虑var(事实上,大多数人并不是真的意识到隐形中间步骤甚至存在).
对于fred上面的函数,存在类似的情况,除了var指向匿名函数(fn [x] (+ x 1))而不是3像wilma.
我们可以将var的自动评估"短路":
> (var wilma)
#'clj.core/wilma
Run Code Online (Sandbox Code Playgroud)
要么
> #'wilma
#'clj.core/wilma
Run Code Online (Sandbox Code Playgroud)
读者宏#'(pound-quote)是调用(var ...)特殊形式的简便方法.请记住,一个特殊的形式,如var内置的编译器,如'if'或'def',并且与常规函数不同.的var特殊形式返回var附连到符号对象wilma.clojure REPL var使用相同的速记打印对象,因此两个结果看起来都一样.
获得var对象后,将禁用自动评估:
> (println (var wilma))
#'clj.core/wilma
Run Code Online (Sandbox Code Playgroud)
如果我们想要获得指向的值wilma,我们需要使用var-get:
> (var-get (var wilma))
3
> (var-get #'wilma)
3
Run Code Online (Sandbox Code Playgroud)
同样的事情适用于fred:
> (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"]
Run Code Online (Sandbox Code Playgroud)
其中的#object[clj.core$fred ...]东西是Clojure将函数对象表示为字符串的方式.
关于Web服务器,它可以通过var?函数或其他方式告知所提供的值是处理函数还是指向处理函数的var.
如果您键入以下内容:
(jetty/run-jetty handler)
Run Code Online (Sandbox Code Playgroud)
double auto-evaluation将产生处理函数对象,该对象将被传递给run-jetty.相反,如果您输入:
(jetty/run-jetty (var handler))
Run Code Online (Sandbox Code Playgroud)
那么var指向处理函数的对象将被传递给run-jetty.然后,run-jetty必须使用if语句或等效语句来确定它已收到的内容,(var-get ...)如果已收到var而不是函数,则调用它.因此,每次通过(var-get ...)将返回var当前指向的对象.因此,这些var行为就像C中的全局指针或Java中的全局"引用"变量.
如果传递一个函数对象run-jetty,它会向函数对象保存一个"本地指针",外部世界无法改变本地指针所指的内容.
你可以在这里找到更多细节:
希望这个小例子能让你走上正轨:
> (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
Run Code Online (Sandbox Code Playgroud)
对此的直觉是,在创建处理程序时使用var时,实际上是在传递具有某个值的"容器",通过定义具有相同名称的var,将来可以更改其内容.如果不使用var(如in your-app-without-var),则传递此"容器"的当前值,不能以任何方式重新定义.
已经有几个很好的答案.只是想添加这个警告:
(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
Run Code Online (Sandbox Code Playgroud)
因此,当启用直接链接时,通过var的间接替换为直接静态调用.与处理程序的情况类似,但随后对每个 var调用,除非您明确引用var,如:
(defn g [] (#'f))
Run Code Online (Sandbox Code Playgroud)