package org.reactfx.util; import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Optional; import java.util.Spliterator; import java.util.function.BiFunction; import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * Immutable singly-linked list. */ public abstract class LL<T> implements Iterable<T> { private static class Nil<T> extends LL<T> { private static final Nil<?> INSTANCE = new Nil<Void>(); @SuppressWarnings("unchecked") static <T> Nil<T> instance() { return (Nil<T>) INSTANCE; } @Override public boolean isEmpty() { return true; } @Override public int size() { return 0; } @Override public T head() { throw new NoSuchElementException(); } @Override public LL<T> tail() { throw new NoSuchElementException(); } @Override public <U> LL<U> map(Function<? super T, ? extends U> f) { return instance(); } @Override public Iterator<T> iterator() { return Collections.emptyIterator(); } @Override public <R> R fold( R acc, BiFunction<? super R, ? super T, ? extends R> reduction) { return acc; } @Override public <R> Optional<R> mapReduce( Function<? super T, ? extends R> map, BinaryOperator<R> reduce) { return Optional.empty(); } } public static final class Cons<T> extends LL<T> { private final T head; private final LL<? extends T> tail; private final int size; private Cons(T head, LL<? extends T> tail) { this.head = head; this.tail = tail; this.size = 1 + tail.size(); } @Override public boolean isEmpty() { return false; } @Override public int size() { return size; } @Override public T head() { return head; } @Override public LL<? extends T> tail() { return tail; } @Override public <U> Cons<U> map(Function<? super T, ? extends U> f) { return cons(f.apply(head), tail.map(f)); } @Override public <R> R fold( R acc, BiFunction<? super R, ? super T, ? extends R> reduction) { return tail.fold(reduction.apply(acc, head), reduction); } @Override public final Iterator<T> iterator() { return new Iterator<T>() { private LL<? extends T> l = Cons.this; @Override public boolean hasNext() { return !l.isEmpty(); } @Override public T next() { T res = l.head(); l = l.tail(); return res; } }; } @Override public <R> Optional<R> mapReduce( Function<? super T, ? extends R> map, BinaryOperator<R> reduce) { return Optional.of(mapReduce1(map, reduce)); } public <R> R mapReduce1( Function<? super T, ? extends R> map, BinaryOperator<R> reduce) { R acc = map.apply(head); return tail.fold(acc, (r, t) -> reduce.apply(r, map.apply(t))); } } public static <T> LL<T> nil() { return Nil.instance(); } public static <T> Cons<T> cons(T head, LL<? extends T> tail) { return new Cons<>(head, tail); } @SafeVarargs public static <T> Cons<T> of(T head, T... tail) { return cons(head, of(tail, tail.length, LL.<T>nil())); } private static <T> LL<T> of(T[] elems, int to, LL<T> tail) { if(to == 0) { return tail; } else { return of(elems, to - 1, cons(elems[to-1], tail)); } } public static <T> LL<? extends T> concat(LL<? extends T> l1, LL<? extends T> l2) { if(l1.isEmpty()) { return l2; } else { return cons(l1.head(), concat(l1.tail(), l2)); } } // private constructor to prevent subclassing private LL() {} public abstract boolean isEmpty(); public abstract int size(); public abstract T head(); public abstract LL<? extends T> tail(); public abstract <U> LL<U> map(Function<? super T, ? extends U> f); public abstract <R> R fold(R acc, BiFunction<? super R, ? super T, ? extends R> reduction); public abstract <R> Optional<R> mapReduce( Function<? super T, ? extends R> map, BinaryOperator<R> reduce); public boolean all(Predicate<T> cond) { return fold(true, (b, t) -> b && cond.test(t)); } public <U> U mapFirst2(BiFunction<? super T, ? super T, ? extends U> f) { return f.apply(head(), tail().head()); } public <U> U mapFirst3(TriFunction<? super T, ? super T, ? super T, ? extends U> f) { return tail().mapFirst2(f.pApply(head())); } public <U> U mapFirst4(TetraFunction<? super T, ? super T, ? super T, ? super T, ? extends U> f) { return tail().mapFirst3(f.pApply(head())); } public <U> U mapFirst5(PentaFunction<? super T, ? super T, ? super T, ? super T, ? super T, ? extends U> f) { return tail().mapFirst4(f.pApply(head())); } public <U> U mapFirst6(HexaFunction<? super T, ? super T, ? super T, ? super T, ? super T, ? super T, ? extends U> f) { return tail().mapFirst5(f.pApply(head())); } @Override public String toString() { if(isEmpty()) { return "[]"; } else { StringBuilder sb = new StringBuilder(); sb.append("["); sb.append(head()); LL<? extends T> tail = tail(); while(!tail.isEmpty()) { sb.append(",").append(tail.head()); tail = tail.tail(); } sb.append("]"); return sb.toString(); } } public Stream<T> stream() { Spliterator<T> spliterator = new Spliterator<T>() { private final Iterator<T> iterator = iterator(); @Override public boolean tryAdvance(Consumer<? super T> action) { if(iterator.hasNext()) { action.accept(iterator.next()); return true; } else { return false; } } @Override public Spliterator<T> trySplit() { return null; } @Override public long estimateSize() { return size(); } @Override public int characteristics() { return Spliterator.IMMUTABLE | Spliterator.SIZED; } }; return StreamSupport.stream(spliterator, false); } }