思わ遅延評価式のが彼らのコードが実行される順序を失うコントロールにプログラマを引き起こす可能性があります。これがプログラマーに受け入れられる、または望まれる理由を理解するのに苦労しています。
式がいつどこで評価されるかが保証されていない場合、このパラダイムを使用して、意図したとおりに動作する予測可能なソフトウェアを構築するにはどうすればよいですか?
思わ遅延評価式のが彼らのコードが実行される順序を失うコントロールにプログラマを引き起こす可能性があります。これがプログラマーに受け入れられる、または望まれる理由を理解するのに苦労しています。
式がいつどこで評価されるかが保証されていない場合、このパラダイムを使用して、意図したとおりに動作する予測可能なソフトウェアを構築するにはどうすればよいですか?
回答:
答えの多くは、無限のリストや計算の未評価の部分からのパフォーマンス向上などに関係していますが、これには怠largerの大きな動機であるモジュール性が欠けています。
古典的な議論は、John Hughesの引用された論文「Why Functional Programming Matters」(PDFリンク)に記載されています。その論文の重要な例(セクション5)は、alpha-beta検索アルゴリズムを使用してTic-Tac-Toeを再生しています。重要な点は(p。9):
[遅延評価]を使用すると、プログラムを、多数の可能な回答を作成するジェネレーター、および適切なものを選択するセレクターとしてモジュール化することが実用的になります。
Tic-Tac-Toeプログラムは、特定の位置から始まるゲームツリー全体を生成する関数と、それを使用する別の関数として作成できます。実行時に、これは本質的にゲームツリー全体を生成するのではなく、消費者が実際に必要とするサブパートのみを生成します。消費者を変更することで、代替品が生産される順序と組み合わせを変更できます。ジェネレータを変更する必要はまったくありません。
熱心な言語では、おそらくツリーを生成するのに時間とメモリを費やしすぎるため、このように書くことはできません。したがって、次のいずれかになります。
式がいつどこで評価されるかが保証されていない場合、このパラダイムを使用して、意図したとおりに動作する予測可能なソフトウェアを構築するにはどうすればよいですか?
式に副作用がない場合、式が評価される順序は値に影響しないため、プログラムの動作は順序の影響を受けません。したがって、動作は完全に予測可能です。
現在、副作用は別の問題です。副作用が任意の順序で発生する可能性がある場合、プログラムの動作は実際には予測できません。しかし、実際にはそうではありません。Haskellなどの遅延言語は、参照を透過的にすることを重要視しています。つまり、式が評価される順序が結果に影響を与えないようにします。Haskellでは、これはユーザーに見える副作用を持つすべての操作をIOモナド内で強制的に実行することで実現されます。これにより、すべての副作用が期待どおりの順序で発生するようになります。
データベースに精通している場合、非常に頻繁にデータを処理する方法は次のとおりです。
select * from foobar
結果の生成方法と方法(インデックス?全テーブルスキャン?)、またはいつ(要求時にすべてのデータを一度に、または増分的に生成するか)を制御しません。あなたが知っているすべては、次のとおりです。場合は、より多くのデータがあり、あなたがそれを求めるとき、あなたはそれを取得します。
遅延評価は同じものにかなり近いです。ieとして定義された無限リストがあるとします。フィボナッチ数列-5つの数値が必要な場合、5つの数値が計算されます。1000が必要な場合、1000を取得します。トリックは、ランタイムが何をいつどこで提供するかを知っていることです。とても便利です。
(Javaプログラマーは、イテレーターを使用してこの動作をエミュレートできます-他の言語にも同様のものがあります)
Collection2.filter()
(そのクラスの他のメソッドと同様に)遅延評価を実装しています:結果は通常のように見えますCollection
が、実行順序は直感的でない(または少なくとも非自明)場合があります。また、yield
Python(およびC#の類似の機能)があり、通常のイテレーターよりも遅延評価のサポートにさらに近いものです。
名前が「Ab」で始まり、20年以上前の最初の2000人のユーザーのリストをデータベースに問い合わせることを検討してください。また、彼らは男性でなければなりません。
これが小さな図です。
You Program Processor
------------------------------------------------------------------------------
Get the first 2000 users ---------->---------- OK!
--------------------- So I'll go get those records...
WAIT! Also, they have to ---------->---------- Gotcha!
start with "Ab"
--------------------- NOW I'll get them...
WAIT! Make sure they're ---------->---------- Good idea Boss!
over 20!
--------------------- Let's go then...
And one more thing! Make ---------->---------- Anything else? Ugh!
sure they're male!
No that is all. :( ---------->---------- FINE! Getting records!
--------------------- Here you go.
Thanks Postgres, you're ---------->---------- ...
my only friend.
このひどい相互作用でわかるように、「データベース」は、すべての条件を処理する準備ができるまで、実際には何もしていません。各ステップで結果を遅延ロードし、毎回新しい条件を適用します。
最初の2000人のユーザーを取得するのではなく、それらを返し、「Ab」でフィルタリングし、それらを返し、20を超えてフィルタリングし、男性をフィルタリングし、最終的にそれらを返します。
簡単に言えば、遅延読み込み。
遅延評価には説得力があると思ういくつかの議論があります
モジュール性遅延評価を使用すると、コードを部分に分割できます。たとえば、「リストリスト内の要素の最初の10個の逆数を見つけて、その逆数が1未満になる」という問題があるとします。Haskellのようなもので書くことができます
take 10 . filter (<1) . map (1/)
しかし、これは厳密な言語では正しくあり[2,3,4,5,6,7,8,9,10,11,12,0]
ません。指定するとゼロで除算されるためです。これが実際に素晴らしい理由については、sacundimの回答を参照してください
より多くのものが機能します厳密に(しゃれた)厳密な評価よりも厳密でない評価で終了するプログラムが多くなります。プログラムが「熱心な」評価戦略で終了する場合、「怠lazな」評価戦略で終了しますが、正反対ではありません。この現象の具体例としては、無限のデータ構造(実際にはちょっとクールなもの)のようなものがあります。遅延言語で動作するプログラムが増えています。
最適性 Call-by- Need評価は、時間に関して漸近的に最適です。主要な遅延言語(基本的にHaskellとHaskellである)はcall-by-needを約束しませんが、多かれ少なかれ最適なコストモデルを期待できます。厳密性アナライザー(および投機的評価)は、実際にオーバーヘッドを抑えます。スペースはより複雑な問題です。
怠zyな評価を使用したForces Purityは、あなたがそれを置くとプログラマがコントロールを失うため、副作用を無秩序な方法で処理することは完全な苦痛になります。これは良いことです。参照の透明性により、プログラムのプログラミング、屈折、推論が非常に簡単になります。厳密な言語は、不純なビットを持つというプレッシャーに必然的に陥ります。HaskellとCleanが美しく抵抗しました。これは、副作用が常に悪であると言うことではありませんが、副作用を制御することは非常に有用なので、この理由だけで怠aloneな言語を使用するのに十分です。