package com.codepoetics.protonpack;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* A sequence of values represented as nested cons pairs.
* @param <T> The type of the values in the sequence.
*/
public interface Seq<T> extends Streamable<T> {
/**
* Creates an empty sequence.
* @param <T> The type of the values in the sequence.
* @return The empty sequence.
*/
static <T> Seq<T> empty() {
return new EmptySeq<>();
}
/**
* Creates a sequence containing only one item.
* @param item The item in the sequence.
* @param <T> The type of the values in the sequence.
* @return The created sequence.
*/
static <T> Seq<T> singleton(T item) {
return new PairSeq<>(item, empty());
}
/**
* Creates a sequence containing zero or more items.
* @param items The items in the sequence.
* @param <T> The type of the items.
* @return The created sequence.
*/
static <T> Seq<T> of(T...items) {
return of(Stream.of(items)).reverse();
}
/**
* Creates a sequence from an ordered list
* @param list The list of items to put in the sequence.
* @param <T> The type of the items.
* @return The created sequence.
*/
static <T> Seq<T> of(List<T> list) {
return of(list.stream()).reverse();
}
/**
* Creates a sequence from a collection
* @param collection The collection of items to put in the sequence.
* @param <T> The type of the items.
* @return The created sequence.
*/
static <T> Seq<T> of(Collection<T> collection) {
return of(collection.stream());
}
/**
* Creates a sequence from a stream by consing each item onto an initially empty stream. Note that the resulting sequence will be in reverse order.
* @param stream The stream of values to put in the sequence.
* @param <T> The type of the items.
* @return The created sequence.
*/
static <T> Seq<T> of(Stream<T> stream) {
return stream.reduce(empty(), Seq::cons, Seq::append);
}
/**
* Returns the stream in reverse order.
* @return The reversed stream.
*/
default Seq<T> reverse() {
return of(stream());
}
/**
* Get the first item in the sequence.
* @return The first item in the sequence.
*/
T head();
/**
* Get the remaining items in the sequence.
* @return The remaining items in the sequence.
*/
Seq<T> tail();
/**
* Add an item to the start of the sequence.
* @param item The item to add to the sequence.
* @return The extended sequence.
*/
Seq<T> cons(T item);
/**
* Append a second sequence to this sequence.
* @param items The sequence to append to this sequence.
* @return The two sequences concatenated.
*/
default Seq<T> append(Seq<T> items) {
return concat(items).toSeq().reverse();
}
/**
* Test if the sequence is empty.
* @return True if the sequence is empty, false otherwise.
*/
boolean isEmpty();
/**
* Gets a stream of the items in the sequence.
* @return A stream of the items in the sequence.
*/
default Stream<T> get() {
return isEmpty()
? Stream.empty()
: StreamUtils.unfold(this, s -> s.tail().isEmpty() ? Optional.empty() : Optional.of(s.tail()))
.map(Seq::head);
}
@Override
default Seq<T> toSeq() {
return this;
}
}