2

私は Haskell の初心者で、プログラミング言語のクラスで Haskell に関する論文を書いています。サンプル コードを使用して Haskell の遅延性を実証したいのですが、実際に遅延性があるかどうかはわかりません。

doubleMe xs = [x*2 | x <- xs]

ghci で:

let xs = [1..10]
import Debug.Trace
trace (show lst) doubleMe (trace (show lst) doubleMe (trace (show lst) doubleMe(lst)))

出力:

[1,2,3,4,5,6,7,8,9,10]
[1,2,3,4,5,6,7,8,9,10]
[1,2,3,4,5,6,7,8,9,10]
[8,16,24,32,40,48,56,64,72,80]

お時間をいただきありがとうございます。

4

4 に答える 4

7

ここでの使用法はtrace特に洞察に満ちているわけではなく、実際にはまったくありません。評価の 4 つの異なるポイントで同じリストを出力するだけで、プログラムの実際の状態については何もわかりません。ここで実際に起こっていることtraceは、計算が開始される前 (結果リストが弱頭正規形に要求されたとき) に、すべての倍加ステップで強制されることです。これは、完全に厳密な評価を行う言語で得られるものとほとんど同じです。

怠惰を確認するには、次のようなことができます

Prelude Debug.Trace> let doubleLsTracing xs = [trace("{Now doubling "++show x++"}")$ x*2 | x<-xs]
Prelude Debug.Trace> take 5 $ doubleLsTracing [1 .. 10]
{Now doubling 1}
{Now doubling 2}
{Now doubling 3}
{Now doubling 4}
{Now doubling 5}
[2,4,6,8,10]

ここでは、5 つの結果のみが要求されたため、5 つの数値のみが 2 倍になっていることがわかります。与えられたリストにdoubleLsTracingは10​​のエントリがありますが。

通常、「実行の流れ」を監視するための優れたツールではないことに注意してください。これtraceは、ローカル変数を「調べて」関数で何が起こっているかを確認できるようにするための単なるハックです。

于 2012-12-10T20:55:53.333 に答える
5

無限のストリームは常に良い例です。特別な構造がなければ他の言語でそれらを取得することはできませんが、Haskellでは非常に自然です。

一例はフィボナッチストリームです:

fib = 0 : 1 : zipWith (+) fib (tail fib)

take 10 fib => [0,1,1,2,3,5,8,13,21,34]

もう1つは、試行除算法を使用して素数のストリームを取得することです。

primes = sieve [2..]
    where sieve (x:xs) = x : filter (not . (== 0) . (`mod` x)) (sieve xs)

take 10 primes => [2,3,5,7,11,13,17,19,23,29]

また、Haskellでのバックトラッキングの実装は非常に簡単で、ソリューションのリストをオンデマンドで怠惰に取得することができます。

http://rosettacode.org/wiki/N-queens_problem#Haskell

minを実装する方法を示す、より複雑な例は、次の例です。

遅延評価と時間計算量

これは基本的に、Haskellの怠惰を使用して、minimum関数の非常にエレガントな定義(要素のリストで最小値を見つける)を取得する方法を示しています。

minimum = head . sort

考案された例によって、Haskellの怠惰を示すことができます。しかし、怠惰が他の言語よりもはるかに大きなモジュール性を示す一般的な問題の解決策を開発するのにどのように役立つかを示す方がはるかに良いと思います。

于 2012-12-11T00:25:29.330 に答える
2

簡単な答えは「いいえ」です。leftaroundaboutは、彼の答えの中でそれをかなりうまく説明しています。

私の提案は次のとおりです。

  1. 遅延評価の定義を読んで理解します。
  2. 引数の1つが発散する可能性のある関数を記述します。これは、お気に入りの厳密な(遅延のない)言語(C、python、Java)では機能しない例です。たとえば、sumIfFirstArgIsNonZero(x、y)は、x!=0の場合はx+ yを返し、それ以外の場合は0を返します。
  3. ボーナスポイントについては、Haskellの組み込みのif-then-else構文を使用しない独自の関数ifThenElseを定義し、遅延言語で新しい制御フロー構造を簡単に記述できる理由を説明してください。

これは、無限のデータストリームに頭を悩ませたり、結び目を作るよりも簡単なはずです。

于 2012-12-10T21:12:56.407 に答える
2

怠惰の主なポイントは、必要のない値が計算されないことです。そのため、これを実証するには、評価されていないものを示す必要があります。すべての値が最終的に計算されるため、あなたの例は怠惰を示すのに最適ではありません。

出発点として、小さな例を次に示します。

someValueThatNeverTerminates = undefined -- for example, a divide-by-zero error

main = do (greeting, _) = ("Hello terminating world!", someValueThatNeverTerminates)
          putStrLn greeting

これはすぐにこんにちはと言います-怠け者でなければ、すべてが途中で壊れるでしょう。

于 2012-12-11T03:00:03.117 に答える