All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalaz.Dequeue.scala Maven / Gradle / Ivy

The newest version!
package scalaz

/**
  * A Double-ended queue, based on the Bankers Double Ended Queue as
  * described by C. Okasaki in "Purely Functional Data Structures"
  *
  * A queue that allows items to be put onto either the front (cons)
  * or the back (snoc) of the queue in constant time, and constant
  * time access to the element at the very front or the very back of
  * the queue.  Dequeueing an element from either end is constant time
  * when amortized over a number of dequeues.
  *
  * This queue maintains an invariant that whenever there are at least
  * two elements in the queue, neither the front list nor back list
  * are empty.  In order to maintain this invariant, a dequeue from
  * either side which would leave that side empty constructs the
  * resulting queue by taking elements from the opposite side
  */
sealed abstract class Dequeue[A] {
  import Maybe.Just
  import Dequeue.reverseNEL

  def isEmpty: Boolean

  def frontMaybe: Maybe[A]
  def backMaybe: Maybe[A]

  /**
    * dequeue from the front of the queue
    */
  def uncons: Maybe[(A, Dequeue[A])] = this match {
    case EmptyDequeue() => Maybe.empty
    case SingletonDequeue(a) => Just((a, EmptyDequeue()))
    case FullDequeue(NonEmptyList(f, INil()), 1, NonEmptyList(single,INil()), 1) => Just((f, SingletonDequeue(single)))
    case FullDequeue(NonEmptyList(f, INil()), 1, NonEmptyList(x, ICons(xx, xs)), bs) => {
      val xsr = reverseNEL(NonEmptyList.nel(xx, xs))
      Just((f, FullDequeue(xsr, bs-1, NonEmptyList.nel(x, IList.empty), 1)))
    }
    case FullDequeue(NonEmptyList(f, ICons(ff, fs)), s, back, bs) => Just((f, FullDequeue(NonEmptyList.nel(ff, fs), s - 1, back, bs)))
  }

  /**
    * dequeue from the back of the queue
    */
  def unsnoc: Maybe[(A, Dequeue[A])] = this match {
    case EmptyDequeue() => Maybe.empty
    case SingletonDequeue(a) => Just((a, EmptyDequeue()))
    case FullDequeue(NonEmptyList(single, INil()), 1, NonEmptyList(b, INil()), 1) => Just((b, SingletonDequeue(single)))
    case FullDequeue(NonEmptyList(x, ICons(xx,xs)), fs, NonEmptyList(b, INil()), 1) => {
      val xsr = reverseNEL(NonEmptyList.nel(xx, xs))
      Just((b, FullDequeue(NonEmptyList.nel(x, IList.empty), 1, xsr, fs-1)))
    }

    case FullDequeue(front, fs, NonEmptyList(b, ICons(bb,bs)), s) => Just((b, FullDequeue(front, fs, NonEmptyList.nel(bb,bs), s-1)))
  }


  /**
    * enqueue to the front of the queue
    */
  def cons(a: A): Dequeue[A] = this match {
    case EmptyDequeue() => SingletonDequeue(a)
    case SingletonDequeue(single) => FullDequeue(NonEmptyList.nel(a, IList.empty), 1, NonEmptyList.nel(single, IList.empty), 1 )
    case FullDequeue(front, fs, back, bs) => FullDequeue(NonEmptyList.nel(a, ICons(front.head, front.tail)), fs+1, back, bs)
  }

  /**
    * enqueue on to the back of the queue
    */
  def snoc(a: A): Dequeue[A] = this match {
    case EmptyDequeue() => SingletonDequeue(a)
    case SingletonDequeue(single) => FullDequeue(NonEmptyList.nel(single, INil[A]()), 1, NonEmptyList.nel(a, INil[A]()), 1 )
    case FullDequeue(front, fs, back, bs) => FullDequeue(front, fs, NonEmptyList.nel(a, ICons(back.head, back.tail)), bs+1)
  }

  /**
    * alias for cons
    */
  def +:(a: A): Dequeue[A] = cons(a)

  /**
    * alias for snoc
    */
  def :+(a: A): Dequeue[A] = snoc(a)

