package com.nurkiewicz.lazyseq; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Stream; /** * @author Tomasz Nurkiewicz * @since 5/6/13, 9:20 PM */ public abstract class LazySeq<E> extends AbstractList<E> { @SuppressWarnings("unchecked") public static <E> LazySeq<E> empty() { return Nil.instance(); } public abstract E head(); public Optional<E> headOption() { return Optional.of(head()); } public abstract LazySeq<E> tail(); public static <E> LazySeq<E> of(E element) { return cons(element, empty()); } public static <E> LazySeq<E> of(E element, Supplier<LazySeq<E>> tailFun) { return cons(element, tailFun); } public static <E> LazySeq<E> of(E element1, E element2) { return cons(element1, of(element2)); } public static <E> LazySeq<E> of(E element1, E element2, Supplier<LazySeq<E>> tailFun) { return cons(element1, of(element2, tailFun)); } public static <E> LazySeq<E> of(E element1, E element2, E element3) { return cons(element1, of(element2, element3)); } public static <E> LazySeq<E> of(E element1, E element2, E element3, Supplier<LazySeq<E>> tailFun) { return cons(element1, of(element2, element3, tailFun)); } @SafeVarargs public static <E> LazySeq<E> of(E... elements) { return of(Arrays.asList(elements).iterator()); } public static <E> LazySeq<E> of(Iterable<E> elements) { return of(elements.iterator()); } public static <E> LazySeq<E> of(Iterator<E> iterator) { if (iterator.hasNext()) { return cons(iterator.next(), () -> of(iterator)); } else { return empty(); } } public static <E> LazySeq<E> concat(Iterable<E> elements, Supplier<LazySeq<E>> tailFun) { return concat(elements.iterator(), tailFun); } public static <E> LazySeq<E> concat(Iterable<E> elements, LazySeq<E> tail) { return concat(elements.iterator(), tail); } public static <E> LazySeq<E> concat(Iterator<E> iterator, LazySeq<E> tail) { if (iterator.hasNext()) { return concatNonEmptyIterator(iterator, tail); } else { return tail; } } public static <E> LazySeq<E> concat(Iterator<E> iterator, Supplier<LazySeq<E>> tailFun) { if (iterator.hasNext()) { return concatNonEmptyIterator(iterator, tailFun); } else { return tailFun.get(); } } private static <E> LazySeq<E> concatNonEmptyIterator(Iterator<E> iterator, LazySeq<E> tail) { final E next = iterator.next(); if (iterator.hasNext()) { return cons(next, concatNonEmptyIterator(iterator, tail)); } else { return cons(next, tail); } } private static <E> LazySeq<E> concatNonEmptyIterator(Iterator<E> iterator, Supplier<LazySeq<E>> tailFun) { final E next = iterator.next(); if (iterator.hasNext()) { return cons(next, concatNonEmptyIterator(iterator, tailFun)); } else { return cons(next, tailFun); } } public static <E> LazySeq<E> cons(E head, Supplier<LazySeq<E>> tailFun) { return new Cons<>(head, tailFun); } public static <E> LazySeq<E> cons(E head, LazySeq<E> tail) { return new FixedCons<>(head, tail); } public static <E> LazySeq<E> iterate(E initial, Function<E, E> fun) { return new Cons<>(initial, () -> iterate(fun.apply(initial), fun)); } public static <E> Collector<E, LazySeq<E>, LazySeq<E>> toLazySeq() { return DummyLazySeqCollector.getInstance(); } public static <E> LazySeq<E> tabulate(int start, Function<Integer, E> generator) { return cons(generator.apply(start), () -> tabulate(start + 1, generator)); } public static <E> LazySeq<E> continually(Supplier<E> generator) { return cons(generator.get(), () -> continually(generator)); } public static <E> LazySeq<E> continually(Iterable<E> cycle) { if (!cycle.iterator().hasNext()) { return empty(); } return continuallyUnsafe(cycle); } private static <E> LazySeq<E> continuallyUnsafe(Iterable<E> cycle) { return concat(cycle, () -> continually(cycle)); } public static <E> LazySeq<E> continually(E value) { return cons(value, () -> continually(value)); } public static LazySeq<Integer> numbers(int start) { return numbers(start, 1); } public static LazySeq<Integer> numbers(int start, int step) { return cons(start, () -> numbers(start + step, step)); } public static LazySeq<Double> numbers(double start) { return numbers(start, 1.0); } public static LazySeq<Double> numbers(double start, double step) { return cons(start, () -> numbers(start + step, step)); } protected abstract boolean isTailDefined(); @Override public E get(final int index) { if (index < 0) { throw new IndexOutOfBoundsException(Integer.toString(index)); } LazySeq<E> cur = this; for (int curIdx = index; curIdx > 0; --curIdx) { if (cur.tail().isEmpty()) { throw new IndexOutOfBoundsException(Integer.toString(index)); } cur = cur.tail(); } return cur.head(); } public abstract <R> LazySeq<R> map(Function<? super E, ? extends R> mapper); @Override public Stream<E> stream() { return new LazySeqStream<>(this); } @Override public Stream<E> parallelStream() { return stream(); } @Override public String toString() { return mkString("[", ", ", "]", true); } /** * Returns a string representation of this {@link LazySeq} by concatenating the elements * using the separator <code>sep</code>. */ public String mkString(String sep) { return mkString("", sep, ""); } /** * Returns a string representation of this {@link LazySeq}. * The string result starts with the string <code>start</code> and ends with <code>end</code>, * whereas <code>sep</code> is as separator string between the elements of this sequence. */ public String mkString(String start, String sep, String end) { return mkString(start, sep, end, false); } /** * Returns a string representation of this {@link LazySeq}. * The string result starts with the string <code>start</code> and ends with <code>end</code>, * whereas <code>sep</code> is as separator string between the elements of this sequence. * If <code>lazy</code> is <code>true</code> the evaluation stops at the first unspecific element. */ public String mkString(String start, String sep, String end, boolean lazy) { final StringBuilder s = new StringBuilder(start); LazySeq<E> cur = this; while (!cur.isEmpty()) { s.append(cur.head()); if (!lazy || cur.isTailDefined()) { if (!cur.tail().isEmpty()) { s.append(sep); } cur = cur.tail(); } else { s.append(sep).append("?"); break; } } return s.append(end).toString(); } public abstract LazySeq<E> filter(Predicate<? super E> predicate); public abstract <R> LazySeq<R> flatMap(Function<? super E, ? extends Iterable<? extends R>> mapper); public LazySeq<E> limit(long maxSize) { return take(maxSize); } /** * Converts this {@link LazySeq} to immutable {@link List}. * <p/> * Notice that this method will eventually fail at runtime when called on infinite sequence. * * @return {@link List} of all elements in this lazy sequence. */ public List<E> toList() { return Collections.unmodifiableList(this.force()); } public LazySeq<E> take(long maxSize) { if (maxSize < 0) { throw new IllegalArgumentException(Long.toString(maxSize)); } if (maxSize == 0) { return empty(); } return takeUnsafe(maxSize); } protected abstract LazySeq<E> takeUnsafe(long maxSize); public LazySeq<E> drop(long startInclusive) { if (startInclusive < 0) { throw new IllegalArgumentException(Long.toString(startInclusive)); } return dropUnsafe(startInclusive); } protected LazySeq<E> dropUnsafe(long startInclusive) { if (startInclusive > 0) { return tail().drop(startInclusive - 1); } else { return this; } } @Override public LazySeq<E> subList(int fromIndex, int toIndex) { return slice(fromIndex, toIndex); } public LazySeq<E> slice(long startInclusive, long endExclusive) { if (startInclusive < 0 || startInclusive > endExclusive) { throw new IllegalArgumentException("slice(" + startInclusive + ", " + endExclusive + ")"); } return dropUnsafe(startInclusive).takeUnsafe(endExclusive - startInclusive); } public void forEach(Consumer<? super E> action) { action.accept(head()); tail().forEach(action); } public Optional<E> reduce(BinaryOperator<E> accumulator) { if (isEmpty() || tail().isEmpty()) { return Optional.empty(); } return Optional.of(tail().reduce(head(), accumulator)); } public <U> U reduce(U identity, BiFunction<U, ? super E, U> accumulator) { U result = identity; LazySeq<E> cur = this; while (!cur.isEmpty()) { result = accumulator.apply(result, cur.head()); cur = cur.tail(); } return result; } public <C extends Comparable<? super C>> Optional<E> maxBy(Function<E, C> propertyFun) { return max(propertyFunToComparator(propertyFun)); } public Optional<E> max(Comparator<? super E> comparator) { return greatestByComparator(comparator); } public <C extends Comparable<? super C>> Optional<E> minBy(Function<E, C> propertyFun) { return min(propertyFunToComparator(propertyFun)); } public Optional<E> min(Comparator<? super E> comparator) { return greatestByComparator(comparator.reversed()); } private <C extends Comparable<? super C>> Comparator<? super E> propertyFunToComparator(Function<E, C> propertyFun) { return (a, b) -> { final C aProperty = propertyFun.apply(a); final C bProperty = propertyFun.apply(b); return aProperty.compareTo(bProperty); }; } private Optional<E> greatestByComparator(Comparator<? super E> comparator) { if (tail().isEmpty()) { return Optional.of(head()); } E minSoFar = head(); LazySeq<E> cur = this.tail(); while (!cur.isEmpty()) { minSoFar = maxByComparator(minSoFar, cur.head(), comparator); cur = cur.tail(); } return Optional.of(minSoFar); } private static <E> E maxByComparator(E first, E second, Comparator<? super E> comparator) { return comparator.compare(first, second) >= 0 ? first : second; } @Override public int size() { return 1 + tail().size(); } @Override public Iterator<E> iterator() { return new LazySeqIterator<>(this); } public boolean anyMatch(Predicate<? super E> predicate) { return predicate.test(head()) || tail().anyMatch(predicate); } public boolean allMatch(Predicate<? super E> predicate) { return predicate.test(head()) && tail().allMatch(predicate); } public boolean noneMatch(Predicate<? super E> predicate) { return allMatch(predicate.negate()); } public <S, R> LazySeq<R> zip(LazySeq<? extends S> second, BiFunction<? super E, ? super S, ? extends R> zipper) { if (second.isEmpty()) { return empty(); } else { final R headsZipped = zipper.apply(head(), second.head()); return cons(headsZipped, () -> tail().zip(second.tail(), zipper)); } } public LazySeq<E> takeWhile(Predicate<? super E> predicate) { if (predicate.test(head())) { return cons(head(), () -> tail().takeWhile(predicate)); } else { return empty(); } } public LazySeq<E> dropWhile(Predicate<? super E> predicate) { if (predicate.test(head())) { return tail().dropWhile(predicate); } else { return this; } } public LazySeq<List<E>> sliding(int size) { if (size <= 0) { throw new IllegalArgumentException(Integer.toString(size)); } return slidingUnsafe(size); } protected LazySeq<List<E>> slidingUnsafe(int size) { final List<E> window = take(size).toList(); return cons(window, () -> tail().slidingFullOnly(size)); } protected LazySeq<List<E>> slidingFullOnly(int size) { final List<E> window = take(size).toList(); if (window.size() < size) { return empty(); } else { return cons(window, () -> tail().slidingFullOnly(size)); } } public LazySeq<List<E>> grouped(int size) { if (size <= 0) { throw new IllegalArgumentException(Integer.toString(size)); } return groupedUnsafe(size); } protected LazySeq<List<E>> groupedUnsafe(int size) { final List<E> window = take(size).toList(); return cons(window, () -> drop(size).groupedUnsafe(size)); } public LazySeq<E> scan(E initial, BinaryOperator<E> fun) { return cons(initial, () -> tail().scan(fun.apply(initial, head()), fun)); } public LazySeq<E> distinct() { return filterOutSeen(new HashSet<>()); } private LazySeq<E> filterOutSeen(Set<E> exclude) { final LazySeq<E> moreDistinct = filter(e -> !exclude.contains(e)); if (moreDistinct.isEmpty()) { return empty(); } final E next = moreDistinct.head(); exclude.add(next); return cons(next, () -> moreDistinct.tail().filterOutSeen(exclude)); } @SuppressWarnings("unchecked") public LazySeq<E> sorted() { return sorted((o1, o2) -> ((Comparable<E>) o1).compareTo(o2)); } public LazySeq<E> sorted(Comparator<? super E> comparator) { final ArrayList<E> list = new ArrayList<>(this); list.sort(comparator); return of(list); } public boolean startsWith(Iterable<E> prefix) { return startsWith(prefix.iterator()); } public boolean startsWith(Iterator<E> iterator) { return !iterator.hasNext() || head().equals(iterator.next()) && tail().startsWith(iterator); } public LazySeq<E> force() { tail().force(); return this; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof LazySeq)) return false; LazySeq right = (LazySeq) o; return !right.isEmpty() && head().equals(right.head()) && tail().equals(right.tail()); } @Override public int hashCode() { return head().hashCode() + tail().hashCode() * 31; } }