Как сохранить нативную структуру циклического списка во время преобразований в Haskell?

Я изучаю графоподобные вещи в Хаскеле, используя технику "связывания узла". Полагаю, циклические списки - это просто внутренняя реализация бесконечного списка, поэтому в идеальном мире не нужно заботиться о subj. Но это может оказать существенное влияние на вычислительную сложность, рассмотрим пример 1D клеточных автоматов с зацикленным миром:

ribbon = let x = 0 : y
             y = 1 : x
         in  x

update_cycle :: Num a => [a] -> [a]
update_cycle lst@(_:xs) = ( f lst : update_cycle xs)
update_cycle []         = error "infinite cycle assumed"

f :: Num a => [a] -> a           --  some arbitrary list reduction
f (x:(y:_)) = traceShow "f called" $ x - y

Это цикл с двумя ячейками. Давайте сделаем шаг:

*Main> take 2 $ update_cycle ribbon
[-1,1]
"f called"
"f called"

Это хорошо, теперь два шага:

*Main> take 2 $ (update_cycle . update_cycle) ribbon
[-2,2]
"f called"
"f called"
"f called"
"f called"
"f called"

Это пять вызовов вместо четырех, и это фактически означает увеличение количества вызовов на каждом шаге, поэтому мы имеем квадратичную сложность общего количества шагов вместо линейного. Я мог бы сделать циклические явные, как это:

newtype UserDefCyclic a = UserDefCyclic { fromTC :: [a] }

udcToList :: UserDefCyclic a -> [a]
udcToList (UserDefCyclic x) = cycle x

update_udc :: Num a => UserDefCyclic a -> UserDefCyclic a
update_udc (UserDefCyclic core) = UserDefCyclic $ take (length core) (update_cycle ( cycle core )) 

Но это ужасно, хотя мне действительно интересны более сложные структуры, такие как графы. Вопрос: есть ли способ, чтобы код был красивым и быстрым? Или есть надежда, что компилятор будет лучше обрабатывать приведенный выше код в будущем?

0 ответов

Другие вопросы по тегам