  /**
    * convert this queue to a LazyList of elements from front to back
    */
  def toLazyList: LazyList[A] = std.lazylist.unfoldm(this)(_.uncons)

  /**
    * convert this queue to a LazyList of elements from back to front
    */
  def toBackLazyList: LazyList[A] = std.lazylist.unfoldm(this)(_.unsnoc)

  /**
    * convert this queue to a list of elements from front to back
    */
  def toIList: IList[A] = this match {
    case EmptyDequeue() => IList.empty
    case SingletonDequeue(a) => ICons(a, IList.empty)
    case FullDequeue(front, fs, back, bs) => front.head +: (front.tail ++ (back.tail reverse_::: ICons(back.head, IList.empty)))
  }

  /**
    * convert this queue to a list of elements from back to front
    */
  def toBackIList: IList[A] = this match {
    case EmptyDequeue() => IList.empty
    case SingletonDequeue(a) => ICons(a, IList.empty)
    case FullDequeue(front, fs, back, bs) => back.head +: (back.tail ++ (front.tail reverse_::: ICons(front.head, IList.empty)))
  }

  /**
    * Append another Dequeue to this dequeue
    */
  def ++(other: Dequeue[A]): Dequeue[A] = this match {
    case EmptyDequeue() => other
    case SingletonDequeue(a) => a +: other
    case FullDequeue(f,fs,b,bs) => other match {
      case EmptyDequeue() => this
      case SingletonDequeue(a) => this :+ a
      case FullDequeue(of,ofs,ob,obs) =>
        FullDequeue(NonEmptyList.nel(f.head, (f.tail ++
                                                ((b.head +: b.tail) reverse_:::
                                                   ICons(of.head, of.tail)))),
                                              fs + bs + ofs,
                                              ob,
                                              obs)
    }
  }

  def foldLeft[B](b: B)(f: (B,A) => B): B = this match {
    case EmptyDequeue() => b
    case SingletonDequeue(a) => f(b, a)
    case FullDequeue(front,_,back,_) => {
      val frontb = front.tail.foldLeft(f(b,front.head))(f)
      val backb = back.tail.foldRight(frontb)((a, b) => f(b,a))
      f(backb,back.head)
    }
  }

  def foldRight[B](b: B)(f: (A,B) => B): B = this match {
    case EmptyDequeue() => b
    case SingletonDequeue(a) => f(a, b)
    case FullDequeue(front,_,back,_) => {
      val backb = back.tail.foldLeft(f(back.head, b))((b,a) => f(a,b))
      val frontb = front.tail.foldRight(backb)(f)
      f(front.head, frontb)
    }
  }


  def map[B](f: A => B): Dequeue[B] = {
    this match {
      case EmptyDequeue() => EmptyDequeue()
      case SingletonDequeue(a) => SingletonDequeue(f(a))
      case FullDequeue(front, fs, back, bs) => {
        val F = Functor[NonEmptyList]
        FullDequeue(F.map(front)(f), fs, F.map(back)(f), bs)
      }
    }
  }

  def size: Int = this match {
    case EmptyDequeue() => 0
    case SingletonDequeue(_) => 1
    case FullDequeue(_, fs, _, bs) => fs + bs
  }

  def reverse: Dequeue[A] = this match {
    case FullDequeue(front, fs, back, bs) => FullDequeue(back, bs, front, fs)
    case x => x
  }
}

object Dequeue extends DequeueInstances {
  def apply[A](as: A*): Dequeue[A] = as.foldLeft[Dequeue[A]](empty)((q, a) => q :+ a)

  def fromFoldable[F[_],A](fa: F[A])(implicit F: Foldable[F]): Dequeue[A] =
    F.foldLeft[A,Dequeue[A]](fa,empty)((q,a) => q :+ a)

  def empty[A]: Dequeue[A] = EmptyDequeue()

  private def reverseNEL[A](fa: NonEmptyList[A]): NonEmptyList[A] = {
    @annotation.tailrec
    def loop(xs: IList[A], acc: IList[A]): NonEmptyList[A] =
      (xs: @unchecked) match {
        case ICons(h, INil()) =>
          NonEmptyList.nel(h, acc)
        case ICons(h, t) =>
          loop(t, h :: acc)
      }
    loop(fa.head :: fa.tail, IList.empty)
  }
}

