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

scalaz.Zipper.scala Maven / Gradle / Ivy

The newest version!
package scalaz

import annotation.tailrec

/**
 * Provides a pointed stream, which is a non-empty zipper-like stream structure that tracks an index (focus)
 * position in a stream. Focus can be moved forward and backwards through the stream, elements can be inserted
 * before or after the focused position, and the focused item can be deleted.
 * 

* Based on the pointedlist library by Jeff Wheeler. */ sealed trait Zipper[+A] { val focus: A val lefts: Stream[A] val rights: Stream[A] import Scalaz._ /** * Get the Stream representation of this Zipper. This fully traverses `lefts`. `rights` is * not evaluated. */ def toStream: Stream[A] = lefts.reverse ++ focus #:: rights def copy[AA >: A](lefts: Stream[AA] = lefts, focus: AA = focus, rights: Stream[AA] = rights): Zipper[AA] = zipper[AA](lefts, focus, rights) /** * Update the focus in this zipper. */ def update[AA >: A](focus: AA) = { this.copy(this.lefts, focus, this.rights) } /** * Apply f to the focus and update with the result. */ def modify[AA >: A](f: A => AA) = this.update(f(this.focus)) /** * Possibly moves to next element to the right of focus. */ def next: Option[Zipper[A]] = rights match { case Stream.Empty => None case r #:: rs => Some(zipper(Stream.cons(focus, lefts), r, rs)) } /** * Moves to the next element to the right of focus, or error if there is no element on the right. */ def tryNext: Zipper[A] = next err "cannot move to next element" /** * Possibly moves to the previous element to the left of focus. */ def previous: Option[Zipper[A]] = lefts match { case Stream.Empty => None case l #:: ls => Some(zipper(ls, l, Stream.cons(focus, rights))) } /** * Moves to the previous element to the left of focus, or error if there is no element on the left. */ def tryPrevious: Zipper[A] = previous err "cannot move to previous element" /** * An alias for insertRight */ def insert[AA >: A]: (AA => Zipper[AA]) = insertRight(_: AA) /** * Inserts an element to the left of focus and focuses on the new element. */ def insertLeft[AA >: A](y: AA): Zipper[AA] = zipper(lefts, y, focus #:: rights) /** * Inserts an element to the right of focus and focuses on the new element. */ def insertRight[AA >: A](y: AA): Zipper[AA] = zipper(focus #:: lefts, y, rights) /** * An alias for deleteRight */ def delete: Option[Zipper[A]] = deleteRight /** * Deletes the element at focus and moves the focus to the left. If there is no element on the left, * focus is moved to the right. */ def deleteLeft: Option[Zipper[A]] = rights match { case Stream.Empty => None case r #:: rs => Some(lefts match { case Stream.Empty => zipper(Stream.Empty, r, rs) case l #:: ls => zipper(ls, l, rights) }) } /** * Deletes the element at focus and moves the focus to the right. If there is no element on the right, * focus is moved to the left. */ def deleteRight: Option[Zipper[A]] = rights match { case Stream.Empty => None case r #:: rs => Some(lefts match { case Stream.Empty => zipper(Stream.Empty, r, rs) case l #:: ls => zipper(ls, l, rights) }) } /** * Deletes all elements except the focused element. */ def deleteOthers: Zipper[A] = zipper(Stream.Empty, focus, Stream.Empty) def length: Int = this.foldr(0)(((a: Any, b: Int) => b + 1)(_, _)) /** * Whether the focus is on the first element in the zipper. */ def atStart: Boolean = lefts.isEmpty /** * Whether the focus is on the last element in the zipper. */ def atEnd: Boolean = rights.isEmpty /** * Pairs each element with a boolean indicating whether that element has focus. */ def withFocus: Zipper[(A, Boolean)] = zipper(lefts.zip(Stream.continually(false)), (focus, true), rights.zip(Stream.continually(false))) /** * Moves focus to the nth element of the zipper, or None if there is no such element. */ def move(n: Int): Option[Zipper[A]] = { @tailrec def move0(z: Option[Zipper[A]], n: Int): Option[Zipper[A]] = if (n > 0 && rights.isEmpty || n < 0 && lefts.isEmpty) None else { if (n == 0) z else if (n > 0) move0(z >>= ((_:Zipper[A]).next), n - 1) else move0(z >>= ((_:Zipper[A]).previous), n + 1) } move0(Some(this), n) } /** * Moves focus to the start of the zipper. */ def start: Zipper[A] = { val rights = this.lefts ++ focus #:: this.rights this.copy(Stream.Empty, rights.head, rights.tail) } /** * Moves focus to the end of the zipper. */ def end: Zipper[A] = { val lefts = this.lefts ++ focus #:: this.rights this.copy(lefts.init, lefts.last, Stream.empty) } /** * Moves focus to the nearest element matching the given predicate, preferring the left, * or None if no element matches. */ def findZ(p: A => Boolean): Option[Zipper[A]] = if (p(focus)) Some(this) else { val c = this.positions c.lefts.merge(c.rights).find((x => p(x.focus))) } /** * Given a traversal function, find the first element along the traversal that matches a given predicate. */ def findBy[AA >: A](f: Zipper[AA] => Option[Zipper[AA]])(p: AA => Boolean): Option[Zipper[AA]] = { f(this) ∗ (x => if (p(x.focus)) Some(x) else x.findBy(f)(p)) } /** * Moves focus to the nearest element on the right that matches the given predicate, * or None if there is no such element. */ def findNext[AA >: A](p: AA => Boolean): Option[Zipper[AA]]= findBy((z: Zipper[A]) => z.next)(p) /** * Moves focus to the previous element on the left that matches the given predicate, * or None if there is no such element. */ def findPrevious[AA >: A](p: AA => Boolean): Option[Zipper[AA]]= findBy((z: Zipper[A]) => z.previous)(p) /** * A zipper of all positions of the zipper, with focus on the current position. */ def positions: Zipper[Zipper[A]] = { val left = this.unfold[Stream, Zipper[A]]((p: Zipper[A]) => p.previous.map(x => (x, x))) val right = this.unfold[Stream, Zipper[A]]((p: Zipper[A]) => p.next.map(x => (x, x))) zipper(left, this, right) } /** * The index of the focus. */ def index: Int = lefts.length /** * Moves focus to the next element. If the last element is currently focused, loop to the first element. */ def nextC: Zipper[A] = (lefts, rights) match { case (Stream.Empty, Stream.Empty) => this case (_, Stream.Empty) => { val xs = lefts.reverse zipper(rights, xs.head, xs.tail.append(Stream(focus))) } case (_, _) => tryNext } /** * Moves focus to the previous element. If the first element is currently focused, loop to the last element. */ def previousC: Zipper[A] = (lefts, rights) match { case (Stream.Empty, Stream.Empty) => this case (Stream.Empty, _) => { val xs = rights.reverse zipper(xs.tail.append(Stream(focus)), xs.head, lefts) } case (_, _) => tryPrevious } /** * Deletes the focused element and moves focus to the left. If the focus was on the first element, * focus is moved to the last element. */ def deleteLeftC: Option[Zipper[A]] = rights match { case Stream.Empty => None case _ #:: _ => Some(lefts match { case l #:: ls => zipper(ls, l, rights) case Stream.Empty => { val r = rights.reverse zipper(r.tail, r.head, Stream.Empty) } }) } /** * Deletes the focused element and moves focus to the right. If the focus was on the last element, * focus is moved to the first element. */ def deleteRightC: Option[Zipper[A]] = lefts match { case Stream.Empty => None case _ #:: _ => Some(rights match { case r #:: rs => zipper(lefts, r, rs) case Stream.Empty => { val l = lefts.reverse zipper(Stream.Empty, l.head, l.tail) } }) } /** * An alias for deleteRightC */ def deleteC: Option[Zipper[A]] = deleteRightC } trait Zippers { def zipper[A](ls: Stream[A], a: A, rs: Stream[A]): Zipper[A] = new Zipper[A] { val focus = a val lefts = ls val rights = rs override def toString = "" } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy