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

fj.data.NonEmptyList Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
package fj.data;

import fj.*;
import fj.function.Effect1;

import java.util.Collection;
import java.util.Iterator;

import static fj.Function.identity;
import static fj.data.Option.some;
import static fj.data.Option.somes;

/**
 * Provides an in-memory, immutable, singly linked list with total head and tail.
 *
 * @version %build.number%
 */
public final class NonEmptyList implements Iterable {
  /**
   * Returns an iterator for this non-empty list. This method exists to permit the use in a for-each loop.
   *
   * @return A iterator for this non-empty list.
   */

  public Iterator iterator() {
    return toCollection().iterator();
  }

  private final A head;

  private final List tail;

  /**
   * The first element of this linked list.
   */
  public A head() { return head; }

  /**
   * This list without the first element.
   */
  public List tail() { return tail; }

  private NonEmptyList(final A head, final List tail) {
    this.head = head;
    this.tail = tail;
  }

  /**
   * Prepend the given value to this list.
   *
   * @param a The value to prepend.
   * @return A non-empty list with an extra element.
   */
  public NonEmptyList cons(final A a) {
    return nel(a, tail.cons(head));
  }

  /**
   * Appends (snoc) the given element to this non empty list to produce a new non empty list. O(n).
   *
   * @param a The element to append to this non empty list.
   * @return A new non empty list with the given element appended.
   */
  public NonEmptyList snoc(final A a) {
    return nel(head, tail.snoc(a));
  }

  /**
   * The length of this list.
   *
   * @return The length of this list.
   */
  public int length() { return 1 + tail.length(); }

  /**
   * Appends the given list to this list.
   *
   * @param as The list to append.
   * @return A new list with the given list appended.
   */
  public NonEmptyList append(final NonEmptyList as) {
    final List.Buffer b = new List.Buffer();
    b.append(tail);
    b.snoc(as.head);
    b.append(as.tail);
    final List bb = b.toList();
    return nel(head, bb);
  }

  /**
   * Maps the given function across this list.
   *
   * @param f The function to map across this list.
   * @return A new list after the given function has been applied to each element.
   */
  public  NonEmptyList map(final F f) {
    return nel(f.f(head), tail.map(f));
  }

  /**
   * Binds the given function across each element of this list with a final join.
   *
   * @param f The function to apply to each element of this list.
   * @return A new list after performing the map, then final join.
   */
  public  NonEmptyList bind(final F> f) {
    final List.Buffer b = new List.Buffer();
    final NonEmptyList p = f.f(head);
    b.snoc(p.head);
    b.append(p.tail);
    tail.foreachDoEffect(new Effect1() {
        public void f(final A a) {
            final NonEmptyList p = f.f(a);
            b.snoc(p.head);
            b.append(p.tail);
        }
    });
    final List bb = b.toList();
    return nel(bb.head(), bb.tail());
  }

  /**
   * Returns a NonEmptyList of the sublists of this list.
   *
   * @return a NonEmptyList of the sublists of this list.
   */
  public NonEmptyList> sublists() {
    return fromList(
        somes(toList().toStream().substreams()
            .map(F1Functions.o(list -> fromList(list), Conversions.Stream_List())).toList())).some();
  }

  /**
   * Returns a NonEmptyList of the tails of this list. A list is considered a tail of itself for the purpose of this
   * function (Comonad pattern).
   *
   * @return A NonEmptyList of the tails of this list.
   */
  public NonEmptyList> tails() {
    return fromList(somes(toList().tails().map(new F, Option>>() {
      public Option> f(final List list) {
        return fromList(list);
      }
    }))).some();
  }

  /**
   * Maps the given function across the tails of this list (comonad pattern).
   *
   * @param f The function to map across the tails of this list.
   * @return The results of applying the given function to the tails of this list, as a NonEmptyList.
   */
  public  NonEmptyList mapTails(final F, B> f) {
    return tails().map(f);
  }

  /**
   * Intersperses the given argument between each element of this non empty list.
   *
   * @param a The separator to intersperse in this non empty list.
   * @return A non empty list with the given separator interspersed.
   */
  public NonEmptyList intersperse(final A a) {
    final List list = toList().intersperse(a);
    return nel(list.head(), list.tail());
  }

  /**
   * Reverse this non empty list in constant stack space.
   *
   * @return A new non empty list with the elements in reverse order.
   */
  public NonEmptyList reverse() {
    final List list = toList().reverse();
    return nel(list.head(), list.tail());
  }

  /**
   * Sorts this non empty list using the given order over elements using a merge sort algorithm.
   *
   * @param o The order over the elements of this non empty list.
   * @return A sorted non empty list according to the given order.
   */
  public NonEmptyList sort(final Ord o) {
    final List list = toList().sort(o);
    return nel(list.head(), list.tail());
  }

