私はclojureWeb開発ブックを読んでいますが、関数は動的に変化するため、関数自体ではなくハンドラー(以下で定義)のvarオブジェクトを渡すように指示されています(これがwrap-reloadの機能です)。
本は言う:
「このミドルウェアが機能するためには、ハンドラーからvarを作成する必要があることに注意してください。これは、現在のハンドラー関数を含むVarオブジェクトが返されるようにするために必要です。代わりにハンドラーを使用した場合、アプリは関数の元の値と変更は反映されません。」これが何を意味するのかよくわかりません。varsは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}))
はい、Clojureの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
ます。
シンボルは、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
ここで、リーダーマクロ#'
(ポンドクォート)は、(var ...)
特別なフォームを呼び出す簡単な方法です。のような特殊な形式var
は、if
またはのようなコンパイラ組み込みdef
であり、通常の関数と同じではないことに注意してください。var
特殊な形態は、シンボルに取り付けVarのオブジェクトを返しますwilma
。clojure REPLVar
は同じ省略形を使用してオブジェクトを印刷するため、両方の結果は同じように見えます。
私たちが持っていたら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)
二重自動評価により、ハンドラー関数オブジェクトが生成され、これがに渡されrun-jetty
ます。代わりに、次のように入力した場合:
(jetty/run-jetty (var handler))
次に、Var
ハンドラー関数オブジェクトを指すwhichがに渡されrun-jetty
ます。次に、ステートメントまたは同等のものrun-jetty
を使用して、if
受信したものを判別し、関数の代わりに(var-get ...)
受信した場合は呼び出す必要Var
があります。したがって、毎回(var-get ...)
、Var
現在ポイントしているオブジェクトが返されます。したがって、Var
Cのグローバルポインタ、またはJavaのグローバル「参照」変数のように機能します。
関数オブジェクトをに渡すと、関数オブジェクトへrun-jetty
の「ローカルポインタ」が保存され、外部ポインタがローカルポインタの参照先を変更する方法はありません。
詳細については、こちらをご覧ください。
OlegTheCat
尖ったアウトを持って、Clojureのはまだヴァールについてその袖まで別のトリックはClojureの機能をポイントするオブジェクトを持っています。単純な関数を考えてみましょう。
(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
Varオブジェクトを関数として扱う場合、Clojureはそれを関数オブジェクトにサイレントにデリファレンスし、指定された引数を使用してその関数オブジェクトを呼び出します。我々は3つのすべてのことを見てadd-3
、add-3-fn
そしてadd-3-var
動作します。これがJettyで起こっていることです。関数ではなくVarオブジェクトを指定したことに気付くことはありませんが、Clojureは、通知せずにその不一致を魔法のように修正します。
サイドバー:私たちの「桟橋」が実際にClojureのラッパーコードであるので、これはのみ動作しますのでご注意ください
ring.adapter.jetty
、とない実際の桟橋ウェブサーバのJava。Clojureラッパーの代わりに実際のJava関数を使用してこのトリックに依存しようとすると、失敗します。実際、proxy
Clojure関数をJavaコードに渡すには、のようなClojureラッパーを使用する必要があります。
Varオブジェクトを関数以外のものとして使用する場合、あなたを救うような守護天使はありません。
(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
したがって、関数を期待していて、誰かが関数を指すVarオブジェクトを提供した場合、Clojureがサイレントに問題を修正するので、問題ありません。関数以外のものを期待していて、誰かがそのことを指すVarオブジェクトを提供した場合、あなたはあなた自身です。
このヘルパー関数について考えてみましょう。
(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)))
あることに注意してください3個の問題を混同することができ二重性は:
var-get
とderef
Clojureのと同じことを行いますVar
#'xxx
はに翻訳されます(var xxx)
@xxx
はに翻訳されます(deref xxx)
したがって、(紛らわしいことに!)同じことを行う方法はたくさんあります。
(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.
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加