2012-07-01 15 views
7

問題の2つのバージョンを書いていますが、それらは同様の効率を持つべきだと思いますが、そうではありません。私はそれがハスケルの怠惰な評価行動に起因すると思う。誰かがあなたがnqueens2はかなり動作しますがサイズ8ハスケルの遅延評価動作の理解に役立つ

のボードのためにそれを評価するためにnqueens1 8 8またはnqueens2 8 8を呼び出すことによって、それらを評価することができ

、それは次の例のためにどのように動作するか

nqueens1 n 1 = [[i] | i <- [1..n]] 
nqueens1 n k = [ i:q | i <- [1..n], q <- (nqueens1 n (k - 1)), isSafe i q k] 

isSafe i q n = isSafeHelper i (zip q [(n-1),(n-2)..1]) 
     where isSafeHelper i [] = True 
       isSafeHelper i (x:xs) = (i /= fst x) && abs(i - (fst x)) /= abs(n - (snd x)) && 
             isSafeHelper i xs 
nqueens2 n 1 = [[i] | i <- [1..n]] 
nqueens2 n k = [ i:q | i <- [1..n], q <- boards, isSafe i q k] 
      where boards = nqueens2 n (k-1) 
を説明していただけます効率的にnqueens1にパフォーマンスの問題があります。再帰呼び出し(nqueens n(k-1))が複数回評価されているためです。 Haskellの遅延評価の私の理解から、これは当てはまりません。

この現象を理解するのを手伝ってください。

ありがとうございます。

+1

「遅延評価は、」遅れて、物事を評価についてです - ではない何かを評価する何回も回避について。 –

+4

@DanielWagner実際には、遅延評価と名前による呼び出しの違いは、名前による呼び出しを使用して複数回評価される特定の式は、遅延評価を使用して一度だけ評価されるということです。しかし、それはここの問題とは関係ありません。 – sepp2k

+2

@ sepp2kあなたは正しいです、私は、「怠惰な評価」の代わりに「名前による呼び出し」と言うか、「何度も何かを評価するのを避ける」の代わりに「メモ」と言って、より正確になっていたはずです。 –

答えて

10

はい、再帰呼び出しは複数回評価されます。具体的には、iの値ごとに1回評価されます。

あなたがこれを避けたい場合はq <-部分がi <-一部の前に来るように、あなたは発電機を並べ替えることができます。

[ i:q | q <- nqueens2 n (k - 1), i <- [1..n], isSafe i q k] 

しかしこれは、結果の順序を変更します。それが受け入れられないなら、あなたは一度、再帰呼び出しの結果を計算し、このようにそれを使用するためにletを使用することができます。

[ i:q | let rec = nqueens2 n (k - 1), i <- [1..n], q <- rec, isSafe i q k] 
+0

再帰呼び出しが2回以上評価されていたことが推測されました。これがnqueens1の減速の理由でしたが、nqueens2の唯一の変更はその式に名前を付けることです。これは、Haskellコンパイラ自体で簡単に行うことができました。コンパイラがなぜそれを行えないのかを知りたかったのです。 ありがとうございます。 – prasannak

+2

この種の「最適化」は、時間のためのスペースを交換します。サブタイトルは現在何度も評価されていませんが、再度必要とされる限り、記憶されなければなりません。したがって、GHCは非常に注意深く、一般的にこの種の変換を自動的には行いません。経験則の大まかなルールは次のとおりです。用語が最大で1回評価されるようにするには、名前を付けます。 – kosmikus

関連する問題