package fj.data; import fj.Equal; import fj.F; import fj.F2; import fj.F2Functions; import fj.F3; import fj.Function; import fj.Ord; import fj.P; import fj.P1; import fj.P2; import fj.P3; import fj.Show; import fj.function.Integers; import java.util.Iterator; import static fj.Function.compose; import static fj.Function.curry; import static fj.Function.flip; import static fj.Function.join; import static fj.Function.uncurryF2; import static fj.data.Option.none; import static fj.data.Option.some; import static fj.data.Stream.nil; import static fj.data.Stream.repeat; /** * 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. * <p/> * Based on the pointedlist library by Jeff Wheeler. */ public final class Zipper<A> implements Iterable<Zipper<A>> { private final Stream<A> left; private final A focus; private final Stream<A> right; private Zipper(final Stream<A> left, final A focus, final Stream<A> right) { this.left = left; this.focus = focus; this.right = right; } /** * Creates a new Zipper with the given streams before and after the focus, and the given focused item. * * @param left The stream of elements before the focus. * @param focus The element under focus. * @param right The stream of elements after the focus. * @return a new Zipper with the given streams before and after the focus, and the given focused item. */ public static <A> Zipper<A> zipper(final Stream<A> left, final A focus, final Stream<A> right) { return new Zipper<>(left, focus, right); } /** * Creates a new Zipper from the given triple. * * @param p A triple of the elements before the focus, the focus element, and the elements after the focus, * respectively. * @return a new Zipper created from the given triple. */ public static <A> Zipper<A> zipper(final P3<Stream<A>, A, Stream<A>> p) { return new Zipper<>(p._1(), p._2(), p._3()); } /** * First-class constructor of zippers. * * @return A function that yields a new zipper given streams on the left and right and a focus element. */ public static <A> F3<Stream<A>, A, Stream<A>, Zipper<A>> zipper() { return Zipper::zipper; } /** * Returns the product-3 representation of this Zipper. * * @return the product-3 representation of this Zipper. */ public P3<Stream<A>, A, Stream<A>> p() { return P.p(left, focus, right); } /** * A first-class function that yields the product-3 representation of a given Zipper. * * @return A first-class function that yields the product-3 representation of a given Zipper. */ public static <A> F<Zipper<A>, P3<Stream<A>, A, Stream<A>>> p_() { return Zipper::p; } /** * An Ord instance for Zippers. * * @param o An Ord instance for the element type. * @return An Ord instance for Zippers. */ public static <A> Ord<Zipper<A>> ord(final Ord<A> o) { final Ord<Stream<A>> so = Ord.streamOrd(o); return Ord.p3Ord(so, o, so).contramap(Zipper.p_()); } /** * An Equal instance for Zippers. * * @param e An Equal instance for the element type. * @return An Equal instance for Zippers. */ public static <A> Equal<Zipper<A>> eq(final Equal<A> e) { final Equal<Stream<A>> se = Equal.streamEqual(e); return Equal.p3Equal(se, e, se).contramap(Zipper.p_()); } /** * A Show instance for Zippers. * * @param s A Show instance for the element type. * @return A Show instance for Zippers. */ public static <A> Show<Zipper<A>> show(final Show<A> s) { final Show<Stream<A>> ss = Show.streamShow(s); return Show.p3Show(ss, s, ss).contramap(Zipper.p_()); } /** * Maps the given function across the elements of this zipper (covariant functor pattern). * * @param f A function to map across this zipper. * @return A new zipper with the given function applied to all elements. */ public <B> Zipper<B> map(final F<A, B> f) { return zipper(left.map(f), f.f(focus), right.map(f)); } /** * Performs a right-fold reduction across this zipper. * * @param f The function to apply on each element of this zipper. * @param z The beginning value to start the application from. * @return the final result after the right-fold reduction. */ public <B> B foldRight(final F<A, F<B, B>> f, final B z) { return left.foldLeft(flip(f), right.cons(focus).foldRight(compose( Function.<P1<B>, B, B>andThen().f(P1.__1()), f), z)); } /** * Creates a new zipper with a single element. * * @param a The focus element of the new zipper. * @return a new zipper with a single element which is in focus. */ public static <A> Zipper<A> single(final A a) { return zipper(Stream.nil(), a, Stream.nil()); } /** * Possibly create a zipper if the provided stream has at least one element, otherwise None. * The provided stream's head will be the focus of the zipper, and the rest of the stream will follow * on the right side. * * @param a The stream from which to create a zipper. * @return a new zipper if the provided stream has at least one element, otherwise None. */ @SuppressWarnings("IfMayBeConditional") public static <A> Option<Zipper<A>> fromStream(final Stream<A> a) { if (a.isEmpty()) return none(); else return some(zipper(Stream.nil(), a.head(), a.tail()._1())); } /** * Possibly create a zipper if the provided stream has at least one element, otherwise None. * The provided stream's last element will be the focus of the zipper, following the rest of the stream in order, * to the left. * * @param a The stream from which to create a zipper. * @return a new zipper if the provided stream has at least one element, otherwise None. */ public static <A> Option<Zipper<A>> fromStreamEnd(final Stream<A> a) { if (a.isEmpty()) return none(); else { final Stream<A> xs = a.reverse(); return some(zipper(xs.tail()._1(), xs.head(), Stream.nil())); } } /** * Returns the focus element of this zipper. * * @return the focus element of this zipper. */ public A focus() { return focus; } /** * Possibly moves the focus to the next element in the list. * * @return An optional zipper with the focus moved one element to the right, if there are elements to the right of * focus, otherwise None. */ public Option<Zipper<A>> next() { return right.isEmpty() ? Option.none() : some(tryNext()); } /** * Attempts to move the focus to the next element, or throws an error if there are no more elements. * * @return A zipper with the focus moved one element to the right, if there are elements to the right of * focus, otherwise throws an error. */ public Zipper<A> tryNext() { if (right.isEmpty()) throw new Error("Tried next at the end of a zipper."); else return zipper(left.cons(focus), right.head(), right.tail()._1()); } /** * Possibly moves the focus to the previous element in the list. * * @return An optional zipper with the focus moved one element to the left, if there are elements to the left of * focus, otherwise None. */ public Option<Zipper<A>> previous() { return left.isEmpty() ? Option.none() : some(tryPrevious()); } /** * Attempts to move the focus to the previous element, or throws an error if there are no more elements. * * @return A zipper with the focus moved one element to the left, if there are elements to the left of * focus, otherwise throws an error. */ public Zipper<A> tryPrevious() { if (left.isEmpty()) throw new Error("Tried previous at the beginning of a zipper."); else return zipper(left.tail()._1(), left.head(), right.cons(focus)); } /** * First-class version of the next() function. * * @return A function that moves the given zipper's focus to the next element. */ public static <A> F<Zipper<A>, Option<Zipper<A>>> next_() { return Zipper::next; } /** * First-class version of the previous() function. * * @return A function that moves the given zipper's focus to the previous element. */ public static <A> F<Zipper<A>, Option<Zipper<A>>> previous_() { return Zipper::previous; } /** * Inserts an element to the left of the focus, then moves the focus to the new element. * * @param a A new element to insert into this zipper. * @return A new zipper with the given element in focus, and the current focus element on its right. */ public Zipper<A> insertLeft(final A a) { return zipper(left, a, right.cons(focus)); } /** * Inserts an element to the right of the focus, then moves the focus to the new element. * * @param a A new element to insert into this zipper. * @return A new zipper with the given element in focus, and the current focus element on its left. */ public Zipper<A> insertRight(final A a) { return zipper(left.cons(focus), a, right); } /** * Possibly deletes the element at the focus, then moves the element on the left into focus. * If no element is on the left, focus on the element to the right. * Returns None if the focus element is the only element in this zipper. * * @return A new zipper with this zipper's focus element removed, or None if deleting the focus element * would cause the zipper to be empty. */ public Option<Zipper<A>> deleteLeft() { return left.isEmpty() && right.isEmpty() ? Option.none() : some(zipper(left.isEmpty() ? left : left.tail()._1(), left.isEmpty() ? right.head() : left.head(), left.isEmpty() ? right.tail()._1() : right)); } /** * Possibly deletes the element at the focus, then moves the element on the right into focus. * If no element is on the right, focus on the element to the left. * Returns None if the focus element is the only element in this zipper. * * @return A new zipper with this zipper's focus element removed, or None if deleting the focus element * would cause the zipper to be empty. */ public Option<Zipper<A>> deleteRight() { return left.isEmpty() && right.isEmpty() ? Option.none() : some(zipper(right.isEmpty() ? left.tail()._1() : left, right.isEmpty() ? left.head() : right.head(), right.isEmpty() ? right : right.tail()._1())); } /** * Deletes all elements in the zipper except the focus. * * @return A new zipper with the focus element as the only element. */ public Zipper<A> deleteOthers() { final Stream<A> nil = nil(); return zipper(nil, focus, nil); } /** * Returns the length of this zipper. * * @return the length of this zipper. */ public int length() { return foldRight(Function.constant(Integers.add.f(1)), 0); } /** * Returns whether the focus is on the first element. * * @return true if the focus is on the first element, otherwise false. */ public boolean atStart() { return left.isEmpty(); } /** * Returns whether the focus is on the last element. * * @return true if the focus is on the last element, otherwise false. */ public boolean atEnd() { return right.isEmpty(); } /** * Creates a zipper of variations of this zipper, in which each element is focused, * with this zipper as the focus of the zipper of zippers (comonad pattern). * * @return a zipper of variations of the provided zipper, in which each element is focused, * with this zipper as the focus of the zipper of zippers. */ public Zipper<Zipper<A>> positions() { final Stream<Zipper<A>> left = Stream.unfold( p -> p.previous().map(join(P.p2())), this); final Stream<Zipper<A>> right = Stream.unfold( p -> p.next().map(join(P.p2())), this); return zipper(left, this, right); } /** * Maps over variations of this zipper, such that the given function is applied to each variation (comonad pattern). * * @param f The comonadic function to apply for each variation of this zipper. * @return A new zipper, with the given function applied for each variation of this zipper. */ public <B> Zipper<B> cobind(final F<Zipper<A>, B> f) { return positions().map(f); } /** * Zips the elements of this zipper with a boolean that indicates whether that element has focus. * All of the booleans will be false, except the focused element. * * @return A new zipper of pairs, with each element of this zipper paired with a boolean that is true if that * element has focus, and false otherwise. */ public Zipper<P2<A, Boolean>> zipWithFocus() { return zipper(left.zip(repeat(false)), P.p(focus, true), right.zip(repeat(false))); } /** * Move the focus to the specified index. * * @param n The index to which to move the focus. * @return A new zipper with the focus moved to the specified index, or none if there is no such index. */ public Option<Zipper<A>> move(final int n) { final int ll = left.length(); final int rl = right.length(); Option<Zipper<A>> p = some(this); if (n < 0 || n >= length()) return none(); else if (ll >= n) for (int i = ll - n; i > 0; i--) p = p.bind(Zipper.previous_()); else if (rl >= n) for (int i = rl - n; i > 0; i--) p = p.bind(Zipper.next_()); return p; } /** * A first-class version of the move function. * * @return A function that moves the focus of the given zipper to the given index. */ public static <A> F<Integer, F<Zipper<A>, Option<Zipper<A>>>> move() { return curry((i, a) -> a.move(i)); } /** * Moves the focus to the element matching the given predicate, if present. * * @param p A predicate to match. * @return A new zipper with the nearest matching element focused if it is present in this zipper. */ public Option<Zipper<A>> find(final F<A, Boolean> p) { if (p.f(focus())) return some(this); else { final Zipper<Zipper<A>> ps = positions(); return ps.lefts().interleave(ps.rights()).find(zipper -> p.f(zipper.focus())); } } /** * Returns the index of the focus. * * @return the index of the focus. */ public int index() { return left.length(); } /** * Move the focus to the next element. If the last element is focused, loop to the first element. * * @return A new zipper with the next element focused, unless the last element is currently focused, in which case * the first element becomes focused. */ public Zipper<A> cycleNext() { if (left.isEmpty() && right.isEmpty()) return this; else if (right.isEmpty()) { final Stream<A> xs = left.reverse(); return zipper(Stream.nil(), xs.head(), xs.tail()._1().snoc(P.p(focus))); } else return tryNext(); } /** * Move the focus to the previous element. If the first element is focused, loop to the last element. * * @return A new zipper with the previous element focused, unless the first element is currently focused, * in which case the last element becomes focused. */ public Zipper<A> cyclePrevious() { if (left.isEmpty() && right.isEmpty()) return this; else if (left.isEmpty()) { final Stream<A> xs = right.reverse(); return zipper(xs.tail()._1().snoc(P.p(focus)), xs.head(), Stream.nil()); } else return tryPrevious(); } /** * Possibly deletes the element at the focus, then move the element on the left into focus. If no element is on the * left, focus on the last element. If the deletion will cause the list to be empty, return None. * * @return A new zipper with the focused element removed, and focus on the previous element to the left, or the last * element if there is no element to the left. */ public Option<Zipper<A>> deleteLeftCycle() { if (left.isEmpty() && right.isEmpty()) return none(); else if (left.isNotEmpty()) return some(zipper(left.tail()._1(), left.head(), right)); else { final Stream<A> xs = right.reverse(); return some(zipper(xs.tail()._1(), xs.head(), Stream.nil())); } } /** * Possibly deletes the element at the focus, then move the element on the right into focus. If no element is on the * right, focus on the first element. If the deletion will cause the list to be empty, return None. * * @return A new zipper with the focused element removed, and focus on the next element to the right, or the first * element if there is no element to the right. */ public Option<Zipper<A>> deleteRightCycle() { if (left.isEmpty() && right.isEmpty()) return none(); else if (right.isNotEmpty()) return some(zipper(left, right.head(), right.tail()._1())); else { final Stream<A> xs = left.reverse(); return some(zipper(Stream.nil(), xs.head(), xs.tail()._1())); } } /** * Replaces the element in focus with the given element. * * @param a An element to replace the focused element with. * @return A new zipper with the given element in focus. */ public Zipper<A> replace(final A a) { return zipper(left, a, right); } /** * Returns the Stream representation of this zipper. * * @return A stream that contains all the elements of this zipper. */ public Stream<A> toStream() { return left.reverse().snoc(P.p(focus)).append(right); } /** * Returns a Stream of the elements to the left of focus. * * @return a Stream of the elements to the left of focus. */ public Stream<A> lefts() { return left; } /** * Returns a Stream of the elements to the right of focus. * * @return a Stream of the elements to the right of focus. */ public Stream<A> rights() { return right; } /** * Zips this Zipper with another, applying the given function lock-step over both zippers in both directions. * The structure of the resulting Zipper is the structural intersection of the two Zippers. * * @param bs A Zipper to zip this one with. * @param f A function with which to zip together the two Zippers. * @return The result of applying the given function over this Zipper and the given Zipper, location-wise. */ public <B, C> Zipper<C> zipWith(final Zipper<B> bs, final F2<A, B, C> f) { return F2Functions.zipZipperM(f).f(this, bs); } /** * Zips this Zipper with another, applying the given function lock-step over both zippers in both directions. * The structure of the resulting Zipper is the structural intersection of the two Zippers. * * @param bs A Zipper to zip this one with. * @param f A function with which to zip together the two Zippers. * @return The result of applying the given function over this Zipper and the given Zipper, location-wise. */ public <B, C> Zipper<C> zipWith(final Zipper<B> bs, final F<A, F<B, C>> f) { return zipWith(bs, uncurryF2(f)); } /** * Returns an iterator of all the positions of this Zipper, starting from the leftmost position. * * @return An iterator of all the positions of this Zipper, starting from the leftmost position. */ public Iterator<Zipper<A>> iterator() { return positions().toStream().iterator(); } }