Во-первых, несколько преобразований:
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
повторяется заново , с самого своего начала, по (!!)
. Это классическое квадратичное поведение, вы выполняете 1 + 2 + 3 + 4 + ... + n = O (n) шагов к тому времени, когда результаты positions2 aa aas
будут изучены полностью (возвращенный список пройден до самого конца).
Но вы исследуете постепенно увеличивая показатели, так что вы могли бы также начать с предыдущей точки по списку (и не с начала его), и только вперед на 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 (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
-}
Это явно и заметно (п) раствор вывода Теперь. И потому, что списки в Haskell ленивые и zip
останавливается, когда самый короткий список заканчивается,
positions2 aa aas = [it | (a,it) <- zip aas [0 ..], a == aa]
(который эквивалентен коду в this answer here).
Duplicate http://stackoverflow.com/questions/6322053/haskell-program-to-find-the-position-of-an-element-in-a-list – Mark
@Mark он просит сложности (но да , очень близко к ...) – josejuan