20

私はしばらくの間、関数型プログラミングに頭を悩ませようとしてきました。私はラムダ計算、LISP、OCaml、F#、さらには組み合わせロジックを調べましたが、私が抱えている主な問題はこれです-次のような副作用を必要とすることをどのように行うのですか?

  • ユーザーとのやり取り、
  • リモートサービスとの通信、または
  • ランダム サンプリングを使用したシミュレートの処理

与えられた入力に対して出力が決定論的であるという純粋な関数型プログラミングの基本的な前提に違反することなく?

私が理にかなっていることを願っています。そうでない場合は、理解を助けるための試みを歓迎します。前もって感謝します。

4

10 に答える 10

9

Haskell は純粋な関数型プログラミング言語です。Haskell では、すべての関数は純粋です (つまり、常に同じ入力に対して同じ出力を返します)。しかし、Haskell で副作用をどのように処理するのでしょうか? さて、この問題はモナドを使用することで見事に解決されます。

例として I/O を取り上げます。Haskell では、I/O を行うすべての関数が IO 計算、つまり IO モナドでの計算を返します。したがって、たとえば、キーボードから int を読み取る関数は、int を返す代わりに、実行時に int を生成する IO 計算を返します。

askForInt :: String -> IO Int

ではなく I/O 計算を返すため、Intこの結果を直接合計に使用することはできません。値にアクセスするにIntは、計算を「アンラップ」する必要があります。これを行う唯一の方法は、バインド関数 ( >>=) を使用することです。

(>>=) :: IO a -> (a -> IO b) -> IO b

これも IO 計算を返すため、常に I/O 計算になります。これが、Haskell が副作用を分離する方法です。IO モナドは、現実世界の状態の抽象化として機能します (実際、内部では通常RealWorld、状態部分にちなんで名付けられた型で実装されています)。

于 2009-12-16T18:43:51.890 に答える
7

ユーザーとのやり取りやリモート サービスとの通信には、ソフトウェアの何らかの非機能部分が必要です。

多くの「関数型言語」(ほとんどの Lisp と同様) は、純粋に関数型ではありません。副作用のあることはほとんどのコンテキストで「推奨されません」が、それでも副作用のあることを行うことができます。

Haskell は「純粋に機能的」ですが、IO モナドを介して非機能的なことを行うことができます。基本的な考え方は、純粋に機能的なプログラムが、非機能的なプログラムによって評価される遅延データ構造を発行することです (これはあなたが書いたものではなく、環境の一部です)。このデータ構造自体が命令型プログラムであると主張する人もいるかもしれません。つまり、関数型言語で命令型のメタプログラミングを行っているようなものです。

どちらのアプローチが「より良い」かは無視して、どちらの場合も、プログラムの機能部分と非機能部分を分離し、非機能部分のサイズを可能な限り制限することが目標です。機能パーツは、より再利用可能で、テストしやすく、推論しやすい傾向があります。

于 2009-12-16T18:45:49.307 に答える
6

関数型プログラミングは、副作用を完全に取り除こうとするのではなく、副作用を制限して分離することです...できないからです。

...そしてはい、私はFPが便利だと思います(確かにとにかくErlangで):「アイデア」から「プログラム」(または問題から解決策;)に到達する方が簡単だと思います;)...しかしもちろんそれは私だけかもしれません.

于 2009-12-16T18:37:37.410 に答える
3

私が知っている唯一の完全に純粋な関数型言語は、C++ のテンプレート システムです。Haskell は、プログラムの命令部分を明示的にすることで 2 位になりました。

Haskell では、プログラムには変更可能な状態がありますが、関数 (ほとんどの場合) にはありません。プログラムの 99% をピュアに保ち、外の世界とやり取りする部分だけをピュアにします。したがって、関数をテストしているときは、副作用がないことがわかります。純粋なコアと不純なシェル。

于 2009-12-16T19:48:58.290 に答える
2

Haskell がそれを行う方法は、モナドを使用することです。ウィキペディアと Haskell による説明を参照してください

基本的には、IO モナドを取り除かないという考え方です。私の理解では、IOモナドをアンラップしてその関数を実行する関数をチェーンできるということです。しかし、IO モナドを完全に削除することはできません。

IO に直接結び付けられていないモナドを使用した別の例は、Maybe Monad です。このモナドは IO モナドとは対照的に「ラップ不可能」です。しかし、Maybe モナドを使用してモナドの使用法を説明する方が簡単です。次の関数があるとします。

wrap :: Maybe x -> (x -> y) -> Maybe y
wrap Nothing  f = Nothing
wrap (Just x) f = Just (f x)

wrap (Just 4) (5+)これで、どちらが返されるかを呼び出すことができます Just 9

IO モナドの考え方は、内部型で (+5) のような関数を使用できるということです。すべての関数はラッピング IO モナドで連鎖されているため、モナドは関数がシリアルで呼び出されることを保証します。

于 2009-12-16T18:44:08.017 に答える
2

少なくとももう 1 つの重要な概念であるMonadsを知っておく必要があります。これは、I/O やその他の「便利な」作業を行うために必要になります。

于 2009-12-16T18:41:45.023 に答える
1

ほとんどのプログラムが外の世界になんらかの影響を与えることを考えると (ファイルへの書き込み、データベース内のデータの変更など)、プログラム全体に副作用がないことはめったにありません。学術的な演習以外では、試しても意味がありません。

しかし、プログラムはビルディング ブロック (サブルーチン、関数、メソッド、好きなように呼び出します) から組み立てられ、純粋な関数は非常に適切に動作するビルディング ブロックを作成します。

ほとんどの関数型プログラミング言語は、関数を純粋にする必要はありませんが、優れた関数型プログラマーは、参照透過性の利点を享受するために、実行可能かつ実用的な限り多くの関数を純粋にしようとします。

Haskell はさらに先に進みます。Haskell Programm のすべての部分は純粋です (少なくとも「unsafePerformIO」などの罪がない場合)。Haskell で作成するすべての関数は純粋です。

副作用はモナドを通じて導入されます。これらは、一種の「買い物リスト -- 買い物客」の分離を導入するために使用できます。基本的に、プログラムは買い物リスト (単なるデータであり、純粋な方法で操作できる) を作成し、言語ランタイムは買い物リストを解釈して効果的な買い物を行います。すべてのコードは純粋で、等式推論などに適していますが、不純なコードはコンパイラ ライターによって提供されます。

于 2009-12-20T11:58:04.527 に答える
0

仕事で使用しない場合でも、1 つまたは複数の関数型プログラミング言語を学習することは、別の考え方を学ぶための優れた方法であり、問​​題に対する代替アプローチのツールキットを提供します (それができないときにイライラすることもあります)。他の言語の関数型アプローチと同じくらいきちんとしたクリーンなもの)。

そして、XSL スタイルシートを書くのが上手になりました。

于 2009-12-16T18:42:23.977 に答える