/**
  * special case of the queue when it contains just a single element
  * which can be accessed from either side of the queue
  */
private[scalaz] final case class SingletonDequeue[A](single: A) extends Dequeue[A] {
  override def isEmpty = false
  override def frontMaybe: Maybe[A] = Maybe.Just(single)
  override def backMaybe: Maybe[A] = Maybe.Just(single)
}

/**
  * a queue which has at least two elements, it is guaranteed that the
  * front list and back lists cannot be empty
  */
private[scalaz] final case class FullDequeue[A](front: NonEmptyList[A], fsize: Int, back: NonEmptyList[A], backSize: Int) extends Dequeue[A]  {
  override def isEmpty = false
  override def frontMaybe: Maybe[A] = Maybe.just(front.head)
  override def backMaybe: Maybe[A] = Maybe.just(back.head)
}
/**
  * a queue which has no elements
  */
private[scalaz] final case class EmptyDequeue[A] private() extends Dequeue[A] {
  override val isEmpty = true
  override val frontMaybe: Maybe[A] = Maybe.empty[A]
  override val backMaybe: Maybe[A] = Maybe.empty[A]
}
object EmptyDequeue {
  private[this] val value: Dequeue[Nothing] = new EmptyDequeue[Nothing]
  def apply[A](): Dequeue[A] = value.asInstanceOf[Dequeue[A]]
}

sealed abstract class DequeueInstances {
  implicit def equalDequeue[A](implicit A0: Equal[A]): Equal[Dequeue[A]] =
    new DequeueEqual[A] {
      val A = A0
    }

  implicit def dequeueMonoid[A]: Monoid[Dequeue[A]] = new Monoid[Dequeue[A]] {
    def zero: Dequeue[A] = Dequeue.empty
    def append(a: Dequeue[A], b: => Dequeue[A]): Dequeue[A] = a ++ b
  }

  implicit val dequeueInstances: Traverse[Dequeue] & IsEmpty[Dequeue] & MonadPlus[Dequeue] & Alt[Dequeue] =
    new Traverse[Dequeue] with IsEmpty[Dequeue] with MonadPlus[Dequeue] with Alt[Dequeue] {
      override def foldRight[A,B](fa: Dequeue[A], b: => B)(f: (A, => B) =>B): B = fa.foldRight(b)((a,b) => f(a,b))
      override def foldLeft[A,B](fa: Dequeue[A], b: B)(f: (B,A)=>B): B = fa.foldLeft(b)(f)
      override def foldMap[A,B](fa: Dequeue[A])(f: A => B)(implicit F: Monoid[B]): B = fa.foldLeft(F.zero)((b,a) => F.append(b, f(a)))
      override def empty[A]: Dequeue[A] = Dequeue.empty
      override def plus[A](a: Dequeue[A], b: => Dequeue[A]): Dequeue[A] = a ++ b
      override def alt[A](a: => Dequeue[A], b: => Dequeue[A]): Dequeue[A] = plus(a, b)
      override def isEmpty[A](fa: Dequeue[A]) = fa.isEmpty
      override def length[A](fa: Dequeue[A]) = fa.size
      override def map[A,B](fa: Dequeue[A])(f: A => B): Dequeue[B] = fa map f
      override def point[A](a: => A): Dequeue[A] = Dequeue(a)
      override def bind[A, B](fa: Dequeue[A])(f: A => Dequeue[B]): Dequeue[B] = foldMap(fa)(f)
      override def traverseImpl[F[_], A, B](fa: Dequeue[A])(f: A => F[B])(implicit F: Applicative[F]): F[Dequeue[B]] =
        fa.foldRight(F.point(Dequeue.empty[B]))((a, fbs) => F.apply2(f(a), fbs)(_ +: _))

    }
}

private[scalaz] trait DequeueEqual[A] extends Equal[Dequeue[A]] {
  implicit def A: Equal[A]
  import std.lazylist._

  final override def equal(a: Dequeue[A], b: Dequeue[A]): Boolean =
    Equal[LazyList[A]].equal(a.toLazyList, b.toLazyList)
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy