x
次の関数では、コンパイラが十分に賢く、一定のままになるのか、それともリスト内のすべての項目のリストの先頭を計算するのか疑問に思っています。(私はGHCを使用しています)
allSame :: Eq a => [a] -> Bool
allSame xs = all (==x) xs where x = head xs
x
次の関数では、コンパイラが十分に賢く、一定のままになるのか、それともリスト内のすべての項目のリストの先頭を計算するのか疑問に思っています。(私はGHCを使用しています)
allSame :: Eq a => [a] -> Bool
allSame xs = all (==x) xs where x = head xs
GHC における 'where' のセマンティクスは、単一のクロージャが 'x' に割り当てられ、すべての用途で共有されるというものです。関数 (== 'x') の新しいクロージャが生成され、オプティマイザがそれをフロートアウトするため、トラバーサルごとに 1 回だけ生成されます。
生成されたコードを正確に確認するには、Core をチェックします (例: ghc-core 経由)。GHC はコードを次のように最適化します。
M.allSame a eq xs =
all
(let
ds =
case xs of
[] -> error "bad head"
x : _-> x
in
\y -> x == y
) xs
パフォーマンスが懸念される場合は、ベクターの使用を検討してください。個別のトラバーサルが融合し、再帰が削除されるためです。
Haskell は必要なものを評価するだけだと思いx
ますwhere
。x
次に、一度計算してall
.
テストしたい場合myall
は、のように再帰を行う関数を書くことができますがall (==x)
、基本的には比較要素を出力するだけです。したがって、毎回新しい引数を取得するか、毎回同じままであるかがわかります。
これをテストするための小さな関数を次に示しmyall
ます。最初の引数を収集してリストに入れるだけです。
myall x [] = [x]
myall x xs = x:(myall x (tail xs))
test xs = myall (x) xs where x = head xs
を呼び出すとtest [1,2,3]
、結果が であることがわかります[1,1,1,1]
。つまり、最初x
に が評価され1
、その後myall
が評価されます。