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

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, и ни одна из публичных операций никогда не вызывает более одной приостановки. Мои рассуждения (или мой код) неверны?

4
pyon 1 Фев 2015 в 22:04

2 ответа

Лучший ответ

Относительно вашего вопроса о списках цепочек. Также необходимо принять во внимание, что принуждение к подвеске может привести к каскаду сил. Обратите внимание, что linkAll форсирует свой входной список, который может быть еще не оцененной приостановкой. Форсирование s может, в свою очередь, вызвать другую приостановку и так далее. Это действительно произойдет, если вы выполните последовательность операций recons над списком. В конце концов, самое большее после операций 'n' recons, структура данных может выродиться в наивный список Cons (Cons (x, Cons (y, ...))), где 'n' - это размер списка. Все дальнейшие операции recons будут иметь постоянное время. Следовательно, структура данных имеет амортизированную постоянную временную привязку, но это не худший случай.

1
Ravi Mad 20 Дек 2015 в 20:05

Неизменяемые (чисто функциональные) очереди имеют только амортизированные конструкторы и деструкторы O (1).

http://www.westpoint.edu/eecs/SiteAssets/SitePages/Faculty%20Publication%20Documents/Okasaki/jfp95queue.pdf

1
seanmcl 1 Фев 2015 в 23:44