Я взял реализацию цепных списков Окасаки и немного изменил ее, чтобы избежать проблем с булевой слепотой. Помимо этого, сама структура данных остается неизменной:
functor CatList (Q : QUEUE) :> CAT_LIST =
struct
(* private stuff, not exposed by CAT_LIST *)
structure L = Lazy
structure O = Option (* from basis library *)
datatype 'a cons = ::: of 'a * 'a cons L.lazy Q.queue
infixr 5 :::
(* Q.snoc : 'a Q.queue * 'a -> 'a Q.queue *)
fun link (x ::: xs, ys) = x ::: Q.snoc (xs, ys)
(* L.delay : ('a -> 'b) -> ('a -> 'b L.lazy)
* L.force : 'a L.lazy -> 'a
* Q.uncons : 'a Q.queue -> ('a * 'a Q.queue lazy) option *)
fun linkAll (xs, ys) =
let
val xs = L.force xs
val ys = L.force ys
in
case Q.uncons ys of
NONE => xs
| SOME ys => link (xs, L.delay linkAll ys)
end
(* public stuff, exposed by CAT_LIST *)
type 'a list = 'a cons option
val empty = NONE
(* L.pure : 'a -> 'a L.lazy *)
fun append (xs, NONE) = xs
| append (NONE, xs) = xs
| append (SOME xs, SOME ys) = SOME (link (xs, L.pure ys))
(* Q.empty : 'a Q.queue *)
fun cons (x, xs) = append (SOME (x ::: Q.empty), xs)
fun snoc (xs, x) = append (xs, SOME (x ::: Q.empty))
(* O.map : ('a -> 'b) -> ('a option -> 'b option) *)
fun uncons NONE = NONE
| uncons (SOME (x ::: xs)) = SOME (x, L.delay (O.map linkAll) (Q.uncons xs))
end
В своей книге Окасаки утверждает, что при реализации очередей с операциями O(1)
(наихудшими или амортизированными) append
и uncons
амортизируются O(1)
.
Почему его утверждение не может быть подтверждено? Учитывая реализацию очередей в реальном времени (все операции выполняются в наихудшем случае O(1)
), append
и uncons
мне кажутся наихудшими O(1)
. Все рекурсивные вызовы в linkAll
охраняются L.delay
, и ни одна из публичных операций никогда не вызывает более одной приостановки. Мои рассуждения (или мой код) неверны?
2 ответа
Относительно вашего вопроса о списках цепочек. Также необходимо принять во внимание, что принуждение к подвеске может привести к каскаду сил. Обратите внимание, что linkAll форсирует свой входной список, который может быть еще не оцененной приостановкой. Форсирование s может, в свою очередь, вызвать другую приостановку и так далее. Это действительно произойдет, если вы выполните последовательность операций recons над списком. В конце концов, самое большее после операций 'n' recons, структура данных может выродиться в наивный список Cons (Cons (x, Cons (y, ...))), где 'n' - это размер списка. Все дальнейшие операции recons будут иметь постоянное время. Следовательно, структура данных имеет амортизированную постоянную временную привязку, но это не худший случай.
Неизменяемые (чисто функциональные) очереди имеют только амортизированные конструкторы и деструкторы O (1).
Похожие вопросы
Новые вопросы
data-structures
Структура данных - это способ организации данных таким образом, который позволяет эффективно запрашивать и / или обновлять конкретные свойства этих данных.