2017-05-24 11 views
0

私はClojureプログラムのいくつかを書いてきましたが、私は閉鎖を使用したことはほとんど思い出せません。Clojureのクロージャをいつ使用するのですか?

Clojureでクロージャを使用するための最良の使用例は何ですか?

また、初心者にも役立つユースケースや例を提供できますか?

+3

私は本当にこれがClojureとは関係がないとは思わない。クロージャの使用例は、言語に関係なく同じになると思います。 – Carcigenicate

+0

@Carcigenicateフェア、確かに、既知の言語の例を尋ねる。このタグは、質問がより広く適用されることをOPが認識していることを示しています。タイトルは「Clojure」なしではより良いでしょう。 – Thumbnail

+0

確かに、閉鎖に精通していないClojureの初心者にとっては、コンセプトを簡単にしたいと思っていました。 –

答えて

3

これは非常に単純な例ですが、すでに1つの引数があるのに後で別の引数を取得する必要がある場合はクロージャを使用して関数を部分的に適用します。

(let [x 10 
     f #(- % x)] 
    f) 

は、私はすべての時間をそれらを使用していますが、それらの使用は、それが不自然ではない例を考えるのは難しいので、簡単です:あなたは、関数の内部でこのような何かをすることによってそれを解決することができます。

最近私がLorenz Systemsで行ったプロジェクトでこれが見つかりました。 Hue-fは、Lorenz Systemのx、y、z座標が与えられたときに、ラインのその部分に色付けする特定の色相を返します。 rand-genはとにかくグローバルであるため、この場合には閉鎖は不要ですが

(def rand-gen (g/new-rand-gen 99)) 
(def hue-f #(co/test1 % %2 %3 (g/random-double 1 1.05 rand-gen) 1.7)) 
                 ;^Closure 

:それは乱数生成器の上に閉鎖を形成しています。私は「それが必要作り」したい場合は、私のようなものに変更することができます:

(def hue-f 
    (let [rand-gen (g/new-rand-gen 99)] 
    #(co/test1 % %2 %3 (g/random-double 1 1.05 rand-gen) 1.7))) 
              ;^Closure 

(私はすぐそこにブラケットを得たと思うのLispは「フリーハンド」を書くのは難しいです

+2

私はちょうどラムダ上の古いletを愛するので、アップアップ。 – yorodm

1

クロージャを使用すると、状態が作成されたコンテキストが届かない場合でも、後で使用するための状態をキャプチャできます。ポータルを介して手を伸ばし、

このように、閉鎖の最も有用なケースは、必要なときです。何かを制御したりアクセスしたりしますが、元のハンドルは持っていません。

ユースケースの1つは、次のとおりです。自分自身で作業を行うスレッドを作成したいとしますが、そのスレッドに「話す」ことができます仕事は終わり、やっていることをやめてください。

(defn do-something-forever 
    [] 
    (let [keep-going (atom true) 
     stop-fn #(reset! keep-going false)] 
    (future (while @keep-going 
       (println "Doing something!") 
       (Thread/sleep 500)) 
      (println "Stopping...")) 
    stop-fn)) 

は(明示的に、例えば、スレッドを作成し、それを遮断する)これを達成するには、いくつかの方法があり、この方法は、評価されたとき、その結果閉鎖オーバー可変keep-goingの値を変更する関数を返しますまったく異なるコンテキストで実行されているスレッドは、変更を見てループを停止します。

これは、ポータルを介して私の手に届き、スイッチを入れ替えるかのようです。閉鎖せずに、私はkeep-goingをつかむことができませんでした。

1

クロージャ、カリングおよび部分的なアプリケーションは、言語の構成可能性に重要な役割を果たします。これは、意図的に、静的に定義されていない、期待される結果に適応する関数を構築する際にある程度の柔軟性を提供します。

閉鎖という用語は、1つのスコープ内の変数を「閉じる」ことができ、閉鎖された変数の値を宣言された場所の範囲外に運ぶことができます。

他のデータ/関数参照と同様にクロージャを渡すことができます。

また、関数とは異なり、クロージャは宣言時に評価されません。私はラムダ計算を忘れていますが、結果の生成を遅らせる方法です。

あなたは(fn []...)または#(...)のいずれかを使用して無名関数のような閉鎖は(それは、より高いレベルのスコープを介して渡された変数の上に閉じた場合、ラムダと区別1差がある速記宣言する。

1

良い例あなたが関数から関数を返したときにクロージャを使用してのものであり、内側の関数はまだそれが近くに作成された変数を「覚えている」ということ

例:。。

(defn get-caller [param1 param2] 
    (fn [param3] 
    (call-remote-service param1 param2 param3))) 

(def caller (get-caller :foo 42)) 
(def result (caller "hello")) 

より良い(そして実際の)例は、HTTP処理で広く使用されているミドルウェアパターンです。たとえば、各HTTPリクエストにユーザーを割り当てたいとします。ここではそのためのミドルウェアは、次のとおりです。

(defn auth-middleware [handler] 
    (fn [request] ;; inner function 
    (let [user-id (some-> request :session :user_id) 
      user (get-user-by-id user-id)] 
     (handler (assoc request :user user))))) 

handlerパラメータは、HTTPハンドラです。今度は、すべてのリクエストに、:userフィールドにユーザーデータのマップまたはnilの値が入力されます。

(GET "/api" request ((auth-middleware your-api-handler) request)) 

あなたの内側の関数がhandler変数を参照するので、あなたはクロージャを使用している:

は、今すぐあなたのミドルウェア(Compojure構文)とのハンドラを包みます。

0

mapの関数またはfilterの環境を作成したことはありませんか?

(let [adults (filter #(>= (:age %) 21) people)] ...) 

...または、述語に名前を与える:これは、一定の21の上に閉じ

(def adult? #(>= (:age %) 21)) 

を。そうでなければ、国や活動の領域(結婚、刑事責任、議決権)などから引き出されている可能性があります。

0

その他の回答に加えて、クロージャを使用するのにキャッシュがよく適しています。あなたはインメモリキャッシュ余裕がある場合は、最も簡単な方法の一つが閉鎖にキャッシュを置くことです:

(defn cached-get-maker [] 
    (let [cache (volatile nil)] 
    (fn [params] 
     (if @cache 
     @cache 
     (let [result (get-data params)] 
      (vreset! cache result) 
      result))))) 

あなたの代わりに揮発性の原子を使用することができ、実行の文脈によって。 Memoizingはクロージャでも作成され、特殊なキャッシングテクニックです。それはあなたがそれを自分で実装する必要はないということだけです.Closureはmemoizeを提供します。

+0

携帯でコードをインデントする方法がわかりません。 – DjebbZ

関連する問題