2016-10-17 8 views
-1

私はRobert C Martinの "Functional Programming"の話を見てきました。 https://www.youtube.com/watch?v=7Zlp9rKHGD4関数型プログラミングにおけるクロージャの場所

この話の主なメッセージは、機能プログラミングで状態が受け入れられないということです。 マーティンはさらに進んで、譲歩は「悪」であると主張する。

これを念頭に置いて私の質問は、関数型プログラミングの閉鎖の場所はどこですか?

機能コードに状態変数や変数がない場合、そのようなクロージャーを作成して使用する主な理由は何でしょうか(どのような状態も変数も囲まないクロージャ)?閉鎖機構は有用ですか?

状態または変数がない場合(おそらくimmutables idsのみの場合)、現在のレキシカルスコープを参照する必要はありません(変更可能なものは何もありません)。

このアプローチでは、現在のレキシカルスコープへのリンクがないJavaのようなラムダメカニズムを使用するのに十分です(変数が最終的でなければならない理由です)。

いくつかのソースでは、クロージャは関数型言語の要素を持つ必要があります。

+3

あなたがコーディングしているパラダイムにかかわらず、あなたは常に**状態を持っています。 **常に**。状態は悪くない、それは完全に必要です。邪悪とは、変化する世界的な状態です。あまりにも多くを包含しようとする州(神の目的)である。 – Carcigenicate

+0

OK。この男は閉鎖とは何かを説明します。 https://www.youtube.com/watch?v=7aYS9PcAITQ#t=18m13s 彼の例では、very_local_variableは「邪悪な」状態だと思います。最初にc1()を呼び出すと、それ以降は同じように呼び出されるので、同じではありません。それぞれの呼び出しは「世界を変える」でしょう。だから...おそらくこの2番目の例(クロージャの説明付き)はあまり正確ではないでしょうか? – LancerX

+1

突然変異している状態が関数の外にある場合、それは悪と見なすことができます。私の答えで私の新しい編集を見てください。あなたはグローバルな状態に影響を与えずに変更可能であり、それは関数の外で何も変更していないので悪ではありません。この機能は依然として効果的で機能的であり、参照性があります。 – Carcigenicate

答えて

3

閉鎖できる語彙スコープは、有用であるために変更可能である必要はありません。ただ、一例として、カリー化関数を考えてみます。

add = \a -> \b -> a+b 
add1 = add(1) 
add3 = add(3) 
[add1(0), add1(2), add3(2), add3(5)] // [1, 2, 5, 8] 

をここでは、内側のラムダはaの値の上に閉じて(あるいはので不変の違いを確認しない変数a、オーバー)。

最終的に関数型プログラミングにはクロージャは必要ありませんが、ローカル変数はどちらでもありません。それでも、どちらも非常に良いアイデアです。クロージャは、抽象化されたコードから特殊な振る舞いを持つ新しい関数を動的に作成する、関数プログラミングの最も重要なタスク(?)の非常に単純な表記を可能にします。

2

変数を変更できる言語と同じようにクロージャを使用します。違いは明らかです(通常は)変更することはできません。

次は(皮肉なことに、私は今で書いています)Clojureの中で、簡単な例です:

(let [a 10 

     f (fn [b] 
      (+ a b))] 

    (println (f 4))) ; Prints "14" 

このような場合、閉鎖の主な利点は、私が「部分的に適用する」ことが可能です関数を呼び出す必要があります(多くのシナリオでは非常に便利です)。以下の例では、すぐに関数を呼びたくない場合はどうなりますか? fが呼び出されたときに利用できるように、aを渡す必要があります。

しかし、あなたはそれが必要(@Bergiが指摘するように、この例では「悪」である、けれども)とみなされる場合、あなたはまた、ミックスにいくつかの可変性を追加することができます。

(let [a (atom 10) ; Atoms are mutable 

     f (fn [b] 
      (do 
      (swap! a inc) ; Increment a 
      (+ @a b)))] 

    (println (f 4)) ; Prints "15" 
    (println (f 4))); Prints "16" 

この方法では、静的エミュレートすることができます変数。これを使ってmemoizeのようなクールなことをすることができます。これは、「静的変数」を使用して、参照関数を透過する関数の入出力をキャッシュします。これによりメモリ使用量は増加しますが、正しく使用するとCPU時間を節約できます。

私は状態を持っているという考えに反することに同意しなければなりません。国家は悪ではない。彼らは必要ですです。すべてのプログラムには状態があります。グローバルな、可変状態は悪いです。

また、に変更可能であり、機能的にはまだプログラムです。リスト上にマップを含む関数があるとします。また、マッピング中はアキュムレータを維持する必要があります。

  • スイッチfoldmap:私は本当に( "手動でそれをやって" 無視して)2つのオプションがあります。
  • 変更可能な変数を作成し、マッピング中に変更します。

オプション1が望ましいはずですが、これらの方法の両方を機能プログラミング中に利用することができます。ある関数が内部的に可変変数を使用していても、 "関数外"という観点からは違いはありません。この関数は、影響を受ける変更可能な状態のみが関数に対して局所的であり、外部に何らかの影響を与えることができないため、依然として参照を透過的かつ純粋にすることができます。ローカル変数を変異

例コード:

(defn mut-fn [xs] 
    (let [a (atom 0)] 

     (map 
     (fn [x] 
      (swap! a inc) ; Increment a 
      (+ x @a)) ; Set the accumulator to x + a 
     xs))) 

注変数a関数外部から見えないができ、それが有する任意の効果は決してグローバル変化を引き起こすことができます。関数は各入力に対して同じ出力を生成するので、実質的に純粋です。

+0

あなたの例の*状態が*悪であると主張するかもしれません。なぜなら、関数 'f'はもう意味的に透明であるからです。 – Bergi

+0

はい、静的変数をエミュレートするのは悪です。関数型言語の中で何らかの有益な効果が得られることを示していましたが、これは理想的なユースケースとはかけ離れています。そのため、ローカルの可変状態が潜在的に有用であるが、有害ではないことを示すために、他の例を追加しました。 – Carcigenicate

+2

Downvoter、コメントしてください。 – Carcigenicate

関連する問題