Web 何时使用变量而不是函数?
我正在阅读这本书,它告诉我将处理程序(定义如下)作为Var对象而不是函数本身传递,因为函数可以动态更改(这就是wrap-reload所做的) 书上说: “请注意,我们必须从处理程序中创建一个var,以便使用此中间件 这是确保包含当前 返回处理程序函数。如果使用处理程序,则应用程序将 只看到函数的原始值,不会反映更改。” 我真的不明白这意味着什么,变量和c指针相似吗Web 何时使用变量而不是函数?,web,clojure,var,Web,Clojure,Var,我正在阅读这本书,它告诉我将处理程序(定义如下)作为Var对象而不是函数本身传递,因为函数可以动态更改(这就是wrap-reload所做的) 书上说: “请注意,我们必须从处理程序中创建一个var,以便使用此中间件 这是确保包含当前 返回处理程序函数。如果使用处理程序,则应用程序将 只看到函数的原始值,不会反映更改。” 我真的不明白这意味着什么,变量和c指针相似吗 (ns ring-app.core (:require [ring.adapter.jetty :as jetty]
(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"))))
是的,Clojure中的Var类似于C指针。这方面的记录很差 假设您创建一个函数
fred
,如下所示:
(defn fred [x] (+ x 1))
这里实际上有三件事。首先,fred
是一个符号。符号fred
(无引号)和关键字:fred
(由前导的:
字符标记)与字符串“fred”
(两端用双引号标记)之间存在差异。在Clojure看来,每个字符由4个字符组成;i、 e.关键字的冒号和字符串的双引号均不包含在其长度或组成中:
> (name 'fred)
"fred"
> (name :fred)
"fred"
> (name "fred")
"fred"
唯一的区别是如何解释它们。字符串表示任何类型的用户数据。关键字用于以可读的形式表示程序的控制信息(与1=左、2=右等“幻数”相反,我们只使用关键字:left
和:right
)
符号意味着指向事物,就像在Java或C中一样
(let [x 1
y (+ x 1) ]
(println y))
;=> 2
然后,x
指向值1,y
指向值2,我们可以看到打印的结果
(def…
表单引入了一个不可见的第三个元素,变量
(def wilma 3)
<>我们现在有3个对象要考虑。<代码>威尔玛<代码>是一个符号,它指向<代码> var >代码>,它指向值<代码> 3 <代码>。当我们的程序遇到符号<代码>威尔玛< /代码>时,发现< <代码> var var >代码> <强> > Value>强>评估< /强>以产生值3。因此,这就像C中指针的两级间接定向。由于符号和变量都是“自动计算”的,因此这会自动且不可见地发生,您不必考虑变量(事实上,大多数人甚至都没有意识到不可见的中间步骤的存在)
对于上面的函数fred
,存在类似的情况,除了var指向匿名函数(fn[x](+x1))
,而不是值3
,如wilma
我们可以“短路”var的自动评估,如:
> (var wilma)
#'clj.core/wilma
或
其中读卡器宏#'
(磅引号)是调用(var…)的简写方式
特殊形式。请记住,像var
这样的特殊形式是一个内置的编译器,类似于if
或def
,并且与常规函数不同。var
特殊形式返回附加到符号wilma
的var对象。clojure REPL打印var
对象使用相同的速记,因此两个结果看起来相同
一旦我们有了Var
对象,自动求值将被禁用:
> (println (var wilma))
#'clj.core/wilma
如果我们想要得到wilma所指向的值,我们需要使用var get
:
> (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"]
其中,#object[clj.core$fred…]
是Clojure将函数对象表示为字符串的方式
对于web服务器,它可以通过var?
函数或其他方式判断提供的值是处理函数还是指向处理函数的var
如果键入以下内容:
(jetty/run-jetty handler)
双重自动求值将生成handler函数对象,该对象被传递到runjetty
。如果您键入:
(jetty/run-jetty (var handler))
然后,指向处理函数对象的Var
将被传递到runjetty
。然后,runjetty
必须使用if
语句或等效语句来确定它接收到了什么,并调用(Var get…)
如果它收到了一个Var
而不是一个函数。因此,每次通过(Var get…
将返回Var
当前指向的对象。因此,Var
就像C中的全局指针,或者Java中的全局“引用”变量
如果将函数对象传递给运行jetty
,它会保存指向函数对象的“本地指针”,外部世界无法更改本地指针所指的内容
您可以在此处找到更多详细信息:
更新
AS <代码> OlegTheCat < /代码>指出,Culjule在Voice对象上有另一个关于指向Culjule函数的技巧。考虑一个简单的函数:
(defn add-3 [x] (+ x 3))
; `add-3` is a global symbol that points to
; a Var object, that points to
; a function object.
(dotest
(let [add-3-fn add-3 ; a local pointer to the fn object
add-3-var (var add-3)] ; a local pointer to the Var object
(is= 42 (add-3 39)) ; double deref from global symbol to fn object
(is= 42 (add-3-fn 39)) ; single deref from local symbol to fn object
(is= 42 (add-3-var 39))) ; use the Var object as a function
; => SILENT deref to fn object
(let [wilma-long wilma ; a local pointer to the long object
wilma-var (var wilma)] ; a local pointer to the Var object
(is (int? wilma-long)) ; it is a Long integer object
(is (var? wilma-var)) ; it is a Var object
(is= 4 (inc wilma)) ; double deref from global symbol to Long object
(is= 4 (inc wilma-long)) ; single deref from local symbol to Long object
(throws? (inc wilma-var)))) ; Var object used as arg => WILL NOT deref to Long object
(defn unvar
"When passed a clojure var-object, returns the referenced value (via deref/var-get);
else returns arg unchanged. Idempotent to multiple calls."
[value-or-var]
(if (var? value-or-var)
(deref value-or-var) ; or var-get
value-or-var))
如果我们将一个Var对象视为一个函数,Clojure将静默地将其释放到函数对象中,然后使用提供的参数调用该函数对象。因此,我们看到add-3
、add-3-fn
和add-3-Var
三者都将工作。这就是Jetty中发生的情况。它从未意识到我给了它一个Var对象而不是函数,但是Clojure神奇地修补了这个不匹配,而没有告诉你
侧栏:请注意,这只适用于我们的“码头”实际上是
Clojure包装器代码ring.adapter.jetty
,而不是实际的。如果您试图使用
实际的Java函数而不是Clojure包装,它将失败
(let [wilma-long wilma ; a local pointer to the long object
wilma-var (var wilma)] ; a local pointer to the Var object
(is (int? wilma-long)) ; it is a Long integer object
(is (var? wilma-var)) ; it is a Var object
(is= 4 (inc wilma)) ; double deref from global symbol to Long object
(is= 4 (inc wilma-long)) ; single deref from local symbol to Long object
(throws? (inc wilma-var)))) ; Var object used as arg => WILL NOT deref to Long object
(defn unvar
"When passed a clojure var-object, returns the referenced value (via deref/var-get);
else returns arg unchanged. Idempotent to multiple calls."
[value-or-var]
(if (var? value-or-var)
(deref value-or-var) ; or var-get
value-or-var))
(is= 42 (+ 39 (unvar wilma))
(+ 39 (unvar wilma-long))
(+ 39 (unvar wilma-var)))
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(def wilma 3)
; `wilma` is a global symbol that points to
; a Var object, that points to
; a java.lang.Long object of value `3`
(dotest
(is= java.lang.Long (type wilma))
(is= 3 (var-get (var wilma)))
(is= 3 (var-get #'wilma))
; `deref` and `var-get` are interchangable
(is= 3 (deref (var wilma)))
(is= 3 (deref #'wilma))
; the reader macro `@xxx` is a shortcut that translates to `(deref xxx)`
(is= 3 @(var wilma))
(is= 3 @#'wilma)) ; Don't do this - it's an abuse of reader macros.
> (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
(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
(defn g [] (#'f))