Skip to content
Snippets Groups Projects
List.java 4.16 KiB
Newer Older
package inf226.util.immutable;

import inf226.util.Maybe;
import inf226.util.Mutable;
import java.util.function.Function;
import java.util.function.BiFunction;
import java.util.function.Consumer;

public final class List<T> {
   private final Maybe<ListItem<T> > items;
   public final Maybe<T> last;
   public final int length;
   
   private List() {
      this.items = Maybe.nothing();
      this.last = Maybe.nothing();
      this.length = 0;
   }
   private List(T head, List<T> tail) {
      this.items = new Maybe<ListItem<T>>(new ListItem<T>(head, tail));

      /* Construct a reference to the last element of
         the list. */
      T new_last;
      try {
         new_last = tail.last.get();
      } catch (Maybe.NothingException e) {
         new_last = head;
      }
      this.last = new Maybe<T>(new_last);
      this.length = tail.length + 1;
   }

   public static<U> List<U> empty() {
      return new List<U>();
   }
   public static<U> List<U> cons(U head, List<U> tail) {
      return new List<U>(head,tail);
   }
   
   public static<U> List<U> singleton(U head) {
      return new List<U>(head,empty());
   }
   public Maybe<T> head() {
      try {
         return new Maybe<T>(items.get().head);
      } catch (Maybe.NothingException e) {
         return Maybe.nothing();
      }
   }

   public Maybe< List<T> > tail() {
      try {
         return new Maybe<List<T> >(items.get().tail);
      } catch (Maybe.NothingException e) {
         return Maybe.nothing();
      }
   }

   public List<T> add(T element) {
      return cons(element, this);
   }

   public<U> List<U> map(Function<T,U> f) {
    List<U> result = empty();
    try {
       for(List<T> l = this.reverse(); ; l = l.tail().get()) {
          result = cons(f.apply(l.head().get()), result);
       }
    } catch (Maybe.NothingException e) {
       // No more elements
    }
    return result;
   }

   public<B,C> List<C> zipWith(List<B> other, BiFunction<T,B,C> f) {
    Builder<C> result = builder();
    try {
       List<T> l0 = this.reverse();
       List<B> l1 = other.reverse();
       while(true) {
           result.accept(f.apply(l0.items.get().head, l1.items.get().head));
           l0 = l0.items.get().tail;
           l1 = l1.items.get().tail; 
       }
    } catch (Maybe.NothingException e) {
       // No more elements
    }
    return result.getList();
   }

   @Override
   public final boolean equals(Object other) {
    if (other == null)
        return false;
    if (getClass() != other.getClass())
        return false;
    @SuppressWarnings("unchecked")
    final List<Object> list_other = (List<Object>) other;
    final Mutable<Boolean> equal = new Mutable<Boolean>(length == list_other.length);
    List<Boolean> equalList = zipWith(list_other, (a, b) ->  a.equals(b));
    equalList.forEach(e -> { equal.accept(equal.get() && e);});
    return equal.get();
   }

   public void forEach(Consumer<T> c) {
      sequenceConsumer(c).accept(this);
   }

   public static<U> Consumer<List<U>> sequenceConsumer(Consumer<U> c) {
    return new Consumer<List<U>>(){
         @Override
         public void accept(List<U> l) { 
            try {
               for(ListItem<U> e = l.items.get(); ; e = e.tail.items.get()) {
                  c.accept(e.head);
               }
            } catch (Maybe.NothingException e) {
               // No more elements
            }
        } };
   }

   public static class Builder<U> implements Consumer<U> {
      private List<U> list;
      public Builder() { list = empty(); }
      @Override
      public synchronized void accept(U element) { list = cons(element, list) ; }
      public List<U> getList() { return list ; }
   }

   public List<T> reverse() {
    List<T> result = empty();
    try {
       for(ListItem<T> e = this.items.get(); ; e = e.tail.items.get()) {
          result = cons(e.head, result);
       }
    } catch (Maybe.NothingException e) {
       // No more elements
    }
    return result;
   }

   public static<U> Builder<U> builder(){return new Builder<U>();}

   private static class ListItem<T> {
      public final T head;
      public final List<T> tail;
      ListItem(T head, List<T> tail) {
         this.head = head;
         this.tail = tail;
      }
   }
}