Визначаючи відмінну відповідь jbapple щодо replicate
, але використовуючи replicateA
(на чому replicate
побудовано), я придумав таке:
--Unlike fromList, one needs the length explicitly.
myFromList :: Int -> [b] -> Seq b
myFromList l xs = flip evalState xs $ Seq.replicateA l go
where go = do
(y:ys) <- get
put ys
return y
myFromList
(у дещо більш ефективному варіанті) вже визначено та використовується внутрішньо в Data.Sequence
побудові пальців дерев , які є результатами сортів.
Загалом, інтуїція для replicateA
цього проста. replicateA
побудований поверх додаткової функції дерева. applicativeTree
бере шматок дерева розміром m
і створює добре збалансоване дерево, що містить його n
копії. Випадків n
до 8 (поодинокі)Deep
пальцем) кодуються жорстко. Все, що вище цього, і викликає себе рекурсивно. Елемент "застосовного" просто полягає в тому, що він перемежовує конструкцію дерева з ефектами різьблення через, наприклад, у випадку вищевказаного коду, стан.
The go
Функція, яка реплікується, це просто дію , яке отримує поточний стан, з'являється елемент з верхньої, і замінює залишок. Таким чином, при кожній виклику він подає поданий податок до списку, наданого як вхідний.
Ще кілька конкретних записок
main = print (length (show (Seq.fromList [1..10000000::Int])))
На деяких простих тестах це дало цікавий компроміс. Основна функція вище виконувалась майже на 1/3 нижче за допомогою myFromList, ніж з fromList
. З іншого боку, myFromList
використовували постійну купу в 2 МБ, тоді як стандарт fromList
використовував до 926 МБ. Це 926 Мб виникає з необхідності одночасно зберігати весь список в пам'яті. Тим часом рішення с . Ми можемо усунути ці виділення, перемістившись у монаду, перетворену CPS, але це призводить до того, що в будь-який момент часу утримується набагато більше пам’яті, оскільки втрата ліні вимагає переходу списку без потокового потоку.myFromList
здатне споживати структуру в ледачому потоковому режимі. Проблема зі швидкістю випливає з того, що myFromList
необхідно виконати приблизно вдвічі більше виділень (в результаті побудови пари / знищення монади держави), ніжfromList
З іншого боку, якщо замість того, щоб змусити всю послідовність із показом, я переходжу до просто витягування голови чи останнього елемента, myFromList
одразу представляє більший виграш - вилучення головного елемента майже миттєве, а вилучення останнього елемента - 0,8s . Тим часом, зі стандартом fromList
витягання або голови, або останнього елемента коштує ~ 2,3 секунди.
Це все деталі, і це наслідок чистоти та ліні. У ситуації з мутацією та випадковим доступом, я думаю, що replicate
рішення суворо краще.
Однак це все-таки ставить питання про те, чи існує спосіб переписати applicativeTree
таке, що myFromList
є суворо ефективнішим. Думаю, проблема полягає в тому, що додаткові дії виконуються в іншому порядку, ніж дерево, природно, проїжджають, але я не повністю розробив, як це працює, або якщо є спосіб вирішити це.