add documentation

This commit is contained in:
Yadunand Prem 2022-11-03 00:25:46 +08:00
parent 66ed18d575
commit 0eb6cd0b30
4 changed files with 135 additions and 21 deletions

View File

@ -10,38 +10,44 @@ package cs2030s.fp;
public class Actually<T> implements Immutatorable<T> {
private T val;
private Exception err;
private Actually(T val, Exception err) {
this.val = val;
this.err = err;
}
public static <T> Actually<T> err() {
// A common error for ease of use
return new Actually<T>((T) null, new Exception("err"));
}
public static <T> Actually<T> err(Exception err) {
return new Actually<T>((T) null, err);
}
public static <T> Actually<T> ok(T val) {
return new Actually<T>(val, null);
}
public T except(Constant<? extends T> com) {
return this.err == null ? this.val : com.init();
}
public <U extends T> T unless(U val) {
return this.err == null ? this.val : val;
}
public void finish(Action<? super T> act) {
if (this.err == null) {
act.call(this.val);
}
}
@Override
public <R> Actually<R> transform(Immutator<? extends R, ? super T> f) {
return this.err == null ? Actually.<R>ok(f.invoke(this.val)) : Actually.<R>err(this.err);
}
public <R> Actually<? extends R> next(Immutator<? extends Actually<? extends R>, ? super T> f) {
if (this.err != null) {
return Actually.err(this.err);
@ -49,6 +55,7 @@ public class Actually<T> implements Immutatorable<T> {
return f.invoke(this.val);
}
}
public Actually<T> check(Immutator<Boolean, ? super T> pred) {
if (this.err != null) {
return Actually.err(this.err);
@ -59,7 +66,7 @@ public class Actually<T> implements Immutatorable<T> {
}
return Actually.err();
}
@Override
public String toString() {
if (this.err != null) {
@ -67,7 +74,7 @@ public class Actually<T> implements Immutatorable<T> {
}
return "<" + this.val + ">";
}
@Override
public boolean equals(Object obj) {
if (obj == this) {

View File

@ -1,7 +1,8 @@
package cs2030s.fp;
/**
* Represent a container that can transforms its content to produce another container containing the immutated element, possible of different types.
* Represent a container that can transforms its content to produce another
* container containing the immutated element, possible of different types.
* CS2030S Lab 5
* AY22/23 Semester 1
*
@ -11,7 +12,8 @@ public interface Immutatorable<T> {
/**
* The method to produce another container with immutated element.
*
* @param f The immutator.
* @param <R> The return value.
* @param f The immutator.
* @return A new container containing the immutated element.
*/
<R> Immutatorable<R> transform(Immutator<? extends R, ? super T> f);

View File

@ -4,57 +4,122 @@ import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* A list of infinite items.
*
* @author Yadunand Prem
* @version CS2030S AY 22/23 Sem 1
*/
public class InfiniteList<T> {
private Memo<Actually<T>> head;
private Memo<InfiniteList<T>> tail;
public static final End END = new End();
/**
* end() returns the END element.
*
* @param <T> The type of each element in the list. Does not matter as it's the
* end element
* @return the End Element
*/
public static <T> InfiniteList<T> end() {
@SuppressWarnings("unchecked")
InfiniteList<T> result = (InfiniteList<T>) END;
return result;
}
/**
* A private constructor for InfiniteList. Use generate / iterate to generate
*
* @param head The head element of the list, of type T
* @param tail The tail of the list, which is an infinite list
*/
private InfiniteList(Memo<Actually<T>> head, Memo<InfiniteList<T>> tail) {
this.head = head;
this.tail = tail;
}
// You may add other private constructor but it's not necessary.
/**
* Generates a list by calling prod.init()
*
* @param <T> The type of each element in the list
* @param prod The producer to generate a list of items for the infinite list
* @return The infiniteList with a generator for the tail
*/
public static <T> InfiniteList<T> generate(Constant<T> prod) {
Memo<Actually<T>> head = Memo.from(() -> Actually.ok(prod.init()));
Memo<InfiniteList<T>> tail = Memo.from(() -> generate(prod));
return new InfiniteList<>(head, tail);
}
/**
* Generates a list by calling the iterator with the seed value.
*
* @param <T> The type of each element in the list
* @param seed The initial value to be passed to the iterator
* @param func The function to generate the next value in the InfiniteList
* @return The infiniteList
*/
public static <T> InfiniteList<T> iterate(T seed, Immutator<T, T> func) {
Memo<Actually<T>> head = Memo.from(Actually.ok(seed));
Memo<InfiniteList<T>> tail = Memo.from(() -> iterate(func.invoke(seed), func));
return new InfiniteList<>(head, tail);
}
/**
* Gets the first valid value of the list.
*
* @return Returns of type T
*/
public T head() {
return this.head.get().except(() -> this.tail.get().head());
}
/**
* Returns the closest tail element of the list with a valid head.
*
* @return The InfiniteList tail
*/
public InfiniteList<T> tail() {
return this.head.get().transform(h -> this.tail.get()).except(() -> this.tail.get().tail());
}
/**
* Transforms the values from type T to type R lazily.
*
* @param <R> The type of each element in the returned list
* @param f The function used to map from type T to R
* @return The InfiniteList with elements of type R
*/
public <R> InfiniteList<R> map(Immutator<? extends R, ? super T> f) {
Memo<Actually<R>> head = Memo.from(() -> this.head.get().transform(f));
Memo<InfiniteList<R>> tail = Memo.from(() -> this.tail.get().map(f));
return new InfiniteList<R>(head, tail);
}
/**
* Filters the value in the infinite list based on the predicate provided
* lazily.
*
* @param pred The predicate to filter the values on
* @return The InfiniteList with filtered elements
*/
public InfiniteList<T> filter(Immutator<Boolean, ? super T> pred) {
Memo<Actually<T>> head = Memo.from(() -> this.head.get().check(pred));
Memo<InfiniteList<T>> tail = Memo.from(() -> this.tail.get().filter(pred));
return new InfiniteList<>(head, tail);
}
/**
* Limits the length of the InfiniteList, converting it to a finite
* InfiniteList.
*
* @param n The size to limit the list to
* @return The Finite infinite list of size n
*/
public InfiniteList<T> limit(long n) {
if (n <= 0) {
return InfiniteList.end();
@ -67,6 +132,12 @@ public class InfiniteList<T> {
return new InfiniteList<>(this.head, tail);
}
/**
* Takes values from the InfiniteList until predicate evaluates to false lazily.
*
* @param pred The predicate to decide whether to stop taking values
* @return The Maybe (if predicate is always true) InfiniteList of elements
*/
public InfiniteList<T> takeWhile(Immutator<Boolean, ? super T> pred) {
Memo<Actually<T>> head = Memo.from(() -> Actually.ok(this.head()).check(pred));
Memo<InfiniteList<T>> tail = Memo.from(() -> {
@ -79,6 +150,12 @@ public class InfiniteList<T> {
return new InfiniteList<>(head, tail);
}
/**
* Converts the InfiniteList to a List type.
* Warning: If list is not limited, this function will cause a stack overflow.
*
* @return The finite list of elements converted to a List
*/
public List<T> toList() {
return this.reduce(new ArrayList<>(), (acc, i) -> {
acc.add(i);
@ -86,25 +163,49 @@ public class InfiniteList<T> {
});
}
/**
* Reduces the elements to type U by calling acc on each element of the list,
* with the initial value of id.
* Warning: If list is not limited, this function will cause a stack overflow.
*
* @param <U> The return type of the function
* @param id The initial value to be passed to the accumulator
* @param acc The function used to reduce the values in the list. Initial value
* will be head of list and id
* @return Returns U by repeatedly calling acc on each element of the list
*/
public <U> U reduce(U id, Combiner<U, U, ? super T> acc) {
return this.head.get().transform((h) -> this.tail.get().reduce(acc.combine(id, this.head.get().unless(null)), acc))
return this.head.get()
.transform((h) -> this.tail.get()
.reduce(acc.combine(id, this.head.get().unless(null)), acc))
.except(() -> this.tail.get().reduce(id, acc));
}
/**
* Counts the number of elements in the list.
* Warning: If list is not limited, this function will cause a stack overflow.
*
* @return The size of the list
*/
public long count() {
return this.reduce(0L, (a, b) -> a + 1L);
}
/**
* Function to check if its the end of the list.
*
* @return returns true if its the end of the list
*/
public boolean isEnd() {
return false;
}
@Override
public String toString() {
return "[" + this.head + " " + this.tail + "]";
}
public boolean isEnd() {
return false;
}
private static class End extends InfiniteList<Object> {
private End() {
super(null, null);

View File

@ -8,44 +8,48 @@ package cs2030s.fp;
* @version CS2030S AY 22/23 Sem 1
*/
public class Memo<T> implements Immutatorable<T> {
private Constant<? extends T> com;
private Actually<T> val;
private Memo(Actually<T> val, Constant<T> com) {
this.com = com;
this.val = val;
}
public static <T> Memo<T> from(T val) {
return new Memo<T>(Actually.ok(val), null);
}
public static <T> Memo<T> from(Constant<T> com) {
return new Memo<T>(Actually.err(), com);
}
public T get() {
this.eval();
return this.val.unless(null);
}
private void eval() {
if (this.com != null) {
this.val = Actually.<T>ok(this.com.init());
this.com = null;
}
}
@Override
public <R> Memo<R> transform(Immutator<? extends R, ? super T> f) {
return Memo.<R>from(() -> f.invoke(this.get()));
}
public <R> Memo<R> next(Immutator<? extends Memo<? extends R>, ? super T> f) {
return Memo.<R>from(() -> f.invoke(this.get()).get());
}
public <S, R> Memo<R> combine(Memo<S> snd, Combiner<? extends R, ? super T, ? super S> f) {
return Memo.<R>from(() -> f.combine(this.get(), snd.get()));
}
@Override
public String toString() {
if (this.com != null) {
@ -53,7 +57,7 @@ public class Memo<T> implements Immutatorable<T> {
}
return this.get().toString();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {