13

私の現在の OOP の状態の概念と、Haskell や Clojure のような関数型言語で行われる方法との違いを理解する助けが必要です。

ハックニーの例を使用するために、単純化された銀行口座オブジェクト/構造体/その他を扱っているとしましょう。OOP 言語では、BankAccount への参照を保持するクラスがあり、金利などのインスタンス変数と、オブジェクトの状態を変更し、通常は何も返さない setInterestRate() などのメソッドがあります。たとえば Clojure では、銀行口座の構造体 (美化されたハッシュマップ) と、銀行口座のパラメーターやその他の情報を受け取り、新しい構造体を返す特別な関数を用意します。したがって、元のオブジェクトの状態を変更する代わりに、必要な変更を加えた新しいオブジェクトが返されます。

それで...どうすればいいですか?古い銀行口座を参照していた変数を上書きしますか? もしそうなら、それは状態を変えるOOPアプローチよりも利点がありますか? 結局、どちらの場合も、必要な変更を加えたオブジェクトを参照する変数があるようです。私は遅れているので、何が起こっているのか漠然とした概念しか持っていません。

それが理にかなっていることを願っています。助けてくれてありがとう!

4

4 に答える 4

11

純粋な機能スタイルでは、変数を上書きすることはありません。

類推は、物理学の時空です。世界を 3D と考えると、オブジェクトには固定された位置がなく、時間とともに移動します。したがって、物理的な世界に数学を適用するために、時間次元を追加し、特定の時間におけるさまざまなプロパティの値を検討します。そうすることで、研究の対象を定数にしました。同様に、プログラミングでは、不変の値を操作することで、概念が単純になります。実世界で同一性を持つオブジェクトは、変化する単一の値としてではなく、一連の不変値 (増加する時間におけるオブジェクトの状態) としてモデル化できます。

もちろん、値のシーケンスを「オブジェクト ID」に関連付ける方法の詳細は、少し複雑になる可能性があります。Haskell には、状態をモデル化できる Monadがあります。Functional Reactive Programmingは、純粋な機能更新で​​世界のオブジェクトをモデル化する、より文字通りの試みであり、プログラミングの非常に有望な方向性だと思います。

Haskellとは異なり、Clojureは純粋ではなく、提案したように変数を更新できることに注意してください。高レベルでいくつかの変数のみを更新する場合でも、関数型プログラミングの概念的な単純さの利点の多くを享受できるでしょう。

于 2008-12-09T20:10:23.600 に答える
8

おそらく OO の世界では、ループがあり、要求に応じてこれらの銀行口座を何度も変更しています。アカウントのポートフォリオ全体があり、これらのタイプがポートフォリオであるとします。次に、Haskell で純粋な関数を記述します。

updatePortfolio :: Request -> Portfolio -> Portfolio

また、メイン ループが標準入力から要求を読み取り、ポートフォリオを最新の状態に保つ場合があります。(この例は、ポートフォリオも書けない限りあまり役に立ちませんが、より単純です。)

readRequest :: IO Request  -- an action that, when performed, reads a Request with side effects

main :: Portfolio -> IO ()  -- a completely useless program that updates a Portfolio in response to a stream of Requests

main portfolio = do req <- readRequest
                    main (updatePortfolio req)

ここで、変更可能な状態に何が起こったのかを確認していただければ幸いです。典型的な関数型プログラムでは、変化する状態はパラメーターとして関数に渡されます。状態が変化したら、新しい関数呼び出しを行います。呼び出しは末尾の位置にあるため (「適切な末尾の呼び出し」を検索できます)、追加のリソースを使用しません。実際、コンパイラがアセンブリ コードを生成すると、ループが生成され、へのポインターが保持されます。レジスター内の常に変化するポートフォリオ。

これは非常におもちゃの例ですが、関数型言語の特徴を少しでも理解していただければ幸いです。

于 2008-12-10T03:57:40.393 に答える
4

それで...どうすればいいですか?古い銀行口座を参照していた変数を上書きしますか?

はい

もしそうなら、それは状態を変えるOOPアプローチよりも利点がありますか?

その構造体に対して行うアクションの計算に時間がかかり、途中で何かが発生し、元の構造体に戻す必要があるか、計算でエラーが発生したとします。あなたがOOについて私に提示した解釈(不変のOO言語を持つことができるため、参照を使用)では、データが破損している可能性があります-失敗した関数呼び出しから十分な情報が提供されない限り、それは不明であり、失敗したことを示唆しますひどく。関数型のアプローチでは、最初にコピーを作成したため、元のデータ構造が正しいことが確実にわかります。

このシナリオをマルチスレッド アプリケーションに拡張します。私たちは皆、独自のバージョンを持っているため、他の誰もが私たちのデータ構造を使用していないことを確認できます。

さらに、コピー元の他の構造のデータを使用することで、スペースを節約できます。古典的な例は、要素をリストの先頭に追加する場合です。2 番目の要素へのポインタと最初の要素へのポインタがある場合、最初の要素のサイズだけで両方のリストを参照できます (以下を参照)。不変性がなければ、これを保証することはできません。

        b__
           |  
a -> [6|] -+-> [5|] -> [4|] -> [3|] -> [2|] -> [1|x]
于 2008-12-09T19:58:18.960 に答える
1

純粋な関数型言語である Haskell を見てください。これには再代入がまったくなく、その他の副作用もありません。IO を実行するために、IO モナドコンストラクトでは実際にRealWorldを新しいインスタンスに置き換えます。たとえば、コンソールに新しいテキストが表示されるワールド。

于 2008-12-09T21:29:16.483 に答える