6

彼の記事 "Why Functional Programming Matters"では、John Hughes氏は、「レイジー評価はおそらく、機能プログラマーのレパートリーにおけるモジュール化のための最も強力なツールです」と主張しています。そうするために、彼は次のような例を提供します:lazy-evaluationはどのようにしてより大きなモジュール化を可能にしますか?

"infiniteLoop"と "terminationCondition"という2つの関数があるとします。

terminationCondition(infiniteLoop input) 

レイジー評価では、ヒューズの言葉で "ループの本体から終了条件を分離することができます。"これは間違いなく評価を使用する "terminationCondition"は、この条件がループの外で定義できることを意味します。つまり、terminationConditionがデータを要求しなくなったときにinfiniteLoopが実行を停止します。

しかし、高次関数は次のように同じことを達成できませんでしたか?

infiniteLoop(input, terminationCondition) 

ここで、遅延評価は高次関数では提供されていないモジュール化をどのように提供しますか?

+0

上記の擬似コードの代わりに、 'Data.List'の関数を考えるのはもっと建設的かもしれません。例えば'takewhile fooとは?ドロップ1。フィルタ(> 0)。あなたが提案している世界のようにバーを繰り返しますか?私たちはまだ有用な 'Data.List'ライブラリを取得していますか?残念ながら、私はこの質問が広すぎると思います。 – jberryman

+2

なぜ 'infiniteLoop'は' terminationCondition'を引数として取りますか?それができれば有限であろう。 – Bergi

+0

'terminationCondition(infiniteLoop(ループ入力))'をどのように書き直しますか? – Bergi

答えて

11

はい、あなたは終了チェックで渡すことができますが、そのためにはinfiniteLoopの作者は、そのような種類の条件でループを終了させたいという可能性を予期しなければならず、終了条件それらの機能に変換する。

特定のの条件を関数として渡すことができても、その形状はinfiniteLoopの作者によってあらかじめ決められています。彼らは私に各要素で呼び出される終了条件 "スロット"を与えても、何らかの収束条件をチェックするために最後のいくつかの要素にアクセスする必要がありますか?シンプルなシーケンスジェネレータでは、 "最も一般的な"可能な終了条件タイプを考え出すことができますが、それを行う方法は明らかではなく、効率的で使いやすいままです。チェックしている場合には、これまでのシーケンス全体を繰り返し終了条件に渡しますか?私は通話者に最も単純な終了条件をより複雑なパッケージにまとめて、最も一般的な条件タイプに適合させるようにしますか?

発信者は、正しい条件を供給するために終了条件がどのように呼び出されたかを正確に知る必要があります。これは、この特定の実装にかなり依存している可能性があります。別の第三者によって書かれたinfiniteLoopの別の実装に切り替えると、終了条件に対して全く同じ設計が使用される可能性はどのくらいありますか?怠惰なinfiniteLoopでは、私は同じシーケンスを生成するはずの実装を削除することができます。

infiniteLoopでない場合、単純なシーケンスジェネレータですが、実際にはツリーのようなより複雑な無限のデータ構造を生成しますか?木のすべての枝が独立して再帰的に生成された場合(チェスのようなゲームの場合は移動木と考える)、それまでに生成された情報のあらゆる種類の条件に基づいて、異なる枝で異なる枝を切り取ることが理にかなっています。

元の作者が(私のユースケースや十分に一般的なクラスのユースケースのために)準備をしていなかった場合は、私は不運です。怠け者の著者は、それを自然な方法で書くだけで、個々の発信者が自分が望むものを怠惰に探索させることができます。どちらも他の人についてはあまり知りません。

さらに、無限の出力を怠惰に探索することを止めるという決定が、呼び出し元がその出力で行っている計算と実際にインターリーブされている(依存する)場合はどうなりますか?チェスの木をもう一度考えてみてください。ツリーの1つのブランチをどれだけ探検したいかは、ツリーの他のブランチで見つけた最良のオプションの評価に簡単に依存することができます。だから私は私の横断と計算を2回行う(終了条件では、infinteLoopに停止を知らせるフラグを返し、次に有限出力で再び実際に結果を得ることができる)か、またはinfiniteLoopの作成者は終了条件だけでなく、出力を返す複雑な関数(私は計算を "終了条件"内にプッシュすることができます)。

極端な場合には、出力を調べて結果を計算し、ユーザーに表示して入力し、データ構造の探索を続けます(ユーザーの入力に基づいてinfiniteLoopを呼び出すことなく)。怠け者の元の著者infiniteLoopは、私がこのようなことを考えているとは思っていない、それはまだ動作します。型システムによって純粋さが得られたなら、終了条件が(モナドを与えて)言うならば、infiniteLoop全体が副作用を許されない限り、渡された終了条件アプローチでは不可能になりますインタフェース)。

簡潔に言えば、infiniteLoopを制御するために高次関数を使用することによって遅延評価を得るには、infiniteLoopの作成者とその呼び出し元の両方にとって非常に複雑なことがありますいろいろな単純なラッパーが公開されておらず、そのうちの1つが呼び出し元のユースケースと一致しない限り)。レイジー評価は、プロデューサと消費者がほぼ完全に切り離され、プロデューサがどれだけの出力を生成するかをコンシューマに制御できるようにします。 のように余分な関数の引数を使って行うことができますが、制御関数がどのように動作するかに関するプロトコルに本質的に同意することはプロデューサとコンシューマに必要です。そのプロトコルは、ほとんどの場合、消費者とプロデューサを結びつけるユースケースに特化しているか、プロデューサとコンシューマがそのプロトコルに結びついていることを完全に一般化するためには複雑です(これは再作成されそうもありません)他の場所では、まだ結ばれています。

関連する問題