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

fj.data.Seq 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 static fj.Bottom.error;
import static fj.Monoid.intAdditionMonoid;
import static fj.data.fingertrees.FingerTree.measured;

import fj.data.fingertrees.FingerTree;
import fj.data.fingertrees.MakeTree;
import fj.data.fingertrees.Measured;

import java.util.AbstractList;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * Provides an immutable finite sequence, implemented as a finger tree. This structure gives O(1) access to
 * the head and tail, as well as O(log n) random access and concatenation of sequences.
 */
public final class Seq implements Iterable {
  private static final Measured ELEM_MEASURED = measured(intAdditionMonoid, Function.constant(1));
  private static final MakeTree MK_TREE = FingerTree.mkTree(ELEM_MEASURED);
  private static final Seq EMPTY = new Seq(MK_TREE.empty());

  @SuppressWarnings("unchecked")
  private static  MakeTree mkTree() {
    return (MakeTree) MK_TREE;
  }

  private final FingerTree ftree;

  private Seq(final FingerTree ftree) {
    this.ftree = ftree;
  }

  @SuppressWarnings("unchecked")
  private static  Measured elemMeasured() {
    return (Measured) ELEM_MEASURED;
  }

  /**
   * The empty sequence.
   *
   * @return A sequence with no elements.
   */
  @SuppressWarnings("unchecked")
  public static  Seq empty() {
    return (Seq) EMPTY;
  }

  @Override
  public boolean equals(Object other) {
    return Equal.equals0(Seq.class, this, other, () -> Equal.seqEqual(Equal.anyEqual()));
  }

  /**
   * A singleton sequence.
   *
   * @param a The single element in the sequence.
   * @return A new sequence with the given element in it.
   */
  public static  Seq single(final A a) {
    return new Seq(Seq.mkTree().single(a));
  }

  public static Seq seq(final A... as) {
    return seq(List.list(as));
  }

  public static Seq seq(final List list) {
    return list.foldLeft((b, a) -> b.snoc(a), Seq.empty());
  }

  /**
   * Inserts the given element at the front of this sequence.
   *
   * @param a An element to insert at the front of this sequence.
   * @return A new sequence with the given element at the front.
   */
  public Seq cons(final A a) {
    return new Seq(ftree.cons(a));
  }

  /**
   * Inserts the given element at the end of this sequence.
   *
   * @param a An element to insert at the end of this sequence.
   * @return A new sequence with the given element at the end.
   */
  public Seq snoc(final A a) {
    return new Seq(ftree.snoc(a));
  }

  /**
   * The first element of this sequence. This is an O(1) operation.
   *
   * @return The first element if this sequence is nonempty, otherwise throws an error.
   */
  public A head() { return ftree.head(); }

  /**
   * The last element of this sequence. This is an O(1) operation.
   *
   * @return The last element if this sequence is nonempty, otherwise throws an error.
   */
  public A last() { return ftree.last(); }

  /**
   * The sequence without the first element. This is an O(1) operation.
   *
   * @return The sequence without the first element if this sequence is nonempty, otherwise throws an error.
   */
  public Seq tail() {
    return (length() == 1) ? empty() : new Seq<>(ftree.tail());
  }

  /**
   * The sequence without the last element. This is an O(1) operation.
   *
   * @return The sequence without the last element if this sequence is nonempty, otherwise throws an error.
   */
  public Seq init() {
    return (length() == 1) ? empty() : new Seq<>(ftree.init());
  }

  public Stream toStream() {
    return ftree.foldLeft((b, a) -> b.cons(a), Stream.nil()).reverse();
  }

  public final java.util.List toJavaList() {
    return new AbstractList() {
      @Override public A get(int i) { return index(i); }
      @Override public Iterator iterator() { return Seq.this.iterator(); }
      @Override public int size() { return length(); }
    };
  }

  /**
   * Returns an iterator for this seq. This method exists to permit the use in a for-each loop.
   *
   * @return A iterator for this seq.
   */
  public final Iterator iterator() {
    return new Iterator() {
      private FingerTree ftree = Seq.this.ftree;

      public boolean hasNext() {
        return !ftree.isEmpty();
      }

      public A next() {
        if (ftree.isEmpty())
          throw new NoSuchElementException();
        else {
          final A a = ftree.head();
          ftree = ftree.tail();
          return a;
        }
      }

      public void remove() {
        throw new UnsupportedOperationException();
      }
    };
  }

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

  /**
   * Appends the given sequence to this sequence.
   *
   * @param as A sequence to append to this one.
   * @return A new sequence with the given sequence appended to this one.
   */
  public Seq append(final Seq as) {
    return new Seq(ftree.append(as.ftree));
  }

  /**
   * Checks if this is the empty sequence.
   *
   * @return True if this sequence is empty, otherwise false.
   */
  public boolean isEmpty() {
    return ftree.isEmpty();
  }

  /**
   * Checks if this sequence is not empty.
   *
   * @return True if this sequence is not empty, otherwise false.
   */
  public boolean isNotEmpty() {
    return !ftree.isEmpty();
  }

  /**
   * Returns the number of elements in this sequence.
   *
   * @return the number of elements in this sequence.
   */
  public int length() {
    return ftree.measure();
  }

  public P2, Seq> split(final int i) {
    final P2, FingerTree> lr = ftree.split(index -> index > i);
    return P.p(new Seq<>(lr._1()), new Seq<>(lr._2()));
  }

  /**
   * Returns the element at the given index. This is an O(log(n)) operation.
   *
   * @param i The index of the element to return.
   * @return The element at the given index, or throws an error if the index is out of bounds.
   */
  public A index(final int i) {
    checkBounds(i);
    return ftree.lookup(Function.identity(), i)._2();
  }

  /**
   * Replace the element at the given index with the supplied value. This is an O(log(n)) operation.
   *
   * @param i The index of the element to update.
   * @param a The new value.
   *
   * @return The updated sequence, or throws an error if the index is out of bounds.
   */
  public Seq update(final int i, final A a) {
    checkBounds(i);
    final P3, A, FingerTree> lxr = ftree.split1(index -> index > i);
    return new Seq<>(lxr._1().append(lxr._3().cons(a)));
  }

  /**
   * Takes the given number of elements from the head of this sequence if they are available.
   *
   * @param n The maximum number of elements to take from this sequence.
   * @return A sequence consisting only of the first n elements of this sequence, or else the whole sequence,
   *   if it has less than n elements.
   */
  public Seq take(final int n) { return split(n)._1(); }

  /**
   * Drops the given number of elements from the head of this sequence if they are available.
   *
   * @param n The number of elements to drop from this sequence.
   * @return A sequence consisting of all elements of this sequence except the first n ones, or else the empty sequence,
   *   if this sequence has less than n elements.
   */
  public Seq drop(final int n) { return split(n)._2(); }

  private void checkBounds(final int i) { if (i < 0 || i >= length()) throw error("Index " + i + " is out of bounds."); }

    public  B foldLeft(final F2 f, final B z) {
        return ftree.foldLeft(f, z);
    }

    public  B foldRight(final F2 f, final B z) {
        return ftree.foldRight(f, z);
    }

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

    public  Seq map(F f) {
        return new Seq(ftree.map(f, Seq.elemMeasured()));
    }

}