まず、いくつかの変換:
positions2 :: (Eq a) => a -> [a] -> [Int]
positions2 = f where
f aa aas = filter (g aa aas) [0 .. (length aas - 1)]
g aa aas it = aa == aas !! it
-- ==
positions2 aa aas = filter (g aa aas) [0 .. (length aas - 1)]
g aa aas it = aa == aas !! it
-- ==
positions2 aa aas = [it | it <- [0 .. (length aas - 1)], aa == (aas !! it)]
{-
positions2 aa aas = for each it in [0 .. (length aas - 1)]
if (aas !! it) == aa
emit it
-}
it
これで、与えられたリストのトラバーサルが、その最初から によって 新たにaas
繰り返されることがはっきりとわかります(!!)
。これは古典的な 2 次動作です。の結果が完全に探索されるまでに1+2+3+4+...+n = O(n 2 )positions2 aa aas
ステップを実行します(返されたリストは最後までトラバースされます)。 .
ただし、徐々に増加するインデックスを探索するため、リストに沿って ( start からではなく) 前のポイントから開始し、毎回 1 ポジションだけ進めることができます(完全なit
インデックスではありません(!!)
)。
positions2 aa aas = g 0 aas
where
g i (a:as) | a == aa = i : g (i+1) as
| otherwise = g (i+1) as
g _ [] = []
ここでは、インデックスの 1 ずつの増分 ( ) と、リストに沿った 1 ポジションの前進 ( ) がどのようにi --> i+1
組み合わされているかを確認できます(a:as) --> as
。
再びリスト内包表記を使用すると、ウィービング (または実際には圧縮に似たもの) は次を使用して実現されzip
ます。
positions2 aa aas = [it | (a,it) <- zip aas [0 .. (length aas - 1)]
, a == aa]
{-
= for each a in aas while incrementing it by 1 from 0 to (length aas - 1),
if a == aa
emit it
-}
これは、明らかに目に見える O(n) ソリューションです。また、Haskell のリストは遅延型でzip
、最短のリストが終了すると停止するため、
positions2 aa aas = [it | (a,it) <- zip aas [0 ..], a == aa]
(これは、こちらの回答のコードと同等です)。