  /**
   * Zips this non empty list with the given non empty list to produce a list of pairs. If this list and the given list
   * have different lengths, then the longer list is normalised so this function never fails.
   *
   * @param bs The non empty list to zip this non empty list with.
   * @return A new non empty list with a length the same as the shortest of this list and the given list.
   */
  public  NonEmptyList> zip(final NonEmptyList bs) {
    final List> list = toList().zip(bs.toList());
    return nel(list.head(), list.tail());
  }

  /**
   * Zips this non empty list with the index of its element as a pair.
   *
   * @return A new non empty list with the same length as this list.
   */
  public NonEmptyList> zipIndex() {
    final List> list = toList().zipIndex();
    return nel(list.head(), list.tail());
  }

  /**
   * Zips this non empty list with the given non empty list using the given function to produce a new list. If this list
   * and the given list have different lengths, then the longer list is normalised so this function
   * never fails.
   *
   * @param bs The non empty list to zip this non empty list with.
   * @param f  The function to zip this non empty list and the given non empty list with.
   * @return A new non empty list with a length the same as the shortest of this list and the given list.
   */
  public  NonEmptyList zipWith(final List bs, final F> f) {
    final List list = toList().zipWith(bs, f);
    return nel(list.head(), list.tail());
  }

  /**
   * Zips this non empty list with the given non empty list using the given function to produce a new list. If this list
   * and the given list have different lengths, then the longer list is normalised so this function
   * never fails.
   *
   * @param bs The non empty list to zip this non empty list with.
   * @param f  The function to zip this non empty list and the given non empty list with.
   * @return A new non empty list with a length the same as the shortest of this list and the given list.
   */
  public  NonEmptyList zipWith(final List bs, final F2 f) {
    final List list = toList().zipWith(bs, f);
    return nel(list.head(), list.tail());
  }

  /**
   * Transforms a non empty list of pairs into a non empty list of first components and
   * a non empty list of second components.
   *
   * @param xs The non empty list of pairs to transform.
   * @return A non empty list of first components and a non empty list of second components.
   */
  public static  P2, NonEmptyList> unzip(final NonEmptyList> xs) {
    final P2, List> p = List.unzip(xs.toList());
    return P.p(nel(p._1().head(), p._1().tail()), nel(p._2().head(), p._2().tail()));
  }

  /**
   * Returns a List projection of this list.
   *
   * @return A List projection of this list.
   */
  public List toList() {
    return tail.cons(head);
  }

  /**
   * Projects an immutable collection of this non-empty list.
   *
   * @return An immutable collection of this non-empty list.
   */
  public Collection toCollection() {
    return toList().toCollection();
  }

  /**
   * Returns a function that takes a non-empty list to a list.
   *
   * @return A function that takes a non-empty list to a list.
   */
  public static  F, List> toList_() {
    return as -> as.toList();
  }

  /**
   * Return a non-empty list with the given head and tail.
   *
   * @param head The first element of the new list.
   * @param tail The remaining elements of the new list.
   * @return A non-empty list with the given head and tail.
   */
  public static  NonEmptyList nel(final A head, final List tail) {
    return new NonEmptyList(head, tail);
  }

  /**
   * Constructs a non empty list from the given elements.
   *
   * @param head The first in the non-empty list.
   * @param tail The elements to construct a list's tail with.
   * @return A non-empty list with the given elements.
   */
  public static  NonEmptyList nel(final A head, final A... tail) {
    return nel(head, List.list(tail));
  }

  /**
   * Returns a function that puts an element into a non-empty list.
   *
   * @return A function that puts an element into a non-empty list.
   */
  public static  F> nel() {
    return a -> nel(a);
  }

  /**
   * Returns a potential non-empty list from the given list. A non-value is returned if the given list is empty.
   *
   * @param as The list to construct a potential non-empty list with.
   * @return A potential non-empty list from the given list.
   */
  public static  Option> fromList(final List as) {
    return as.isEmpty() ?
           Option.>none() :
           some(nel(as.head(), as.tail()));
  }

  /**
   * Concatenate (join) a non empty list of non empty lists.
   *
   * @param o The non empty list of non empty lists to join.
   * @return A new non empty list that is the concatenation of the given lists.
   */
  public static  NonEmptyList join(final NonEmptyList> o) { return o.bind(identity()); }

  /**
   * Perform an equality test on this list which delegates to the .equals() method of the member instances.
   * This is implemented with Equal.nonEmptyListEqual using the anyEqual rule.
   *
   * @param obj the other object to check for equality against.
   * @return true if this list is equal to the provided argument
   */
  @Override public boolean equals( final Object obj ) {
    return Equal.equals0(NonEmptyList.class, this, obj, () -> Equal.nonEmptyListEqual(Equal.anyEqual()));
  }

  @Override public int hashCode() {
    return Hash.nonEmptyListHash(Hash.anyHash()).hash(this);
  }

  @Override public String toString() { return Show.nonEmptyListShow(Show.anyShow()).showS(this); }
}