何时使用Var而不是函数?

Mau*_*rex 10 var clojure web

我正在浏览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))而不是3wilma.

我们可以将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,它会向函数对象保存一个"本地指针",外部世界无法改变本地指针所指的内容.

你可以在这里找到更多细节:


Ole*_*Cat 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
Run Code Online (Sandbox Code Playgroud)

对此的直觉是,在创建处理程序时使用var时,实际上是在传递具有某个值的"容器",通过定义具有相同名称的var,将来可以更改其内容.如果不使用var(如in your-app-without-var),则传递此"容器"的当前值,不能以任何方式重新定义.


Mic*_*ent 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
Run Code Online (Sandbox Code Playgroud)

因此,当启用直接链接时,通过var的间接替换为直接静态调用.与处理程序的情况类似,但随后对每个 var调用,除非您明确引用var,如:

(defn g [] (#'f))
Run Code Online (Sandbox Code Playgroud)