summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/bjc/data/ArrayIterator.java3
-rw-r--r--src/main/java/bjc/data/BooleanToggle.java17
-rw-r--r--src/main/java/bjc/data/Context.java62
-rw-r--r--src/main/java/bjc/data/Contexts.java103
-rw-r--r--src/main/java/bjc/data/Either.java241
-rw-r--r--src/main/java/bjc/data/GeneratingIterator.java13
-rw-r--r--src/main/java/bjc/data/Holder.java (renamed from src/main/java/bjc/data/IHolder.java)37
-rw-r--r--src/main/java/bjc/data/IPair.java249
-rw-r--r--src/main/java/bjc/data/ITree.java287
-rw-r--r--src/main/java/bjc/data/Identity.java35
-rw-r--r--src/main/java/bjc/data/IntHolder.java72
-rw-r--r--src/main/java/bjc/data/Lazy.java96
-rw-r--r--src/main/java/bjc/data/LazyPair.java88
-rw-r--r--src/main/java/bjc/data/ListHolder.java37
-rw-r--r--src/main/java/bjc/data/NonCMEIterator.java54
-rw-r--r--src/main/java/bjc/data/NonCMEListIterator.java68
-rw-r--r--src/main/java/bjc/data/OneWayToggle.java10
-rw-r--r--src/main/java/bjc/data/Option.java51
-rw-r--r--src/main/java/bjc/data/Pair.java315
-rw-r--r--src/main/java/bjc/data/QueuedIterator.java37
-rw-r--r--src/main/java/bjc/data/ResettableIterator.java7
-rw-r--r--src/main/java/bjc/data/ReverseListIterator.java72
-rw-r--r--src/main/java/bjc/data/SimplePair.java154
-rw-r--r--src/main/java/bjc/data/SimpleTree.java446
-rw-r--r--src/main/java/bjc/data/TopDownTransformIterator.java66
-rw-r--r--src/main/java/bjc/data/Tree.java604
-rw-r--r--src/main/java/bjc/data/ValueToggle.java14
-rw-r--r--src/main/java/bjc/data/internals/BoundLazy.java38
-rw-r--r--src/main/java/bjc/data/internals/BoundLazyPair.java40
-rw-r--r--src/main/java/bjc/data/internals/BoundListHolder.java24
-rw-r--r--src/main/java/bjc/data/internals/HalfBoundLazyPair.java40
-rw-r--r--src/main/java/bjc/data/internals/WrappedLazy.java24
-rw-r--r--src/main/java/bjc/data/internals/WrappedOption.java24
-rw-r--r--src/main/java/bjc/esodata/AbbrevMap2.java15
-rw-r--r--src/main/java/bjc/esodata/DefaultList.java6
-rw-r--r--src/main/java/bjc/esodata/Directory.java3
-rw-r--r--src/main/java/bjc/esodata/DoubleTape.java29
-rw-r--r--src/main/java/bjc/esodata/FlatNestIterator.java103
-rw-r--r--src/main/java/bjc/esodata/MapSet.java9
-rw-r--r--src/main/java/bjc/esodata/MinMaxList.java238
-rw-r--r--src/main/java/bjc/esodata/Multimap.java14
-rw-r--r--src/main/java/bjc/esodata/NestList.java389
-rw-r--r--src/main/java/bjc/esodata/PushdownMap.java135
-rw-r--r--src/main/java/bjc/esodata/QueueStack.java21
-rw-r--r--src/main/java/bjc/esodata/SimpleDirectory.java31
-rw-r--r--src/main/java/bjc/esodata/SimpleStack.java21
-rw-r--r--src/main/java/bjc/esodata/SingleTape.java63
-rw-r--r--src/main/java/bjc/esodata/SpaghettiStack.java27
-rw-r--r--src/main/java/bjc/esodata/Stack.java36
-rw-r--r--src/main/java/bjc/esodata/TapeLibrary.java163
-rw-r--r--src/main/java/bjc/esodata/TapeView.java102
-rw-r--r--src/main/java/bjc/esodata/ThresholdSet.java30
-rw-r--r--src/main/java/bjc/esodata/UnifiedDirectory.java31
-rw-r--r--src/main/java/bjc/funcdata/ExtendedMap.java157
-rw-r--r--src/main/java/bjc/funcdata/Freezable.java73
-rw-r--r--src/main/java/bjc/funcdata/FunctionalList.java62
-rw-r--r--src/main/java/bjc/funcdata/FunctionalMap.java149
-rw-r--r--src/main/java/bjc/funcdata/FunctionalStringTokenizer.java6
-rw-r--r--src/main/java/bjc/funcdata/ListEx.java (renamed from src/main/java/bjc/funcdata/IList.java)22
-rw-r--r--src/main/java/bjc/funcdata/MapEx.java (renamed from src/main/java/bjc/funcdata/IMap.java)146
-rw-r--r--src/main/java/bjc/funcdata/ObjectFrozen.java47
-rw-r--r--src/main/java/bjc/funcdata/TransformedValueMap.java87
-rw-r--r--src/main/java/bjc/funcdata/bst/BinarySearchTree.java21
-rw-r--r--src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java2
-rw-r--r--src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java8
-rw-r--r--src/main/java/bjc/funcdata/bst/TreePart.java (renamed from src/main/java/bjc/funcdata/bst/ITreePart.java)2
-rw-r--r--src/main/java/bjc/functypes/ArrayChooser.java20
-rw-r--r--src/main/java/bjc/functypes/Combinators.java297
-rw-r--r--src/main/java/bjc/functypes/Fixpoints.java58
-rw-r--r--src/main/java/bjc/functypes/FunctionalIsomorphism.java50
-rw-r--r--src/main/java/bjc/functypes/ID.java2
-rw-r--r--src/main/java/bjc/functypes/IntArrayChooser.java19
-rw-r--r--src/main/java/bjc/functypes/Isomorphism.java54
-rw-r--r--src/main/java/bjc/functypes/ListFlattener.java4
-rw-r--r--src/main/java/bjc/functypes/ThrowFunction.java184
-rw-r--r--src/main/java/bjc/functypes/Thrower.java165
76 files changed, 4435 insertions, 2094 deletions
diff --git a/src/main/java/bjc/data/ArrayIterator.java b/src/main/java/bjc/data/ArrayIterator.java
index 6d11a1d..c5f68cc 100644
--- a/src/main/java/bjc/data/ArrayIterator.java
+++ b/src/main/java/bjc/data/ArrayIterator.java
@@ -34,8 +34,7 @@ public class ArrayIterator<T> implements Iterator<T> {
@SuppressWarnings("unchecked")
@Override
public T next() {
- if (idx >= arr.length)
- return null;
+ if (idx >= arr.length) return null;
return (T) (arr[idx++]);
}
diff --git a/src/main/java/bjc/data/BooleanToggle.java b/src/main/java/bjc/data/BooleanToggle.java
index af083f5..e0286d4 100644
--- a/src/main/java/bjc/data/BooleanToggle.java
+++ b/src/main/java/bjc/data/BooleanToggle.java
@@ -59,23 +59,18 @@ public class BooleanToggle implements Toggle<Boolean> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof BooleanToggle))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof BooleanToggle)) return false;
final BooleanToggle other = (BooleanToggle) obj;
- if (val != other.val)
- return false;
-
- return true;
+ if (val != other.val) return false;
+ else return true;
}
@Override
public String toString() {
- return String.format("BooleanToggle [val=%s]", val);
+ return String.format("%s", val);
}
}
diff --git a/src/main/java/bjc/data/Context.java b/src/main/java/bjc/data/Context.java
new file mode 100644
index 0000000..7efdc4d
--- /dev/null
+++ b/src/main/java/bjc/data/Context.java
@@ -0,0 +1,62 @@
+package bjc.data;
+
+/**
+ * Represents a 'context' which is a hierarchical set of objects.
+ * @author Ben Culkin
+ *
+ */
+public interface Context {
+ /**
+ * Register an object with this context.
+ *
+ * @param name The name of the object.
+ * @param o The object to register.
+ */
+ void register(String name, Object o);
+
+ /**
+ * Get the parent of this context.
+ *
+ * @return The parent of this context.
+ */
+ Context getParent();
+
+ /**
+ * Get an object from this context.
+ *
+ * @param name The name of the object.
+ *
+ * @return The object bound to that name.
+ */
+ Object get(String name);
+
+ /**
+ * Get an object which is an instance of the provided class or a subclass
+ * thereof.
+ *
+ * @param <T> The type of the object.
+ *
+ * @param contract The class of the object.
+ *
+ * @return An instance of the provided class.
+ */
+ <T> T get(Class<T> contract);
+
+ /**
+ * Get a named object which is an instance of the provided class or a subclass
+ * thereof.
+ *
+ * @param <T> The type of the object.
+ *
+ * @param name The name of the object
+ * @param contract The class of the object.
+ *
+ * @return An instance of the provided class, with the given name..
+ */
+ default <T> T get(String name, Class<T> contract) {
+ Object obj = get(name);
+ return obj == null
+ ? getParent().get(name, contract)
+ : contract.cast(obj);
+ };
+}
diff --git a/src/main/java/bjc/data/Contexts.java b/src/main/java/bjc/data/Contexts.java
new file mode 100644
index 0000000..b028ad1
--- /dev/null
+++ b/src/main/java/bjc/data/Contexts.java
@@ -0,0 +1,103 @@
+package bjc.data;
+
+import java.util.*;
+
+/**
+ * Utility methods for dealing with contexts.
+ *
+ * @author Ben Culkin
+ *
+ */
+public class Contexts {
+ /**
+ * The null context, which always throws an exception.
+ */
+ public static final Context NULL = new NullContextImpl();
+
+ private Contexts() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Create a new context with no parent.
+ *
+ * @return A context with no parent.
+ */
+ public static Context create() {
+ return new ContextImpl(NULL);
+ }
+
+ /**
+ * Create a context with the specified parent.
+ *
+ * @param parent The parent of this context.
+ *
+ * @return A context with the given context as its parent.
+ */
+ public static Context create(Context parent) {
+ return new ContextImpl(parent);
+ }
+
+ private static class NullContextImpl implements Context {
+ @Override
+ public Context getParent() {
+ return this;
+ }
+
+ @Override
+ public void register(String name, Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object get(String name) {
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public <T> T get(Class<T> contract) {
+ throw new NoSuchElementException();
+ }
+ }
+
+ private static class ContextImpl implements Context {
+
+ private final Context parent;
+
+ private final Map<String, Object> objects;
+
+ public ContextImpl(Context parent) {
+ this.parent = parent;
+ this.objects = new HashMap<>();
+ }
+
+ @Override
+ public void register(String name, Object o) {
+ objects.put(name, o);
+ }
+
+ @Override
+ public Object get(String name) {
+ if (objects.containsKey(name)) {
+ return objects.get(name);
+ }
+ return parent.get(name);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T get(Class<T> contract) {
+ for (Object o : objects.values()) {
+ if (contract.isInstance(o)) {
+ return (T) o;
+ }
+ }
+ return parent.get(contract);
+ }
+
+ @Override
+ public Context getParent() {
+ return parent;
+ }
+ }
+}
diff --git a/src/main/java/bjc/data/Either.java b/src/main/java/bjc/data/Either.java
index 55518d8..75447f2 100644
--- a/src/main/java/bjc/data/Either.java
+++ b/src/main/java/bjc/data/Either.java
@@ -1,7 +1,7 @@
package bjc.data;
-import java.util.function.BiFunction;
-import java.util.function.Function;
+import java.util.*;
+import java.util.function.*;
/**
* Represents a pair where only one side has a value.
@@ -15,7 +15,7 @@ import java.util.function.Function;
* The type that could be on the right.
*
*/
-public class Either<LeftType, RightType> implements IPair<LeftType, RightType> {
+public class Either<LeftType, RightType> {
/**
* Create a new either with the left value occupied.
*
@@ -72,141 +72,138 @@ public class Either<LeftType, RightType> implements IPair<LeftType, RightType> {
}
}
- @Override
- public <BoundLeft, BoundRight> IPair<BoundLeft, BoundRight> bind(
- final BiFunction<LeftType, RightType, IPair<BoundLeft, BoundRight>> binder) {
- if (binder == null)
- throw new NullPointerException("Binder must not be null");
-
- return binder.apply(leftVal, rightVal);
+ /**
+ * Perform a mapping over this either.
+ *
+ * @param <NewLeft> The new left type.
+ * @param <NewRight> The new right type.
+ *
+ * @param leftFunc The function to apply if this is a left either.
+ * @param rightFunc The function to apply if this is a right either.
+ *
+ * @return A new either, containing a value transformed by the appropriate function.
+ */
+ public <NewLeft, NewRight> Either<NewLeft, NewRight> map(
+ Function<LeftType, NewLeft> leftFunc,
+ Function<RightType, NewRight> rightFunc)
+ {
+ if (isLeft) return left(leftFunc.apply(leftVal));
+ else return right(rightFunc.apply(rightVal));
}
-
- @Override
- public <BoundLeft> IPair<BoundLeft, RightType>
- bindLeft(final Function<LeftType, IPair<BoundLeft, RightType>> leftBinder) {
- if (leftBinder == null)
- throw new NullPointerException("Left binder must not be null");
-
- if (isLeft)
- return leftBinder.apply(leftVal);
-
- return new Either<>(null, rightVal);
+
+ /**
+ * Extract the value from this Either.
+ *
+ * @param <Common> The common type to extract.
+ *
+ * @param leftHandler The function to handle left-values.
+ * @param rightHandler The function to handle right-values.
+ *
+ * @return The result of applying the proper function.
+ */
+ public <Common> Common extract(
+ Function<LeftType, Common> leftHandler,
+ Function<RightType, Common> rightHandler)
+ {
+ if (isLeft) return leftHandler.apply(leftVal);
+ else return rightHandler.apply(rightVal);
}
-
- @Override
- public <BoundRight> IPair<LeftType, BoundRight> bindRight(
- final Function<RightType, IPair<LeftType, BoundRight>> rightBinder) {
- if (rightBinder == null)
- throw new NullPointerException("Right binder must not be null");
-
- if (isLeft)
- return new Either<>(leftVal, null);
-
- return rightBinder.apply(rightVal);
+
+ /**
+ * Perform an action on this either.
+ *
+ * @param leftHandler The handler of left values.
+ * @param rightHandler The handler of right values.
+ */
+ public void pick(
+ Consumer<LeftType> leftHandler, Consumer<RightType> rightHandler)
+ {
+ if (isLeft) leftHandler.accept(leftVal);
+ else rightHandler.accept(rightVal);
}
- @Override
- public <OtherLeft, OtherRight, CombinedLeft, CombinedRight>
- IPair<CombinedLeft, CombinedRight>
- combine(final IPair<OtherLeft, OtherRight> otherPair,
- final BiFunction<LeftType, OtherLeft, CombinedLeft> leftCombiner,
- final BiFunction<RightType, OtherRight,
- CombinedRight> rightCombiner) {
- if (otherPair == null) {
- throw new NullPointerException("Other pair must not be null");
- } else if (leftCombiner == null) {
- throw new NullPointerException("Left combiner must not be null");
- } else if (rightCombiner == null) {
- throw new NullPointerException("Right combiner must not be null");
- }
-
- if (isLeft) {
- return otherPair.bind((otherLeft, otherRight) -> {
- CombinedLeft cLeft = leftCombiner.apply(leftVal, otherLeft);
-
- return new Either<>(cLeft, null);
- });
- }
-
- return otherPair.bind((otherLeft, otherRight) -> {
- CombinedRight cRight = rightCombiner.apply(rightVal, otherRight);
-
- return new Either<>(null, cRight);
- });
+ /**
+ * Check if this either is left-aligned (has the left value filled,
+ * not the right value).
+ *
+ * @return Whether this either is left-aligned.
+ */
+ public boolean isLeft() {
+ return isLeft;
}
- @Override
- public <NewLeft> IPair<NewLeft, RightType>
- mapLeft(final Function<LeftType, NewLeft> mapper) {
- if (mapper == null)
- throw new NullPointerException("Mapper must not be null");
-
- if (isLeft)
- return new Either<>(mapper.apply(leftVal), null);
-
- return new Either<>(null, rightVal);
+ /**
+ * Get the left value of this either if there is one.
+ *
+ * @return An optional containing the left value, if there is one.
+ */
+ public Optional<LeftType> getLeft() {
+ return Optional.ofNullable(leftVal);
}
-
- @Override
- public <NewRight> IPair<LeftType, NewRight>
- mapRight(final Function<RightType, NewRight> mapper) {
- if (mapper == null)
- throw new NullPointerException("Mapper must not be null");
-
+
+ /**
+ * Get the left value of this either, or get a {@link NoSuchElementException}
+ * if there isn't one.
+ *
+ * @return The left value of this either.
+ *
+ * @throws NoSuchElementException If this either doesn't have a left value.
+ */
+ public LeftType forceLeft() {
if (isLeft)
- return new Either<>(leftVal, null);
-
- return new Either<>(null, mapper.apply(rightVal));
+ {
+ return leftVal;
+ } else
+ {
+ throw new NoSuchElementException("Either has no left value, is right value");
+ }
}
- @Override
- public <MergedType> MergedType
- merge(final BiFunction<LeftType, RightType, MergedType> merger) {
- if (merger == null)
- throw new NullPointerException("Merger must not be null");
-
- return merger.apply(leftVal, rightVal);
+ /**
+ * Get the right value of this either if there is one.
+ *
+ * @return An optional containing the right value, if there is one.
+ */
+ public Optional<RightType> getRight() {
+ return Optional.ofNullable(rightVal);
}
-
+
+ /**
+ * Get the right value of this either, or get a {@link NoSuchElementException}
+ * if there isn't one.
+ *
+ * @return The right value of this either.
+ *
+ * @throws NoSuchElementException If this either doesn't have a right value.
+ */
+ public RightType forceRight() {
+ if (isLeft)
+ {
+ throw new NoSuchElementException("Either has no right value, has left value");
+ } else
+ {
+ return rightVal;
+ }
+ }
+
+ // Misc. overrides
+
@Override
public int hashCode() {
- final int prime = 31;
-
- int result = 1;
- result = prime * result + (isLeft ? 1231 : 1237);
- result = prime * result + (leftVal == null ? 0 : leftVal.hashCode());
- result = prime * result + (rightVal == null ? 0 : rightVal.hashCode());
-
- return result;
+ return Objects.hash(isLeft, leftVal, rightVal);
}
@Override
- public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof Either<?, ?>))
- return false;
-
- final Either<?, ?> other = (Either<?, ?>) obj;
-
- if (isLeft != other.isLeft)
- return false;
-
- if (leftVal == null) {
- if (other.leftVal != null)
- return false;
- } else if (!leftVal.equals(other.leftVal))
- return false;
-
- if (rightVal == null) {
- if (other.rightVal != null)
- return false;
- } else if (!rightVal.equals(other.rightVal))
- return false;
-
- return true;
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ Either<?, ?> other = (Either<?, ?>) obj;
+
+ return isLeft == other.isLeft
+ && Objects.equals(leftVal, other.leftVal)
+ && Objects.equals(rightVal, other.rightVal);
}
@Override
diff --git a/src/main/java/bjc/data/GeneratingIterator.java b/src/main/java/bjc/data/GeneratingIterator.java
index ffa92cf..f926833 100644
--- a/src/main/java/bjc/data/GeneratingIterator.java
+++ b/src/main/java/bjc/data/GeneratingIterator.java
@@ -58,4 +58,17 @@ public class GeneratingIterator<E> implements Iterator<E> {
return state;
}
+
+ /**
+ * Sets the state of this iterator.
+ *
+ * @param newState The new state value.
+ *
+ * @return The old state value.
+ */
+ public E setState(E newState) {
+ E oldState = this.state;
+ this.state = newState;
+ return oldState;
+ }
}
diff --git a/src/main/java/bjc/data/IHolder.java b/src/main/java/bjc/data/Holder.java
index faa3f64..f01812e 100644
--- a/src/main/java/bjc/data/IHolder.java
+++ b/src/main/java/bjc/data/Holder.java
@@ -18,7 +18,7 @@ import bjc.funcdata.theory.Functor;
* @param <ContainedType>
* The type of value held.
*/
-public interface IHolder<ContainedType> extends Functor<ContainedType> {
+public interface Holder<ContainedType> extends Functor<ContainedType> {
/**
* Bind a function across the value in this container.
*
@@ -30,8 +30,8 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*
* @return A holder from binding the value.
*/
- public <BoundType> IHolder<BoundType>
- bind(Function<ContainedType, IHolder<BoundType>> binder);
+ public <BoundType> Holder<BoundType>
+ bind(Function<ContainedType, Holder<BoundType>> binder);
/**
* Apply an action to the value.
@@ -51,14 +51,14 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
default <ArgType, ReturnType> Function<Functor<ArgType>, Functor<ReturnType>>
fmap(final Function<ArgType, ReturnType> func) {
return argumentFunctor -> {
- if (!(argumentFunctor instanceof IHolder<?>)) {
+ if (!(argumentFunctor instanceof Holder<?>)) {
final String msg
= "This functor only supports mapping over instances of IHolder";
throw new IllegalArgumentException(msg);
}
- final IHolder<ArgType> holder = (IHolder<ArgType>) argumentFunctor;
+ final Holder<ArgType> holder = (Holder<ArgType>) argumentFunctor;
return holder.map(func);
};
@@ -80,7 +80,7 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*
* @return The function lifted over the holder.
*/
- public <NewType> Function<ContainedType, IHolder<NewType>>
+ public <NewType> Function<ContainedType, Holder<NewType>>
lift(Function<ContainedType, NewType> func);
/**
@@ -88,7 +88,7 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*
* @return A lazy version of this holder.
*/
- public default IHolder<ContainedType> makeLazy() {
+ public default Holder<ContainedType> makeLazy() {
return new WrappedLazy<>(this);
}
@@ -97,7 +97,7 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*
* @return A list version of this holder.
*/
- public default IHolder<ContainedType> makeList() {
+ public default Holder<ContainedType> makeList() {
return new BoundListHolder<>(new FunctionalList<>(this));
}
@@ -106,7 +106,7 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*
* @return An optional version of this holder.
*/
- public default IHolder<ContainedType> makeOptional() {
+ public default Holder<ContainedType> makeOptional() {
return new WrappedOption<>(this);
}
@@ -123,7 +123,7 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*
* @return A holder with the mapped value
*/
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(Function<ContainedType, MappedType> mapper);
/**
@@ -134,7 +134,7 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*
* @return The holder itself.
*/
- public default IHolder<ContainedType> replace(final ContainedType newValue) {
+ public default Holder<ContainedType> replace(final ContainedType newValue) {
return transform(oldValue -> newValue);
}
@@ -146,7 +146,7 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*
* @return The holder itself, for easy chaining.
*/
- public IHolder<ContainedType> transform(UnaryOperator<ContainedType> transformer);
+ public Holder<ContainedType> transform(UnaryOperator<ContainedType> transformer);
/**
* Unwrap the value contained in this holder so that it is no longer held.
@@ -161,4 +161,17 @@ public interface IHolder<ContainedType> extends Functor<ContainedType> {
*/
public <UnwrappedType> UnwrappedType
unwrap(Function<ContainedType, UnwrappedType> unwrapper);
+
+ /**
+ * Create an instace of IHolder containing a single value.
+ *
+ * @param <ElementType> The type of the value contained.
+ *
+ * @param contained The value to contain.
+ *
+ * @return An instance of IHolder containing that value.
+ */
+ static <ElementType> Holder<ElementType> of(ElementType contained) {
+ return new Identity<>(contained);
+ }
}
diff --git a/src/main/java/bjc/data/IPair.java b/src/main/java/bjc/data/IPair.java
deleted file mode 100644
index 5b1298e..0000000
--- a/src/main/java/bjc/data/IPair.java
+++ /dev/null
@@ -1,249 +0,0 @@
-package bjc.data;
-
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-import bjc.funcdata.theory.Bifunctor;
-
-/**
- * Represents a pair of values.
- *
- * @author ben
- *
- * @param <LeftType>
- * The type of the left side of the pair.
- *
- * @param <RightType>
- * The type of the right side of the pair.
- *
- */
-public interface IPair<LeftType, RightType> extends Bifunctor<LeftType, RightType> {
- /**
- * Bind a function across the values in this pair.
- *
- * @param <BoundLeft>
- * The type of the bound left.
- *
- * @param <BoundRight>
- * The type of the bound right.
- *
- * @param binder
- * The function to bind with.
- *
- * @return The bound pair.
- */
- public <BoundLeft, BoundRight> IPair<BoundLeft, BoundRight>
- bind(BiFunction<LeftType, RightType, IPair<BoundLeft, BoundRight>> binder);
-
- /**
- * Bind a function to the left value in this pair.
- *
- * @param <BoundLeft>
- * The type of the bound value.
- *
- * @param leftBinder
- * The function to use to bind.
- *
- * @return A pair with the left type bound.
- */
- public <BoundLeft> IPair<BoundLeft, RightType>
- bindLeft(Function<LeftType, IPair<BoundLeft, RightType>> leftBinder);
-
- /**
- * Bind a function to the right value in this pair.
- *
- * @param <BoundRight>
- * The type of the bound value.
- *
- * @param rightBinder
- * The function to use to bind.
- *
- * @return A pair with the right type bound.
- */
- public <BoundRight> IPair<LeftType, BoundRight>
- bindRight(Function<RightType, IPair<LeftType, BoundRight>> rightBinder);
-
- /**
- * Pairwise combine two pairs together.
- *
- * @param <OtherLeft>
- * The left type of the other pair.
- *
- * @param <OtherRight>
- * The right type of the other pair.
- *
- * @param otherPair
- * The pair to combine with.
- *
- * @return The pairs, pairwise combined together.
- */
- public default <OtherLeft, OtherRight>
- IPair<IPair<LeftType, OtherLeft>, IPair<RightType, OtherRight>>
- combine(final IPair<OtherLeft, OtherRight> otherPair) {
- return combine(otherPair, Pair<LeftType, OtherLeft>::new,
- Pair<RightType, OtherRight>::new);
- }
-
- /**
- * Combine the contents of two pairs together.
- *
- * @param <OtherLeft>
- * The type of the left value of the other pair.
- *
- * @param <OtherRight>
- * The type of the right value of the other pair.
- *
- * @param <CombinedLeft>
- * The type of the left value of the combined pair.
- *
- * @param <CombinedRight>
- * The type of the right value of the combined pair.
- *
- * @param otherPair
- * The other pair to combine with.
- *
- * @param leftCombiner
- * The function to combine the left values with.
- *
- * @param rightCombiner
- * The function to combine the right values with.
- *
- * @return A pair with its values combined.
- */
- public <OtherLeft, OtherRight, CombinedLeft, CombinedRight>
- IPair<CombinedLeft, CombinedRight>
- combine(IPair<OtherLeft, OtherRight> otherPair,
- BiFunction<LeftType, OtherLeft, CombinedLeft> leftCombiner,
- BiFunction<RightType, OtherRight, CombinedRight> rightCombiner);
-
- /**
- * Immediately perfom the specified action with the contents of this pair.
- *
- * @param consumer
- * The action to perform on the pair.
- */
- public default void doWith(final BiConsumer<LeftType, RightType> consumer) {
- merge((leftValue, rightValue) -> {
- consumer.accept(leftValue, rightValue);
-
- return null;
- });
- }
-
- @Override
- default <OldLeft, OldRight, NewLeft> LeftBifunctorMap<OldLeft, OldRight, NewLeft>
- fmapLeft(final Function<OldLeft, NewLeft> func) {
- return argumentPair -> {
- if (!(argumentPair instanceof IPair<?, ?>)) {
- final String msg
- = "This function can only be applied to instances of IPair";
-
- throw new IllegalArgumentException(msg);
- }
-
- final IPair<OldLeft, OldRight> argPair
- = (IPair<OldLeft, OldRight>) argumentPair;
-
- return argPair.mapLeft(func);
- };
- }
-
- @Override
- default <OldLeft, OldRight, NewRight> RightBifunctorMap<OldLeft, OldRight, NewRight>
- fmapRight(final Function<OldRight, NewRight> func) {
- return argumentPair -> {
- if (!(argumentPair instanceof IPair<?, ?>)) {
- final String msg
- = "This function can only be applied to instances of IPair";
-
- throw new IllegalArgumentException(msg);
- }
-
- final IPair<OldLeft, OldRight> argPair
- = (IPair<OldLeft, OldRight>) argumentPair;
-
- return argPair.mapRight(func);
- };
- }
-
- /**
- * Get the value on the left side of the pair.
- *
- * @return The value on the left side of the pair.
- */
- @Override
- public default LeftType getLeft() {
- return merge((leftValue, rightValue) -> leftValue);
- }
-
- /**
- * Get the value on the right side of the pair.
- *
- * @return The value on the right side of the pair.
- */
- @Override
- public default RightType getRight() {
- return merge((leftValue, rightValue) -> rightValue);
- }
-
- /**
- * Transform the value on the left side of the pair.
- *
- * Doesn't modify the pair.
- *
- * @param <NewLeft>
- * The new type of the left part of the pair.
- *
- * @param mapper
- * The function to use to transform the left part of the pair.
- *
- * @return The pair, with its left part transformed.
- */
- public <NewLeft> IPair<NewLeft, RightType>
- mapLeft(Function<LeftType, NewLeft> mapper);
-
- /**
- * Transform the value on the right side of the pair.
- *
- * Doesn't modify the pair.
- *
- * @param <NewRight>
- * The new type of the right part of the pair.
- *
- * @param mapper
- * The function to use to transform the right part of the
- * pair.
- *
- * @return The pair, with its right part transformed.
- */
- public <NewRight> IPair<LeftType, NewRight>
- mapRight(Function<RightType, NewRight> mapper);
-
- /**
- * Merge the two values in this pair into a single value.
- *
- * @param <MergedType>
- * The type of the single value.
- *
- * @param merger
- * The function to use for merging.
- *
- * @return The pair, merged into a single value.
- */
- public <MergedType> MergedType
- merge(BiFunction<LeftType, RightType, MergedType> merger);
-
- /**
- * Static pair constructor.
- *
- * @param left
- * The left side of the pair.
- * @param right
- * The right side of the pair.
- * @return A pair, with the specified left/right side.
- */
- public static <T1, T2> IPair<T1, T2> pair(T1 left, T2 right) {
- return new Pair<>(left, right);
- }
-}
diff --git a/src/main/java/bjc/data/ITree.java b/src/main/java/bjc/data/ITree.java
deleted file mode 100644
index e9c829e..0000000
--- a/src/main/java/bjc/data/ITree.java
+++ /dev/null
@@ -1,287 +0,0 @@
-package bjc.data;
-
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.UnaryOperator;
-
-import bjc.funcdata.IList;
-import bjc.funcdata.bst.TreeLinearizationMethod;
-
-/**
- * A node in a homogeneous tree with a unlimited amount of children.
- *
- * @author ben
- *
- * @param <ContainedType>
- * The type of data contained in the tree nodes.
- *
- */
-public interface ITree<ContainedType> {
- /**
- * Append a child to this node.
- *
- * @param child
- * The child to append to this node.
- */
- void addChild(ITree<ContainedType> child);
-
- /**
- * Append a child to this node.
- *
- * @param child
- * The child to append to this node.
- */
- void addChild(ContainedType child);
-
- /**
- * Prepend a child to this node.
- *
- * @param child
- * The child to prepend to this node.
- */
- void prependChild(ITree<ContainedType> child);
-
- /**
- * Collapse a tree into a single version.
- *
- * @param <NewType>
- * The intermediate type being folded.
- *
- * @param <ReturnedType>
- * The type that is the end result.
- *
- * @param leafTransform
- * The function to use to convert leaf values.
- *
- * @param nodeCollapser
- * The function to use to convert internal nodes and
- * their children.
- *
- * @param resultTransformer
- * The function to use to convert a state to the
- * returned version.
- *
- * @return The final transformed state.
- */
- <NewType, ReturnedType> ReturnedType collapse(
- Function<ContainedType, NewType> leafTransform,
- BiFunction<ContainedType, IList<NewType>, NewType> nodeCollapser,
- Function<NewType, ReturnedType> resultTransformer);
-
- /**
- * Execute a given action for each of this tree's children.
- *
- * @param action
- * The action to execute for each child.
- */
- void doForChildren(Consumer<ITree<ContainedType>> action);
-
- /**
- * Expand the nodes of a tree into trees, and then merge the contents of those
- * trees into a single tree.
- *
- * @param mapper
- * The function to use to map values into trees.
- *
- * @return A tree, with some nodes expanded into trees.
- */
- default ITree<ContainedType>
- flatMapTree(final Function<ContainedType, ITree<ContainedType>> mapper) {
- return topDownTransform(dat -> TopDownTransformResult.PUSHDOWN, node -> {
- if (node.getChildrenCount() > 0) {
- final ITree<ContainedType> parent = node.transformHead(mapper);
-
- node.doForChildren(parent::addChild);
-
- return parent;
- }
-
- return node.transformHead(mapper);
- });
- }
-
- /**
- * Get the specified child of this tree.
- *
- * @param childNo
- * The number of the child to get.
- *
- * @return The specified child of this tree.
- */
- default ITree<ContainedType> getChild(final int childNo) {
- return transformChild(childNo, child -> child);
- }
-
- /**
- * Get a count of the number of direct children this node has.
- *
- * @return The number of direct children this node has.
- */
- int getChildrenCount();
-
- /**
- * Get a count of the number of direct children this node has.
- *
- * @return The number of direct children this node has.
- */
- default int size() {
- return getChildrenCount();
- }
-
- /**
- * Get the data stored in this node.
- *
- * @return The data stored in this node.
- */
- default ContainedType getHead() {
- return transformHead(head -> head);
- }
-
- /**
- * Rebuild the tree with the same structure, but different nodes.
- *
- * @param <MappedType>
- * The type of the new tree.
- *
- * @param leafTransformer
- * The function to use to transform leaf tokens.
- *
- * @param internalTransformer
- * The function to use to transform internal tokens.
- *
- * @return The tree, with the nodes changed.
- */
- <MappedType> ITree<MappedType> rebuildTree(
- Function<ContainedType, MappedType> leafTransformer,
- Function<ContainedType, MappedType> internalTransformer);
-
- /**
- * Transform some of the nodes in this tree.
- *
- * @param nodePicker
- * The predicate to use to pick nodes to transform.
- *
- * @param transformer
- * The function to use to transform picked nodes.
- */
- void selectiveTransform(Predicate<ContainedType> nodePicker,
- UnaryOperator<ContainedType> transformer);
-
- /**
- * Do a top-down transform of the tree.
- *
- * @param transformPicker
- * The function to use to pick how to progress.
- *
- * @param transformer
- * The function used to transform picked subtrees.
- *
- * @return The tree with the transform applied to picked subtrees.
- */
- ITree<ContainedType> topDownTransform(
- Function<ContainedType, TopDownTransformResult> transformPicker,
- UnaryOperator<ITree<ContainedType>> transformer);
-
- /**
- * Transform one of this nodes children.
- *
- * @param <TransformedType>
- * The type of the transformed value.
- *
- * @param childNo
- * The number of the child to transform.
- *
- * @param transformer
- * The function to use to transform the value.
- *
- * @return The transformed value.
- *
- * @throws IllegalArgumentException
- * if the childNo is out of bounds (0 &lt;=
- * childNo &lt;= childCount()).
- */
- <TransformedType> TransformedType transformChild(int childNo,
- Function<ITree<ContainedType>, TransformedType> transformer);
-
- /**
- * Transform the value that is the head of this node.
- *
- * @param <TransformedType>
- * The type of the transformed value.
- *
- * @param transformer
- * The function to use to transform the value.
- *
- * @return The transformed value.
- */
- <TransformedType> TransformedType
- transformHead(Function<ContainedType, TransformedType> transformer);
-
- /**
- * Transform the tree into a tree with a different type of token.
- *
- * @param <MappedType>
- * The type of the new tree.
- *
- * @param transformer
- * The function to use to transform tokens.
- *
- * @return A tree with the token types transformed.
- */
- default <MappedType> ITree<MappedType>
- transformTree(final Function<ContainedType, MappedType> transformer) {
- return rebuildTree(transformer, transformer);
- }
-
- /**
- * Perform an action on each part of the tree.
- *
- * @param linearizationMethod
- * The way to traverse the tree.
- *
- * @param action
- * The action to perform on each tree node.
- */
- void traverse(TreeLinearizationMethod linearizationMethod,
- Consumer<ContainedType> action);
-
- /**
- * Find the farthest to right child that satisfies the given predicate.
- *
- * @param childPred
- * The predicate to satisfy.
- *
- * @return The index of the right-most child that satisfies the predicate, or -1
- * if one doesn't exist.
- */
- int revFind(Predicate<ITree<ContainedType>> childPred);
-
- /**
- * Check if this tree contains any nodes that satisfy the predicate.
- *
- * @param pred
- * The predicate to look for.
- *
- * @return Whether or not any items satisfied the predicate.
- */
- default boolean containsMatching(Predicate<ContainedType> pred) {
- Toggle<Boolean> tog = new OneWayToggle<>(false, true);
-
- traverse(TreeLinearizationMethod.POSTORDER, val -> {
- if (pred.test(val))
- tog.get();
- });
-
- return tog.get();
- }
-
- /**
- * Set the head of the tree.
- *
- * @param dat
- * The value to set as the head of the tree.
- */
- void setHead(ContainedType dat);
-}
diff --git a/src/main/java/bjc/data/Identity.java b/src/main/java/bjc/data/Identity.java
index c80c7e1..fc4798e 100644
--- a/src/main/java/bjc/data/Identity.java
+++ b/src/main/java/bjc/data/Identity.java
@@ -11,7 +11,7 @@ import java.util.function.UnaryOperator;
* @param <ContainedType>
* The type contained in the holder.
*/
-public class Identity<ContainedType> implements IHolder<ContainedType> {
+public class Identity<ContainedType> implements Holder<ContainedType> {
/* The held value. */
private ContainedType heldValue;
@@ -31,8 +31,8 @@ public class Identity<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public <BoundType> IHolder<BoundType>
- bind(final Function<ContainedType, IHolder<BoundType>> binder) {
+ public <BoundType> Holder<BoundType>
+ bind(final Function<ContainedType, Holder<BoundType>> binder) {
return binder.apply(heldValue);
}
@@ -48,26 +48,23 @@ public class Identity<ContainedType> implements IHolder<ContainedType> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof Identity))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof Identity)) return false;
final Identity<?> other = (Identity<?>) obj;
if (heldValue == null) {
- if (other.heldValue != null)
- return false;
- } else if (!heldValue.equals(other.heldValue))
+ if (other.heldValue != null) return false;
+ } else if (!heldValue.equals(other.heldValue)) {
return false;
+ }
return true;
}
@Override
- public <NewType> Function<ContainedType, IHolder<NewType>>
+ public <NewType> Function<ContainedType, Holder<NewType>>
lift(final Function<ContainedType, NewType> func) {
return val -> {
return new Identity<>(func.apply(val));
@@ -75,7 +72,7 @@ public class Identity<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(final Function<ContainedType, MappedType> mapper) {
return new Identity<>(mapper.apply(heldValue));
}
@@ -86,7 +83,7 @@ public class Identity<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public IHolder<ContainedType>
+ public Holder<ContainedType>
transform(final UnaryOperator<ContainedType> transformer) {
heldValue = transformer.apply(heldValue);
@@ -102,8 +99,8 @@ public class Identity<ContainedType> implements IHolder<ContainedType> {
/**
* Create a new identity container.
*
- * @param val
- * The contained value.
+ * @param <ContainedType> The type of the contained value.
+ * @param val The contained value.
*
* @return A new identity container.
*/
@@ -113,7 +110,9 @@ public class Identity<ContainedType> implements IHolder<ContainedType> {
/**
* Create a new empty identity container.
- *
+ *
+ * @param <ContainedType> The type of the contained value.
+ *
* @return A new empty identity container.
*/
public static <ContainedType> Identity<ContainedType> id() {
diff --git a/src/main/java/bjc/data/IntHolder.java b/src/main/java/bjc/data/IntHolder.java
new file mode 100644
index 0000000..2783669
--- /dev/null
+++ b/src/main/java/bjc/data/IntHolder.java
@@ -0,0 +1,72 @@
+package bjc.data;
+
+/**
+ * Utility class for ints by ref.
+ *
+ * @author Ben Culkin
+ */
+public class IntHolder {
+ /**
+ * The int value.
+ */
+ public int val;
+
+ /**
+ * Create a new int-holder set to 0.
+ */
+ public IntHolder() {
+ val = 0;
+ }
+
+ /**
+ * Create a new int-holder set to a value.
+ *
+ * @param i
+ * The value to set the int to.
+ */
+ public IntHolder(int i) {
+ val = i;
+ }
+
+ /**
+ * Increment the value by one, and return it.
+ *
+ * @return The value of the holder.
+ */
+ public int incr() {
+ return incr(1);
+ }
+
+ /**
+ * Increment the value by an amount and return it.
+ *
+ * @param i
+ * The amount to increment by.
+ *
+ * @return The value of the holder.
+ */
+ public int incr(int i) {
+ val += 1;
+
+ return val;
+ }
+
+ /**
+ * Get the value.
+ *
+ * @return The value.
+ */
+ public int get() {
+ return val;
+ }
+
+ /**
+ * Set the value.
+ *
+ * @param i
+ * The value to set it to.
+ */
+ public void set(int i) {
+ val = i;
+ }
+}
diff --git a/src/main/java/bjc/data/Lazy.java b/src/main/java/bjc/data/Lazy.java
index a425232..b237935 100644
--- a/src/main/java/bjc/data/Lazy.java
+++ b/src/main/java/bjc/data/Lazy.java
@@ -6,7 +6,7 @@ import java.util.function.UnaryOperator;
import bjc.data.internals.BoundLazy;
import bjc.funcdata.FunctionalList;
-import bjc.funcdata.IList;
+import bjc.funcdata.ListEx;
/**
* A holder that holds a means to create a value, but doesn't actually compute
@@ -17,7 +17,7 @@ import bjc.funcdata.IList;
* @param <ContainedType>
* The type of the value being held.
*/
-public class Lazy<ContainedType> implements IHolder<ContainedType> {
+public class Lazy<ContainedType> implements Holder<ContainedType> {
/* The supplier of the type. */
private Supplier<ContainedType> valueSupplier;
/* The actual type value. */
@@ -26,7 +26,7 @@ public class Lazy<ContainedType> implements IHolder<ContainedType> {
private boolean valueMaterialized;
/* The list of pending actions on the value. */
- private IList<UnaryOperator<ContainedType>> actions = new FunctionalList<>();
+ private ListEx<UnaryOperator<ContainedType>> actions = new FunctionalList<>();
/**
* Create a new lazy value from the specified seed value.
@@ -54,41 +54,44 @@ public class Lazy<ContainedType> implements IHolder<ContainedType> {
/* Create a new value from a supplier and a list of actions. */
private Lazy(final Supplier<ContainedType> supp,
- final IList<UnaryOperator<ContainedType>> pendingActions) {
+ final ListEx<UnaryOperator<ContainedType>> pendingActions) {
valueSupplier = supp;
actions = pendingActions;
}
@Override
- public <BoundType> IHolder<BoundType>
- bind(final Function<ContainedType, IHolder<BoundType>> binder) {
- final IList<UnaryOperator<ContainedType>> pendingActions = new FunctionalList<>();
+ public <BoundType> Holder<BoundType>
+ bind(final Function<ContainedType, Holder<BoundType>> binder) {
+ final ListEx<UnaryOperator<ContainedType>> pendingActions = new FunctionalList<>();
- actions.forEach(pendingActions::add);
+ for (UnaryOperator<ContainedType> action : actions) {
+ pendingActions.add(action);
+ }
+
final Supplier<ContainedType> supplier = () -> {
- if (valueMaterialized)
- return heldValue;
-
- return valueSupplier.get();
+ if (valueMaterialized) return heldValue;
+ else return valueSupplier.get();
};
return new BoundLazy<>(() -> new Lazy<>(supplier, pendingActions), binder);
}
@Override
- public <NewType> Function<ContainedType, IHolder<NewType>>
+ public <NewType> Function<ContainedType, Holder<NewType>>
lift(final Function<ContainedType, NewType> func) {
return val -> new Lazy<>(func.apply(val));
}
@Override
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(final Function<ContainedType, MappedType> mapper) {
- final IList<UnaryOperator<ContainedType>> pendingActions = new FunctionalList<>();
-
- actions.forEach(pendingActions::add);
+ final ListEx<UnaryOperator<ContainedType>> pendingActions = new FunctionalList<>();
+
+ for (UnaryOperator<ContainedType> action : actions) {
+ pendingActions.add(action);
+ }
return new Lazy<>(() -> {
ContainedType currVal = heldValue;
@@ -97,7 +100,8 @@ public class Lazy<ContainedType> implements IHolder<ContainedType> {
currVal = valueSupplier.get();
}
- return pendingActions.reduceAux(currVal, UnaryOperator<ContainedType>::apply,
+ return pendingActions.reduceAux(currVal,
+ UnaryOperator<ContainedType>::apply,
value -> mapper.apply(value));
});
}
@@ -107,16 +111,22 @@ public class Lazy<ContainedType> implements IHolder<ContainedType> {
if (valueMaterialized) {
if (actions.isEmpty()) {
return String.format("value[v='%s']", heldValue);
+ } else {
+ return String.format("value[v='%s'] (has %d pending transforms)",
+ heldValue, actions.getSize());
}
-
- return String.format("value[v='%s'] (has pending transforms)", heldValue);
}
- return "(unmaterialized)";
+ if (actions.isEmpty()) {
+ return"(unmaterialized)";
+ } else {
+ return String.format("(unmaterialized; has %d pending transforms",
+ actions.getSize());
+ }
}
@Override
- public IHolder<ContainedType>
+ public Holder<ContainedType>
transform(final UnaryOperator<ContainedType> transformer) {
actions.add(transformer);
@@ -132,9 +142,9 @@ public class Lazy<ContainedType> implements IHolder<ContainedType> {
valueMaterialized = true;
}
- actions.forEach(action -> {
- heldValue = action.apply(heldValue);
- });
+ for (UnaryOperator<ContainedType> action : actions) {
+ heldValue = action.apply(heldValue);
+ }
actions = new FunctionalList<>();
@@ -155,12 +165,9 @@ public class Lazy<ContainedType> implements IHolder<ContainedType> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof Lazy<?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof Lazy<?>)) return false;
final Lazy<?> other = (Lazy<?>) obj;
@@ -169,27 +176,29 @@ public class Lazy<ContainedType> implements IHolder<ContainedType> {
if (valueMaterialized) {
if (heldValue == null) {
- if (other.heldValue != null)
- return false;
- } else if (!heldValue.equals(other.heldValue))
+ if (other.heldValue != null) return false;
+ } else if (!heldValue.equals(other.heldValue)) {
return false;
- } else
+ }
+ } else {
return false;
+ }
if (actions == null) {
- if (other.actions != null)
- return false;
- } else if (actions.getSize() > 0 || other.actions.getSize() > 0)
+ if (other.actions != null) return false;
+ } else if (actions.getSize() > 0 || other.actions.getSize() > 0) {
return false;
+ }
return true;
}
/**
* Create a new lazy container with an already present value.
- *
- * @param val
- * The value for the lazy container.
+ *
+ * @param <ContainedType> The type of the contained value.
+ *
+ * @param val The value for the lazy container.
*
* @return A new lazy container holding that value.
*/
@@ -200,8 +209,9 @@ public class Lazy<ContainedType> implements IHolder<ContainedType> {
/**
* Create a new lazy container with a suspended value.
*
- * @param supp
- * The suspended value for the lazy container.
+ * @param <ContainedType> The type of the contained value.
+ *
+ * @param supp The suspended value for the lazy container.
*
* @return A new lazy container that will un-suspend the value when necessary.
*/
diff --git a/src/main/java/bjc/data/LazyPair.java b/src/main/java/bjc/data/LazyPair.java
index e668cd4..048254a 100644
--- a/src/main/java/bjc/data/LazyPair.java
+++ b/src/main/java/bjc/data/LazyPair.java
@@ -18,7 +18,7 @@ import bjc.data.internals.HalfBoundLazyPair;
* @param <RightType>
* The type on the right side of the pair.
*/
-public class LazyPair<LeftType, RightType> implements IPair<LeftType, RightType> {
+public class LazyPair<LeftType, RightType> implements Pair<LeftType, RightType> {
/* The supplier for the left value. */
private Supplier<LeftType> leftSupplier;
/* The left value. */
@@ -70,17 +70,16 @@ public class LazyPair<LeftType, RightType> implements IPair<LeftType, RightType>
}
@Override
- public <BoundLeft, BoundRight> IPair<BoundLeft, BoundRight> bind(
- final BiFunction<LeftType, RightType, IPair<BoundLeft, BoundRight>> binder) {
+ public <BoundLeft, BoundRight> Pair<BoundLeft, BoundRight> bind(
+ final BiFunction<LeftType, RightType, Pair<BoundLeft, BoundRight>> binder) {
return new BoundLazyPair<>(leftSupplier, rightSupplier, binder);
}
@Override
- public <BoundLeft> IPair<BoundLeft, RightType>
- bindLeft(final Function<LeftType, IPair<BoundLeft, RightType>> leftBinder) {
+ public <BoundLeft> Pair<BoundLeft, RightType>
+ bindLeft(final Function<LeftType, Pair<BoundLeft, RightType>> leftBinder) {
final Supplier<LeftType> leftSupp = () -> {
- if (leftMaterialized)
- return leftValue;
+ if (leftMaterialized) return leftValue;
return leftSupplier.get();
};
@@ -89,11 +88,10 @@ public class LazyPair<LeftType, RightType> implements IPair<LeftType, RightType>
}
@Override
- public <BoundRight> IPair<LeftType, BoundRight> bindRight(
- final Function<RightType, IPair<LeftType, BoundRight>> rightBinder) {
+ public <BoundRight> Pair<LeftType, BoundRight> bindRight(
+ final Function<RightType, Pair<LeftType, BoundRight>> rightBinder) {
final Supplier<RightType> rightSupp = () -> {
- if (rightMaterialized)
- return rightValue;
+ if (rightMaterialized) return rightValue;
return rightSupplier.get();
};
@@ -103,17 +101,19 @@ public class LazyPair<LeftType, RightType> implements IPair<LeftType, RightType>
@Override
public <OtherLeft, OtherRight, CombinedLeft, CombinedRight>
- IPair<CombinedLeft, CombinedRight>
- combine(final IPair<OtherLeft, OtherRight> otherPair,
+ Pair<CombinedLeft, CombinedRight>
+ combine(final Pair<OtherLeft, OtherRight> otherPair,
final BiFunction<LeftType, OtherLeft, CombinedLeft> leftCombiner,
final BiFunction<RightType, OtherRight,
CombinedRight> rightCombiner) {
- return otherPair.bind((otherLeft, otherRight) -> bind((leftVal, rightVal) -> {
- final CombinedLeft left = leftCombiner.apply(leftVal, otherLeft);
- final CombinedRight right = rightCombiner.apply(rightVal, otherRight);
-
- return new LazyPair<>(left, right);
- }));
+ return otherPair.bind((otherLeft, otherRight) -> {
+ return bind((leftVal, rightVal) -> {
+ final CombinedLeft left = leftCombiner.apply(leftVal, otherLeft);
+ final CombinedRight right = rightCombiner.apply(rightVal, otherRight);
+
+ return new LazyPair<>(left, right);
+ });
+ });
}
@Override
@@ -139,18 +139,16 @@ public class LazyPair<LeftType, RightType> implements IPair<LeftType, RightType>
}
@Override
- public <NewLeft> IPair<NewLeft, RightType>
+ public <NewLeft> Pair<NewLeft, RightType>
mapLeft(final Function<LeftType, NewLeft> mapper) {
final Supplier<NewLeft> leftSupp = () -> {
- if (leftMaterialized)
- return mapper.apply(leftValue);
+ if (leftMaterialized) return mapper.apply(leftValue);
return mapper.apply(leftSupplier.get());
};
final Supplier<RightType> rightSupp = () -> {
- if (rightMaterialized)
- return rightValue;
+ if (rightMaterialized) return rightValue;
return rightSupplier.get();
};
@@ -159,18 +157,16 @@ public class LazyPair<LeftType, RightType> implements IPair<LeftType, RightType>
}
@Override
- public <NewRight> IPair<LeftType, NewRight>
+ public <NewRight> Pair<LeftType, NewRight>
mapRight(final Function<RightType, NewRight> mapper) {
final Supplier<LeftType> leftSupp = () -> {
- if (leftMaterialized)
- return leftValue;
+ if (leftMaterialized) return leftValue;
return leftSupplier.get();
};
final Supplier<NewRight> rightSupp = () -> {
- if (rightMaterialized)
- return mapper.apply(rightValue);
+ if (rightMaterialized) return mapper.apply(rightValue);
return mapper.apply(rightSupplier.get());
};
@@ -231,37 +227,35 @@ public class LazyPair<LeftType, RightType> implements IPair<LeftType, RightType>
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof LazyPair<?, ?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof LazyPair<?, ?>)) return false;
final LazyPair<?, ?> other = (LazyPair<?, ?>) obj;
- if (leftMaterialized != other.leftMaterialized)
- return false;
+ if (leftMaterialized != other.leftMaterialized) return false;
if (leftMaterialized) {
if (leftValue == null) {
- if (other.leftValue != null)
- return false;
- } else if (!leftValue.equals(other.leftValue))
+ if (other.leftValue != null) return false;
+ } else if (!leftValue.equals(other.leftValue)) {
return false;
- } else
+ }
+ } else {
return false;
+ }
- if (rightMaterialized != other.rightMaterialized)
- return false;
+ if (rightMaterialized != other.rightMaterialized) return false;
+
if (rightMaterialized) {
if (rightValue == null) {
- if (other.rightValue != null)
- return false;
- } else if (!rightValue.equals(other.rightValue))
+ if (other.rightValue != null) return false;
+ } else if (!rightValue.equals(other.rightValue)) {
return false;
- } else
+ }
+ } else {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/data/ListHolder.java b/src/main/java/bjc/data/ListHolder.java
index ab3bfa8..c8a75ff 100644
--- a/src/main/java/bjc/data/ListHolder.java
+++ b/src/main/java/bjc/data/ListHolder.java
@@ -5,7 +5,7 @@ import java.util.function.UnaryOperator;
import bjc.data.internals.BoundListHolder;
import bjc.funcdata.FunctionalList;
-import bjc.funcdata.IList;
+import bjc.funcdata.ListEx;
/**
* A holder that represents a set of non-deterministic computations.
@@ -15,8 +15,8 @@ import bjc.funcdata.IList;
* @param <ContainedType>
* The type of contained value.
*/
-public class ListHolder<ContainedType> implements IHolder<ContainedType> {
- private IList<ContainedType> heldValues;
+public class ListHolder<ContainedType> implements Holder<ContainedType> {
+ private ListEx<ContainedType> heldValues;
/**
* Create a new list holder.
@@ -36,34 +36,34 @@ public class ListHolder<ContainedType> implements IHolder<ContainedType> {
}
/* Create a new holder with values. */
- private ListHolder(final IList<ContainedType> toHold) {
+ private ListHolder(final ListEx<ContainedType> toHold) {
heldValues = toHold;
}
@Override
- public <BoundType> IHolder<BoundType>
- bind(final Function<ContainedType, IHolder<BoundType>> binder) {
- final IList<IHolder<BoundType>> boundValues = heldValues.map(binder);
+ public <BoundType> Holder<BoundType>
+ bind(final Function<ContainedType, Holder<BoundType>> binder) {
+ final ListEx<Holder<BoundType>> boundValues = heldValues.map(binder);
return new BoundListHolder<>(boundValues);
}
@Override
- public <NewType> Function<ContainedType, IHolder<NewType>>
+ public <NewType> Function<ContainedType, Holder<NewType>>
lift(final Function<ContainedType, NewType> func) {
return val -> new ListHolder<>(new FunctionalList<>(func.apply(val)));
}
@Override
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(final Function<ContainedType, MappedType> mapper) {
- final IList<MappedType> mappedValues = heldValues.map(mapper);
+ final ListEx<MappedType> mappedValues = heldValues.map(mapper);
return new ListHolder<>(mappedValues);
}
@Override
- public IHolder<ContainedType>
+ public Holder<ContainedType>
transform(final UnaryOperator<ContainedType> transformer) {
heldValues = heldValues.map(transformer);
@@ -93,20 +93,17 @@ public class ListHolder<ContainedType> implements IHolder<ContainedType> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof ListHolder<?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof ListHolder<?>)) return false;
final ListHolder<?> other = (ListHolder<?>) obj;
if (heldValues == null) {
- if (other.heldValues != null)
- return false;
- } else if (!heldValues.equals(other.heldValues))
+ if (other.heldValues != null) return false;
+ } else if (!heldValues.equals(other.heldValues)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/data/NonCMEIterator.java b/src/main/java/bjc/data/NonCMEIterator.java
new file mode 100644
index 0000000..88eaa6b
--- /dev/null
+++ b/src/main/java/bjc/data/NonCMEIterator.java
@@ -0,0 +1,54 @@
+package bjc.data;
+
+import java.util.*;
+
+/**
+ * A Iterator which is guaranteed to never throw {@link ConcurrentModificationException}.
+ *
+ * The intended use case for this iterator is that you want to be able to modify
+ * a list you are iterating over, and not get an immediate exception.
+ *
+ * Note that using this is an agreement that you will not complain if it behaves
+ * oddly in the face of you concurrently modifying the list, as this class was
+ * designed with the usecase of modifying the list from the same thread, without
+ * invalidating the iterators. After all, there is a reason that most iterator
+ * types throw that exception.
+ *
+ * However, sometimes you want to play with fire, even if you might get burnt.
+ * This allows you to do so.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ElementType> The type being iterated over.
+ */
+public class NonCMEIterator<ElementType> implements Iterator<ElementType> {
+ /**
+ * The list being iterated over.
+ */
+ protected final List<ElementType> source;
+
+ /**
+ * The index of the current item in the list.
+ */
+ protected int currIndex;
+
+ /**
+ * Create a new iterator which won't throw {@link ConcurrentModificationException}
+ *
+ * @param source The list being iterated over.
+ */
+ public NonCMEIterator(List<ElementType> source) {
+ this.source = source;
+ this.currIndex = -1;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return currIndex >= source.size();
+ }
+
+ @Override
+ public ElementType next() {
+ return source.get(++currIndex);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/bjc/data/NonCMEListIterator.java b/src/main/java/bjc/data/NonCMEListIterator.java
new file mode 100644
index 0000000..6e74a24
--- /dev/null
+++ b/src/main/java/bjc/data/NonCMEListIterator.java
@@ -0,0 +1,68 @@
+package bjc.data;
+
+import java.util.*;
+
+/**
+ * A ListIterator which is guaranteed to never throw {@link ConcurrentModificationException}.
+ *
+ * The intended use case for this iterator is that you want to be able to modify
+ * a list you are iterating over, and not get an immediate exception.
+ *
+ * Note that using this is an agreement that you will not complain if it behaves
+ * oddly in the face of you concurrently modifying the list, as this class was
+ * designed with the usecase of modifying the list from the same thread, without
+ * invalidating the iterators. After all, there is a reason that most iterator
+ * types throw that exception.
+ *
+ * However, sometimes you want to play with fire, even if you might get burnt.
+ * This allows you to do so.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ElementType> The type being iterated over.
+ */
+public class NonCMEListIterator<ElementType> extends NonCMEIterator<ElementType> implements ListIterator<ElementType> {
+ /**
+ * Create a new list iterator which won't throw {@link ConcurrentModificationException}
+ *
+ * @param source The list to iterate over.
+ */
+ public NonCMEListIterator(List<ElementType> source) {
+ super(source);
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return currIndex > 0;
+ }
+
+ @Override
+ public ElementType previous() {
+ return source.get(--currIndex);
+ }
+
+ @Override
+ public int nextIndex() {
+ return currIndex + 1;
+ }
+
+ @Override
+ public int previousIndex() {
+ return currIndex - 1;
+ }
+
+ @Override
+ public void remove() {
+ source.remove(currIndex);
+ }
+
+ @Override
+ public void set(ElementType element) {
+ source.set(currIndex, element);
+ }
+
+ @Override
+ public void add(ElementType element) {
+ source.add(currIndex, element);
+ }
+}
diff --git a/src/main/java/bjc/data/OneWayToggle.java b/src/main/java/bjc/data/OneWayToggle.java
index c2e9e4b..b6074b0 100644
--- a/src/main/java/bjc/data/OneWayToggle.java
+++ b/src/main/java/bjc/data/OneWayToggle.java
@@ -31,23 +31,21 @@ public class OneWayToggle<E> implements Toggle<E> {
@Override
public E get() {
- if (gotFirst)
- return second;
+ if (gotFirst) return second;
gotFirst = true;
+
return first;
}
@Override
public E peek() {
- if (gotFirst)
- return second;
- return first;
+ if (gotFirst) return second;
+ else return first;
}
@Override
public void set(boolean gotFirst) {
this.gotFirst = gotFirst;
}
-
}
diff --git a/src/main/java/bjc/data/Option.java b/src/main/java/bjc/data/Option.java
index b5d6d5e..6ca8b13 100644
--- a/src/main/java/bjc/data/Option.java
+++ b/src/main/java/bjc/data/Option.java
@@ -11,9 +11,17 @@ import java.util.function.UnaryOperator;
* @param <ContainedType>
* The type of the value that may or may not be held.
*/
-public class Option<ContainedType> implements IHolder<ContainedType> {
+public class Option<ContainedType> implements Holder<ContainedType> {
private ContainedType held;
-
+ private boolean isHolding;
+
+ /**
+ * Create a new empty optional.
+ */
+ public Option() {
+ isHolding = false;
+ }
+
/**
* Create a new optional, using the given initial value.
*
@@ -22,38 +30,35 @@ public class Option<ContainedType> implements IHolder<ContainedType> {
*/
public Option(final ContainedType seed) {
held = seed;
+ isHolding = true;
}
@Override
- public <BoundType> IHolder<BoundType>
- bind(final Function<ContainedType, IHolder<BoundType>> binder) {
- if (held == null)
- return new Option<>(null);
+ public <BoundType> Holder<BoundType>
+ bind(final Function<ContainedType, Holder<BoundType>> binder) {
+ if (isHolding) return new Option<>();
return binder.apply(held);
}
@Override
- public <NewType> Function<ContainedType, IHolder<NewType>>
+ public <NewType> Function<ContainedType, Holder<NewType>>
lift(final Function<ContainedType, NewType> func) {
return val -> new Option<>(func.apply(val));
}
@Override
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(final Function<ContainedType, MappedType> mapper) {
- if (held == null)
- return new Option<>(null);
+ if (isHolding) return new Option<>();
return new Option<>(mapper.apply(held));
}
@Override
- public IHolder<ContainedType>
+ public Holder<ContainedType>
transform(final UnaryOperator<ContainedType> transformer) {
- if (held != null) {
- held = transformer.apply(held);
- }
+ if (isHolding) held = transformer.apply(held);
return this;
}
@@ -61,8 +66,7 @@ public class Option<ContainedType> implements IHolder<ContainedType> {
@Override
public <UnwrappedType> UnwrappedType
unwrap(final Function<ContainedType, UnwrappedType> unwrapper) {
- if (held == null)
- return null;
+ if (isHolding) return null;
return unwrapper.apply(held);
}
@@ -84,20 +88,17 @@ public class Option<ContainedType> implements IHolder<ContainedType> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof Option<?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof Option<?>)) return false;
final Option<?> other = (Option<?>) obj;
if (held == null) {
- if (other.held != null)
- return false;
- } else if (!held.equals(other.held))
+ if (other.held != null) return false;
+ } else if (!held.equals(other.held)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/data/Pair.java b/src/main/java/bjc/data/Pair.java
index 610e89c..baf1894 100644
--- a/src/main/java/bjc/data/Pair.java
+++ b/src/main/java/bjc/data/Pair.java
@@ -1,163 +1,250 @@
package bjc.data;
+import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
+import bjc.funcdata.theory.Bifunctor;
+
/**
- * A pair of values, with nothing special about them.
+ * Represents a pair of values.
*
* @author ben
*
* @param <LeftType>
- * The type of the left value.
+ * The type of the left side of the pair.
*
* @param <RightType>
- * The type of the right value.
+ * The type of the right side of the pair.
+ *
*/
-public class Pair<LeftType, RightType> implements IPair<LeftType, RightType> {
- /* The left value. */
- private LeftType leftValue;
- /* The right value. */
- private RightType rightValue;
-
- /** Create a new pair with both sides set to null. */
- public Pair() {
-
- }
-
+public interface Pair<LeftType, RightType> extends Bifunctor<LeftType, RightType> {
/**
- * Create a new pair with both sides set to the specified values.
+ * Bind a function across the values in this pair.
*
- * @param left
- * The value of the left side.
+ * @param <BoundLeft>
+ * The type of the bound left.
*
- * @param right
- * The value of the right side.
+ * @param <BoundRight>
+ * The type of the bound right.
+ *
+ * @param binder
+ * The function to bind with.
+ *
+ * @return The bound pair.
*/
- public Pair(final LeftType left, final RightType right) {
- leftValue = left;
- rightValue = right;
- }
-
- @Override
- public <BoundLeft, BoundRight> IPair<BoundLeft, BoundRight> bind(
- final BiFunction<LeftType, RightType, IPair<BoundLeft, BoundRight>> binder) {
- if (binder == null)
- throw new NullPointerException("Binder must not be null.");
+ public <BoundLeft, BoundRight> Pair<BoundLeft, BoundRight>
+ bind(BiFunction<LeftType, RightType, Pair<BoundLeft, BoundRight>> binder);
- return binder.apply(leftValue, rightValue);
- }
+ /**
+ * Bind a function to the left value in this pair.
+ *
+ * @param <BoundLeft>
+ * The type of the bound value.
+ *
+ * @param leftBinder
+ * The function to use to bind.
+ *
+ * @return A pair with the left type bound.
+ */
+ public <BoundLeft> Pair<BoundLeft, RightType>
+ bindLeft(Function<LeftType, Pair<BoundLeft, RightType>> leftBinder);
- @Override
- public <BoundLeft> IPair<BoundLeft, RightType>
- bindLeft(final Function<LeftType, IPair<BoundLeft, RightType>> leftBinder) {
- if (leftBinder == null)
- throw new NullPointerException("Binder must not be null");
+ /**
+ * Bind a function to the right value in this pair.
+ *
+ * @param <BoundRight>
+ * The type of the bound value.
+ *
+ * @param rightBinder
+ * The function to use to bind.
+ *
+ * @return A pair with the right type bound.
+ */
+ public <BoundRight> Pair<LeftType, BoundRight>
+ bindRight(Function<RightType, Pair<LeftType, BoundRight>> rightBinder);
- return leftBinder.apply(leftValue);
+ /**
+ * Pairwise combine two pairs together.
+ *
+ * @param <OtherLeft>
+ * The left type of the other pair.
+ *
+ * @param <OtherRight>
+ * The right type of the other pair.
+ *
+ * @param otherPair
+ * The pair to combine with.
+ *
+ * @return The pairs, pairwise combined together.
+ */
+ public default <OtherLeft, OtherRight>
+ Pair<Pair<LeftType, OtherLeft>, Pair<RightType, OtherRight>>
+ combine(final Pair<OtherLeft, OtherRight> otherPair) {
+ return combine(otherPair,
+ SimplePair<LeftType, OtherLeft>::new,
+ SimplePair<RightType, OtherRight>::new);
}
- @Override
- public <BoundRight> IPair<LeftType, BoundRight> bindRight(
- final Function<RightType, IPair<LeftType, BoundRight>> rightBinder) {
- if (rightBinder == null)
- throw new NullPointerException("Binder must not be null");
+ /**
+ * Combine the contents of two pairs together.
+ *
+ * @param <OtherLeft>
+ * The type of the left value of the other pair.
+ *
+ * @param <OtherRight>
+ * The type of the right value of the other pair.
+ *
+ * @param <CombinedLeft>
+ * The type of the left value of the combined pair.
+ *
+ * @param <CombinedRight>
+ * The type of the right value of the combined pair.
+ *
+ * @param otherPair
+ * The other pair to combine with.
+ *
+ * @param leftCombiner
+ * The function to combine the left values with.
+ *
+ * @param rightCombiner
+ * The function to combine the right values with.
+ *
+ * @return A pair with its values combined.
+ */
+ public <OtherLeft, OtherRight, CombinedLeft, CombinedRight>
+ Pair<CombinedLeft, CombinedRight>
+ combine(Pair<OtherLeft, OtherRight> otherPair,
+ BiFunction<LeftType, OtherLeft, CombinedLeft> leftCombiner,
+ BiFunction<RightType, OtherRight, CombinedRight> rightCombiner);
- return rightBinder.apply(rightValue);
- }
+ /**
+ * Immediately perfom the specified action with the contents of this pair.
+ *
+ * @param consumer
+ * The action to perform on the pair.
+ */
+ public default void doWith(final BiConsumer<LeftType, RightType> consumer) {
+ merge((leftValue, rightValue) -> {
+ consumer.accept(leftValue, rightValue);
- @Override
- public <OtherLeft, OtherRight, CombinedLeft, CombinedRight>
- IPair<CombinedLeft, CombinedRight>
- combine(final IPair<OtherLeft, OtherRight> otherPair,
- final BiFunction<LeftType, OtherLeft, CombinedLeft> leftCombiner,
- final BiFunction<RightType, OtherRight,
- CombinedRight> rightCombiner) {
- return otherPair.bind((otherLeft, otherRight) -> {
- final CombinedLeft left = leftCombiner.apply(leftValue, otherLeft);
- final CombinedRight right = rightCombiner.apply(rightValue, otherRight);
-
- return new Pair<>(left, right);
+ return null;
});
}
@Override
- public <NewLeft> IPair<NewLeft, RightType>
- mapLeft(final Function<LeftType, NewLeft> mapper) {
- if (mapper == null)
- throw new NullPointerException("Mapper must not be null");
+ default <OldLeft, OldRight, NewLeft> LeftBifunctorMap<OldLeft, OldRight, NewLeft>
+ fmapLeft(final Function<OldLeft, NewLeft> func) {
+ return argumentPair -> {
+ if (!(argumentPair instanceof Pair<?, ?>)) {
+ final String msg
+ = "This function can only be applied to instances of IPair";
- return new Pair<>(mapper.apply(leftValue), rightValue);
- }
+ throw new IllegalArgumentException(msg);
+ }
- @Override
- public <NewRight> IPair<LeftType, NewRight>
- mapRight(final Function<RightType, NewRight> mapper) {
- if (mapper == null)
- throw new NullPointerException("Mapper must not be null");
+ final Pair<OldLeft, OldRight> argPair
+ = (Pair<OldLeft, OldRight>) argumentPair;
- return new Pair<>(leftValue, mapper.apply(rightValue));
+ return argPair.mapLeft(func);
+ };
}
@Override
- public <MergedType> MergedType
- merge(final BiFunction<LeftType, RightType, MergedType> merger) {
- if (merger == null)
- throw new NullPointerException("Merger must not be null");
+ default <OldLeft, OldRight, NewRight> RightBifunctorMap<OldLeft, OldRight, NewRight>
+ fmapRight(final Function<OldRight, NewRight> func) {
+ return argumentPair -> {
+ if (!(argumentPair instanceof Pair<?, ?>)) {
+ final String msg
+ = "This function can only be applied to instances of IPair";
- return merger.apply(leftValue, rightValue);
- }
+ throw new IllegalArgumentException(msg);
+ }
- @Override
- public String toString() {
- return String.format("Pair [leftValue='%s', rightValue='%s']", leftValue,
- rightValue);
+ final Pair<OldLeft, OldRight> argPair
+ = (Pair<OldLeft, OldRight>) argumentPair;
+
+ return argPair.mapRight(func);
+ };
}
+ /**
+ * Get the value on the left side of the pair.
+ *
+ * @return The value on the left side of the pair.
+ */
@Override
- public LeftType getLeft() {
- return leftValue;
+ public default LeftType getLeft() {
+ return merge((leftValue, rightValue) -> leftValue);
}
+ /**
+ * Get the value on the right side of the pair.
+ *
+ * @return The value on the right side of the pair.
+ */
@Override
- public RightType getRight() {
- return rightValue;
+ public default RightType getRight() {
+ return merge((leftValue, rightValue) -> rightValue);
}
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
+ /**
+ * Transform the value on the left side of the pair.
+ *
+ * Doesn't modify the pair.
+ *
+ * @param <NewLeft>
+ * The new type of the left part of the pair.
+ *
+ * @param mapper
+ * The function to use to transform the left part of the pair.
+ *
+ * @return The pair, with its left part transformed.
+ */
+ public <NewLeft> Pair<NewLeft, RightType>
+ mapLeft(Function<LeftType, NewLeft> mapper);
- result = prime * result + (leftValue == null ? 0 : leftValue.hashCode());
- result = prime * result + (rightValue == null ? 0 : rightValue.hashCode());
+ /**
+ * Transform the value on the right side of the pair.
+ *
+ * Doesn't modify the pair.
+ *
+ * @param <NewRight>
+ * The new type of the right part of the pair.
+ *
+ * @param mapper
+ * The function to use to transform the right part of the
+ * pair.
+ *
+ * @return The pair, with its right part transformed.
+ */
+ public <NewRight> Pair<LeftType, NewRight>
+ mapRight(Function<RightType, NewRight> mapper);
- return result;
- }
+ /**
+ * Merge the two values in this pair into a single value.
+ *
+ * @param <MergedType>
+ * The type of the single value.
+ *
+ * @param merger
+ * The function to use for merging.
+ *
+ * @return The pair, merged into a single value.
+ */
+ public <MergedType> MergedType
+ merge(BiFunction<LeftType, RightType, MergedType> merger);
- @Override
- public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof Pair<?, ?>))
- return false;
-
- final Pair<?, ?> other = (Pair<?, ?>) obj;
-
- if (leftValue == null) {
- if (other.leftValue != null)
- return false;
- } else if (!leftValue.equals(other.leftValue))
- return false;
-
- if (rightValue == null) {
- if (other.rightValue != null)
- return false;
- } else if (!rightValue.equals(other.rightValue))
- return false;
-
- return true;
+ /**
+ * Static pair constructor.
+ *
+ * @param <Left> The type of the left side.
+ * @param <Right> The type of the right side.
+ * @param left The left side of the pair.
+ * @param right The right side of the pair.
+ * @return A pair, with the specified left/right side.
+ */
+ public static <Left, Right> Pair<Left, Right> pair(Left left, Right right) {
+ return new SimplePair<>(left, right);
}
}
diff --git a/src/main/java/bjc/data/QueuedIterator.java b/src/main/java/bjc/data/QueuedIterator.java
index f10014e..805fc5e 100644
--- a/src/main/java/bjc/data/QueuedIterator.java
+++ b/src/main/java/bjc/data/QueuedIterator.java
@@ -9,8 +9,7 @@ import java.util.Iterator;
*
* @author bjculkin
*
- * @param <E>
- * The type of element this iterator iterates over
+ * @param <E> The type of element this iterator iterates over
*/
public class QueuedIterator<E> implements Iterator<E> {
private Iterator<E> cur;
@@ -19,7 +18,9 @@ public class QueuedIterator<E> implements Iterator<E> {
/**
* Static method for constructing iterators.
- *
+ *
+ * @param <E> The type of element this iterator iterates over
+ *
* @return A queued iterator.
*/
public static <E> QueuedIterator<E> queued() {
@@ -29,8 +30,9 @@ public class QueuedIterator<E> implements Iterator<E> {
/**
* Static method for constructing iterators.
*
- * @param vals
- * The values to iterate over.
+ * @param <E> The type of element this iterator iterates over
+ *
+ * @param vals The values to iterate over.
*
* @return A queued iterator.
*/
@@ -42,8 +44,9 @@ public class QueuedIterator<E> implements Iterator<E> {
/**
* Static method for constructing iterators.
*
- * @param itrs
- * The iterators to use.
+ * @param <E> The type of element this iterator iterates over
+ *
+ * @param itrs The iterators to use.
*
* @return A queued iterator over the provided iterators.
*/
@@ -55,8 +58,9 @@ public class QueuedIterator<E> implements Iterator<E> {
/**
* Static method for constructing iterators.
*
- * @param itrs
- * The iterables to use.
+ * @param <E> The type of element this iterator iterates over
+ *
+ * @param itrs The iterables to use.
*
* @return A queued iterator over the provided iterables.
*/
@@ -82,9 +86,7 @@ public class QueuedIterator<E> implements Iterator<E> {
public QueuedIterator(Iterator<E>... inits) {
this();
- for (Iterator<E> init : inits) {
- pending.add(init);
- }
+ for (Iterator<E> init : inits) pending.add(init);
}
/**
@@ -97,9 +99,7 @@ public class QueuedIterator<E> implements Iterator<E> {
public QueuedIterator(Iterable<E>... inits) {
this();
- for (Iterable<E> init : inits) {
- pending.add(init.iterator());
- }
+ for (Iterable<E> init : inits) pending.add(init.iterator());
}
/**
@@ -208,8 +208,7 @@ public class QueuedIterator<E> implements Iterator<E> {
@Override
public boolean hasNext() {
while (cur == null || !cur.hasNext()) {
- if (pending.isEmpty())
- return false;
+ if (pending.isEmpty()) return false;
cur = pending.pop();
}
@@ -220,13 +219,11 @@ public class QueuedIterator<E> implements Iterator<E> {
@Override
public E next() {
while (cur == null || !cur.hasNext()) {
- if (pending.isEmpty())
- return null;
+ if (pending.isEmpty()) return null;
cur = pending.pop();
}
return cur.next();
}
-
}
diff --git a/src/main/java/bjc/data/ResettableIterator.java b/src/main/java/bjc/data/ResettableIterator.java
index 2208fb2..8b7f07a 100644
--- a/src/main/java/bjc/data/ResettableIterator.java
+++ b/src/main/java/bjc/data/ResettableIterator.java
@@ -56,11 +56,8 @@ public class ResettableIterator<T> implements Iterator<T> {
@Override
public boolean hasNext() {
- if (isRepeating) {
- return cacheIterator.hasNext() ? true : backing.hasNext();
- } else {
- return backing.hasNext();
- }
+ if (isRepeating) return cacheIterator.hasNext() ? true : backing.hasNext();
+ else return backing.hasNext();
}
@Override
diff --git a/src/main/java/bjc/data/ReverseListIterator.java b/src/main/java/bjc/data/ReverseListIterator.java
new file mode 100644
index 0000000..0b6ec84
--- /dev/null
+++ b/src/main/java/bjc/data/ReverseListIterator.java
@@ -0,0 +1,72 @@
+package bjc.data;
+
+import java.util.*;
+
+/**
+ * A list iterator which iterates over a list in reverse order.
+ *
+ * @author Ben Culkin
+ *
+ * @param <Element> The type of element contained.
+ */
+public class ReverseListIterator<Element> implements ListIterator<Element>
+{
+ private List<Element> source;
+ private int currIndex;
+
+ /**
+ * Create a new reversed iterator from a list.
+ *
+ * @param source The list to operate from.
+ */
+ public ReverseListIterator(List<Element> source)
+ {
+ this.source = source;
+ this.currIndex = source.size();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return currIndex <= 0;
+ }
+
+ @Override
+ public Element next() {
+ return source.get(--currIndex);
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return currIndex < source.size();
+ }
+
+ @Override
+ public Element previous() {
+ return source.get(++currIndex);
+ }
+
+ @Override
+ public int nextIndex() {
+ return currIndex - 1;
+ }
+
+ @Override
+ public int previousIndex() {
+ return currIndex + 1;
+ }
+
+ @Override
+ public void remove() {
+ source.remove(currIndex);
+ }
+
+ @Override
+ public void set(Element element) {
+ source.set(currIndex, element);
+ }
+
+ @Override
+ public void add(Element element) {
+ source.add(currIndex, element);
+ }
+}
diff --git a/src/main/java/bjc/data/SimplePair.java b/src/main/java/bjc/data/SimplePair.java
new file mode 100644
index 0000000..48f5ca3
--- /dev/null
+++ b/src/main/java/bjc/data/SimplePair.java
@@ -0,0 +1,154 @@
+package bjc.data;
+
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * A pair of values, with nothing special about them.
+ *
+ * @author ben
+ *
+ * @param <LeftType>
+ * The type of the left value.
+ *
+ * @param <RightType>
+ * The type of the right value.
+ */
+public class SimplePair<LeftType, RightType> implements Pair<LeftType, RightType> {
+ /* The left value. */
+ private LeftType leftValue;
+ /* The right value. */
+ private RightType rightValue;
+
+ /** Create a new pair with both sides set to null. */
+ public SimplePair() {
+ // Do nothing :)
+ }
+
+ /**
+ * Create a new pair with both sides set to the specified values.
+ *
+ * @param left
+ * The value of the left side.
+ *
+ * @param right
+ * The value of the right side.
+ */
+ public SimplePair(final LeftType left, final RightType right) {
+ leftValue = left;
+ rightValue = right;
+ }
+
+ @Override
+ public <BoundLeft, BoundRight> Pair<BoundLeft, BoundRight> bind(
+ final BiFunction<LeftType, RightType, Pair<BoundLeft, BoundRight>> binder) {
+ if (binder == null) throw new NullPointerException("Binder must not be null.");
+
+ return binder.apply(leftValue, rightValue);
+ }
+
+ @Override
+ public <BoundLeft> Pair<BoundLeft, RightType>
+ bindLeft(final Function<LeftType, Pair<BoundLeft, RightType>> leftBinder) {
+ if (leftBinder == null) throw new NullPointerException("Binder must not be null");
+
+ return leftBinder.apply(leftValue);
+ }
+
+ @Override
+ public <BoundRight> Pair<LeftType, BoundRight> bindRight(
+ final Function<RightType, Pair<LeftType, BoundRight>> rightBinder) {
+ if (rightBinder == null) throw new NullPointerException("Binder must not be null");
+
+ return rightBinder.apply(rightValue);
+ }
+
+ @Override
+ public <OtherLeft, OtherRight, CombinedLeft, CombinedRight>
+ Pair<CombinedLeft, CombinedRight>
+ combine(final Pair<OtherLeft, OtherRight> otherPair,
+ final BiFunction<LeftType, OtherLeft, CombinedLeft> leftCombiner,
+ final BiFunction<RightType, OtherRight,
+ CombinedRight> rightCombiner) {
+ return otherPair.bind((otherLeft, otherRight) -> {
+ final CombinedLeft left = leftCombiner.apply(leftValue, otherLeft);
+ final CombinedRight right = rightCombiner.apply(rightValue, otherRight);
+
+ return new SimplePair<>(left, right);
+ });
+ }
+
+ @Override
+ public <NewLeft> Pair<NewLeft, RightType>
+ mapLeft(final Function<LeftType, NewLeft> mapper) {
+ if (mapper == null) throw new NullPointerException("Mapper must not be null");
+
+ return new SimplePair<>(mapper.apply(leftValue), rightValue);
+ }
+
+ @Override
+ public <NewRight> Pair<LeftType, NewRight>
+ mapRight(final Function<RightType, NewRight> mapper) {
+ if (mapper == null) throw new NullPointerException("Mapper must not be null");
+
+ return new SimplePair<>(leftValue, mapper.apply(rightValue));
+ }
+
+ @Override
+ public <MergedType> MergedType
+ merge(final BiFunction<LeftType, RightType, MergedType> merger) {
+ if (merger == null) throw new NullPointerException("Merger must not be null");
+
+ return merger.apply(leftValue, rightValue);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Pair [leftValue='%s', rightValue='%s']", leftValue,
+ rightValue);
+ }
+
+ @Override
+ public LeftType getLeft() {
+ return leftValue;
+ }
+
+ @Override
+ public RightType getRight() {
+ return rightValue;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+
+ result = prime * result + (leftValue == null ? 0 : leftValue.hashCode());
+ result = prime * result + (rightValue == null ? 0 : rightValue.hashCode());
+
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof SimplePair<?, ?>)) return false;
+
+ final SimplePair<?, ?> other = (SimplePair<?, ?>) obj;
+
+ if (leftValue == null) {
+ if (other.leftValue != null) return false;
+ } else if (!leftValue.equals(other.leftValue)) {
+ return false;
+ }
+
+ if (rightValue == null) {
+ if (other.rightValue != null) return false;
+ } else if (!rightValue.equals(other.rightValue)) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/bjc/data/SimpleTree.java b/src/main/java/bjc/data/SimpleTree.java
new file mode 100644
index 0000000..7d9fb6d
--- /dev/null
+++ b/src/main/java/bjc/data/SimpleTree.java
@@ -0,0 +1,446 @@
+package bjc.data;
+
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+
+import bjc.funcdata.FunctionalList;
+import bjc.funcdata.ListEx;
+import bjc.funcdata.bst.TreeLinearizationMethod;
+
+/**
+ * A node in a homogeneous tree.
+ *
+ * @author ben
+ *
+ * @param <ContainedType>
+ * The type contained in the tree.
+ */
+public class SimpleTree<ContainedType> implements Tree<ContainedType> {
+ /* The data/label for this node. */
+ private ContainedType data;
+
+ /* The children of this node. */
+ private ListEx<Tree<ContainedType>> children;
+
+ /* Whether this node has children. */
+ /*
+ * @NOTE Why have both this boolean and childCount? Why not just do a childCount
+ * == 0 whenever you'd check hasChildren?
+ *
+ * - Because hasChildren is set once and not reset, and really what it
+ * indicates is that children has been allocated.
+ */
+ private boolean hasChildren;
+ /* The number of children this node has. */
+ private int childCount = 0;
+
+ /* The ID of this node. */
+ private int ID;
+ /* The next ID to assign to a node. */
+ private static int nextID = 0;
+
+ /**
+ * Create a new leaf node in a tree.
+ */
+ public SimpleTree() {
+ this(null);
+ }
+
+ /**
+ * Create a new leaf node in a tree.
+ *
+ * @param leaf
+ * The data to store as a leaf node.
+ */
+ public SimpleTree(final ContainedType leaf) {
+ data = leaf;
+
+ hasChildren = false;
+
+ ID = nextID++;
+ }
+
+ /**
+ * Create a new tree node with the specified children.
+ *
+ * @param leaf
+ * The data to hold in this node.
+ *
+ * @param childrn
+ * A list of children for this node.
+ */
+ public SimpleTree(final ContainedType leaf, final ListEx<Tree<ContainedType>> childrn) {
+ this(leaf);
+
+ hasChildren = true;
+
+ childCount = childrn.getSize();
+
+ children = childrn;
+ }
+
+ /**
+ * Create a new tree node with the specified children.
+ *
+ * @param leaf
+ * The data to hold in this node.
+ *
+ * @param childrn
+ * A list of children for this node.
+ */
+ @SafeVarargs
+ public SimpleTree(final ContainedType leaf, final Tree<ContainedType>... childrn) {
+ this(leaf);
+
+ hasChildren = true;
+
+ childCount = 0;
+
+ children = new FunctionalList<>();
+
+ for (final Tree<ContainedType> child : childrn) {
+ children.add(child);
+
+ childCount++;
+ }
+ }
+
+ @Override
+ public void addChild(final ContainedType child) {
+ addChild(new SimpleTree<>(child));
+ }
+
+ @Override
+ public void addChild(final Tree<ContainedType> child) {
+ if (hasChildren == false) {
+ hasChildren = true;
+
+ children = new FunctionalList<>();
+ }
+
+ childCount++;
+
+ children.add(child);
+ }
+
+ @Override
+ public void prependChild(final Tree<ContainedType> child) {
+ if (hasChildren == false) {
+ hasChildren = true;
+
+ children = new FunctionalList<>();
+ }
+
+ childCount++;
+
+ children.prepend(child);
+ }
+
+ @Override
+ public void doForChildren(final Consumer<Tree<ContainedType>> action) {
+ if (childCount > 0) children.forEach(action);
+ }
+
+ @Override
+ public int getChildrenCount() {
+ return childCount;
+ }
+
+ @Override
+ public int revFind(final Predicate<Tree<ContainedType>> childPred) {
+ if (childCount == 0) return -1;
+
+ for (int i = childCount - 1; i >= 0; i--) {
+ if (childPred.test(getChild(i))) return i;
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void traverse(final TreeLinearizationMethod linearizationMethod,
+ final Consumer<ContainedType> action) {
+ if (hasChildren) {
+ switch (linearizationMethod) {
+ case INORDER:
+ if (childCount != 2) {
+ final String msg = "Can only do in-order traversal for binary trees.";
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ children.getByIndex(0).traverse(linearizationMethod, action);
+
+ action.accept(data);
+
+ children.getByIndex(1).traverse(linearizationMethod, action);
+ break;
+ case POSTORDER:
+ children.forEach(child -> child.traverse(linearizationMethod, action));
+
+ action.accept(data);
+ break;
+ case PREORDER:
+ action.accept(data);
+
+ children.forEach(child -> child.traverse(linearizationMethod, action));
+ break;
+ default:
+ break;
+
+ }
+ } else {
+ action.accept(data);
+ }
+ }
+
+ @Override
+ public <NewType, ReturnedType> ReturnedType collapse(
+ final Function<ContainedType, NewType> leafTransform,
+ final BiFunction<ContainedType, ListEx<NewType>, NewType> nodeCollapser,
+ final Function<NewType, ReturnedType> resultTransformer) {
+ return resultTransformer.apply(internalCollapse(leafTransform, nodeCollapser));
+ }
+
+ @Override
+ public Tree<ContainedType>
+ flatMapTree(final Function<ContainedType, Tree<ContainedType>> mapper) {
+ if (hasChildren) {
+ final Tree<ContainedType> flatMappedData = mapper.apply(data);
+
+ final ListEx<Tree<ContainedType>> mappedChildren
+ = children.map(child -> child.flatMapTree(mapper));
+
+ mappedChildren.forEach(flatMappedData::addChild);
+
+ return flatMappedData;
+ }
+
+ return mapper.apply(data);
+ }
+
+ /*
+ * Do a collapse of this tree.
+ */
+
+ private <NewType> NewType internalCollapse(
+ final Function<ContainedType, NewType> leafTransform,
+ final BiFunction<ContainedType, ListEx<NewType>, NewType> nodeCollapser) {
+ if (hasChildren) {
+ final ListEx<NewType> collapsedChildren = children.map(child -> {
+ final NewType collapsed = child.collapse(leafTransform, nodeCollapser,
+ subTreeVal -> subTreeVal);
+
+ return collapsed;
+ });
+
+ return nodeCollapser.apply(data, collapsedChildren);
+ }
+
+ return leafTransform.apply(data);
+ }
+
+ private void internalToString(final StringBuilder builder, final int indentLevel,
+ final boolean initial) {
+ if (!initial) {
+ for (int i = 0; i < indentLevel; i++) builder.append(">\t");
+ }
+
+ builder.append("Node #");
+ builder.append(ID);
+ builder.append(": ");
+ builder.append(data == null ? "(null)" : data.toString());
+ builder.append("\n");
+
+ if (hasChildren) {
+ children.forEach(child -> {
+ if (child instanceof SimpleTree<?>) {
+ final SimpleTree<ContainedType> kid = (SimpleTree<ContainedType>) child;
+
+ kid.internalToString(builder, indentLevel + 1, false);
+ } else {
+ for (int i = 0; i < indentLevel + 1; i++) {
+ builder.append(">\t");
+ }
+
+ builder.append("Unknown node of type ");
+ builder.append(child.getClass().getName());
+ builder.append("\n");
+ }
+ });
+ }
+ }
+
+ @Override
+ public <MappedType> Tree<MappedType> rebuildTree(
+ final Function<ContainedType, MappedType> leafTransformer,
+ final Function<ContainedType, MappedType> operatorTransformer) {
+ if (hasChildren) {
+ final ListEx<Tree<MappedType>> mappedChildren =
+ children.map(child ->
+ child.rebuildTree(leafTransformer, operatorTransformer));
+
+ final MappedType mapData = operatorTransformer.apply(data);
+ return new SimpleTree<>(mapData, mappedChildren);
+ }
+
+ return new SimpleTree<>(leafTransformer.apply(data));
+ }
+
+ @Override
+ public void selectiveTransform(final Predicate<ContainedType> nodePicker,
+ final UnaryOperator<ContainedType> transformer) {
+ if (hasChildren) {
+ children.forEach(child -> child.selectiveTransform(nodePicker, transformer));
+ } else {
+ data = transformer.apply(data);
+ }
+ }
+
+ @Override
+ public Tree<ContainedType> topDownTransform(
+ final Function<ContainedType, TopDownTransformResult> transformPicker,
+ final UnaryOperator<Tree<ContainedType>> transformer) {
+ final TopDownTransformResult transformResult = transformPicker.apply(data);
+
+ switch (transformResult) {
+ case PASSTHROUGH:
+ Tree<ContainedType> result = new SimpleTree<>(data);
+
+ if (hasChildren) {
+ children.forEach(child -> {
+ final Tree<ContainedType> kid
+ = child.topDownTransform(transformPicker, transformer);
+
+ result.addChild(kid);
+ });
+ }
+
+ return result;
+ case SKIP:
+ return this;
+ case TRANSFORM:
+ return transformer.apply(this);
+ case RTRANSFORM:
+ return transformer.apply(this).topDownTransform(transformPicker, transformer);
+ case PUSHDOWN:
+ result = new SimpleTree<>(data);
+
+ if (hasChildren) {
+ children.forEach(child -> {
+ final Tree<ContainedType> kid
+ = child.topDownTransform(transformPicker, transformer);
+
+ result.addChild(kid);
+ });
+ }
+
+ return transformer.apply(result);
+ case PULLUP:
+ final Tree<ContainedType> intermediateResult = transformer.apply(this);
+
+ result = new SimpleTree<>(intermediateResult.getHead());
+
+ intermediateResult.doForChildren(child -> {
+ final Tree<ContainedType> kid
+ = child.topDownTransform(transformPicker, transformer);
+
+ result.addChild(kid);
+ });
+
+ return result;
+ default:
+ final String msg = String.format("Recieved unknown transform result type %s",
+ transformResult);
+
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ @Override
+ public <TransformedType> TransformedType transformChild(final int childNo,
+ final Function<Tree<ContainedType>, TransformedType> transformer) {
+ if (childNo < 0 || childNo > childCount - 1) {
+ final String msg = String.format("Child index #%d is invalid", childNo);
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ final Tree<ContainedType> selectedKid = children.getByIndex(childNo);
+
+ return transformer.apply(selectedKid);
+ }
+
+ @Override
+ public Tree<ContainedType> getChild(final int childNo) {
+ if (childNo < 0 || childNo > childCount - 1) {
+ final String msg = String.format("Child index #%d is invalid", childNo);
+
+ throw new IllegalArgumentException(msg);
+ }
+ return children.getByIndex(childNo);
+ }
+
+ @Override
+ public <TransformedType> TransformedType
+ transformHead(final Function<ContainedType, TransformedType> transformer) {
+ return transformer.apply(data);
+ }
+
+ @Override
+ public void setHead(ContainedType dat) {
+ this.data = dat;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+
+ result = prime * result + childCount;
+ result = prime * result + (children == null ? 0 : children.hashCode());
+ result = prime * result + (data == null ? 0 : data.hashCode());
+
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+
+ internalToString(builder, 1, true);
+
+ /* Delete a trailing nl. */
+ builder.deleteCharAt(builder.length() - 1);
+
+ return builder.toString();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof SimpleTree<?>)) return false;
+
+ final SimpleTree<?> other = (SimpleTree<?>) obj;
+
+ if (data == null) {
+ if (other.data != null) return false;
+ } else if (!data.equals(other.data)) {
+ return false;
+ }
+
+ if (childCount != other.childCount) return false;
+
+ if (children == null) {
+ if (other.children != null) return false;
+ } else if (!children.equals(other.children)) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/bjc/data/TopDownTransformIterator.java b/src/main/java/bjc/data/TopDownTransformIterator.java
index 3b4b997..28df590 100644
--- a/src/main/java/bjc/data/TopDownTransformIterator.java
+++ b/src/main/java/bjc/data/TopDownTransformIterator.java
@@ -26,7 +26,7 @@ import java.util.function.Function;
* The type of the nodes in the tree.
*/
public class TopDownTransformIterator<ContainedType>
- implements Iterator<ITree<ContainedType>> {
+ implements Iterator<Tree<ContainedType>> {
/**
* Alias type for a tree transformation.
*
@@ -35,8 +35,8 @@ public class TopDownTransformIterator<ContainedType>
* @param <ContainedType>
* The type contained in the tree.
*/
- public interface TreeTransform<ContainedType> extends BiFunction<ITree<ContainedType>,
- Consumer<Iterator<ITree<ContainedType>>>, ITree<ContainedType>> {
+ public interface TreeTransform<ContainedType> extends BiFunction<Tree<ContainedType>,
+ Consumer<Iterator<Tree<ContainedType>>>, Tree<ContainedType>> {
// Alias type; no body is needed
}
@@ -49,19 +49,19 @@ public class TopDownTransformIterator<ContainedType>
*/
private final TreeTransform<ContainedType> transform;
- private ITree<ContainedType> preParent;
- private ITree<ContainedType> postParent;
+ private Tree<ContainedType> preParent;
+ private Tree<ContainedType> postParent;
- private final Deque<ITree<ContainedType>> preChildren;
- private final Deque<ITree<ContainedType>> postChildren;
+ private final Deque<Tree<ContainedType>> preChildren;
+ private final Deque<Tree<ContainedType>> postChildren;
private TopDownTransformIterator<ContainedType> curChild;
private boolean done;
private boolean initial;
- private final Deque<Iterator<ITree<ContainedType>>> toYield;
- private Iterator<ITree<ContainedType>> currYield;
+ private final Deque<Iterator<Tree<ContainedType>>> toYield;
+ private Iterator<Tree<ContainedType>> currYield;
/**
* Create a new tree iterator.
@@ -76,7 +76,7 @@ public class TopDownTransformIterator<ContainedType>
public TopDownTransformIterator(
final Function<ContainedType, TopDownTransformResult> pickr,
final TreeTransform<ContainedType> transfrm,
- final ITree<ContainedType> tree) {
+ final Tree<ContainedType> tree) {
preParent = tree;
preChildren = new LinkedList<>();
@@ -96,10 +96,8 @@ public class TopDownTransformIterator<ContainedType>
* @param src
* The nodes to yield.
*/
- public void addYield(final Iterator<ITree<ContainedType>> src) {
- if (currYield != null) {
- toYield.push(currYield);
- }
+ public void addYield(final Iterator<Tree<ContainedType>> src) {
+ if (currYield != null) toYield.push(currYield);
currYield = src;
}
@@ -117,7 +115,7 @@ public class TopDownTransformIterator<ContainedType>
*
* @return The next yielded value.
*/
- public ITree<ContainedType> flushYields(final ITree<ContainedType> val) {
+ public Tree<ContainedType> flushYields(final Tree<ContainedType> val) {
if (currYield != null) {
/*
* We have non-sentinel values to yield.
@@ -128,9 +126,7 @@ public class TopDownTransformIterator<ContainedType>
*/
toYield.add(new SingleIterator<>(val));
- if (currYield.hasNext()) {
- return currYield.next();
- }
+ if (currYield.hasNext()) return currYield.next();
while (toYield.size() != 0 && !currYield.hasNext()) {
currYield = toYield.pop();
@@ -148,18 +144,16 @@ public class TopDownTransformIterator<ContainedType>
}
@Override
- public ITree<ContainedType> next() {
- if (done)
- throw new NoSuchElementException();
+ public Tree<ContainedType> next() {
+ if (done) throw new NoSuchElementException();
/*
* Flush any values that need to be yielded.
*/
if (currYield != null) {
- ITree<ContainedType> yeld = flushYields(null);
+ Tree<ContainedType> yeld = flushYields(null);
- if (yeld != null)
- return yeld;
+ if (yeld != null) return yeld;
}
if (initial) {
@@ -170,7 +164,7 @@ public class TopDownTransformIterator<ContainedType>
switch (res) {
case PASSTHROUGH:
- postParent = new Tree<>(preParent.getHead());
+ postParent = new SimpleTree<>(preParent.getHead());
if (preParent.getChildrenCount() != 0) {
for (int i = 0; i < preParent.getChildrenCount(); i++) {
@@ -204,12 +198,12 @@ public class TopDownTransformIterator<ContainedType>
done = true;
return flushYields(
- transform.apply(new Tree<>(preParent.getHead()), this::addYield));
+ transform.apply(new SimpleTree<>(preParent.getHead()), this::addYield));
case PULLUP:
- final ITree<ContainedType> intRes
+ final Tree<ContainedType> intRes
= transform.apply(preParent, this::addYield);
- postParent = new Tree<>(intRes.getHead());
+ postParent = new SimpleTree<>(intRes.getHead());
if (intRes.getChildrenCount() != 0) {
for (int i = 0; i < intRes.getChildrenCount(); i++) {
@@ -226,9 +220,7 @@ public class TopDownTransformIterator<ContainedType>
throw new IllegalArgumentException("Unknown result type " + res);
}
- if (res != RTRANSFORM) {
- initial = false;
- }
+ if (res != RTRANSFORM) initial = false;
}
if (curChild == null || !curChild.hasNext()) {
@@ -236,22 +228,22 @@ public class TopDownTransformIterator<ContainedType>
curChild = new TopDownTransformIterator<>(picker, transform,
preChildren.pop());
- final ITree<ContainedType> res = curChild.next();
+ final Tree<ContainedType> res = curChild.next();
// System.out.println("\t\tTRACE: adding node " + res + " to children");
postChildren.add(res);
return flushYields(res);
}
- ITree<ContainedType> res = null;
+ Tree<ContainedType> res = null;
if (postParent == null) {
- res = new Tree<>(preParent.getHead());
+ res = new SimpleTree<>(preParent.getHead());
// System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " +
// res);
- for (final ITree<ContainedType> child : postChildren) {
+ for (final Tree<ContainedType> child : postChildren) {
res.addChild(child);
}
@@ -262,7 +254,7 @@ public class TopDownTransformIterator<ContainedType>
// System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " +
// res);
- for (final ITree<ContainedType> child : postChildren) {
+ for (final Tree<ContainedType> child : postChildren) {
res.addChild(child);
}
}
@@ -271,7 +263,7 @@ public class TopDownTransformIterator<ContainedType>
return flushYields(res);
}
- final ITree<ContainedType> res = curChild.next();
+ final Tree<ContainedType> res = curChild.next();
// System.out.println("\t\tTRACE: adding node " + res + " to children");
postChildren.add(res);
diff --git a/src/main/java/bjc/data/Tree.java b/src/main/java/bjc/data/Tree.java
index 9a4caa6..3e16e02 100644
--- a/src/main/java/bjc/data/Tree.java
+++ b/src/main/java/bjc/data/Tree.java
@@ -6,441 +6,281 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
-import bjc.funcdata.FunctionalList;
-import bjc.funcdata.IList;
+import bjc.funcdata.ListEx;
import bjc.funcdata.bst.TreeLinearizationMethod;
/**
- * A node in a homogeneous tree.
+ * A node in a homogeneous tree with a unlimited amount of children.
*
* @author ben
*
* @param <ContainedType>
- * The type contained in the tree.
+ * The type of data contained in the tree nodes.
+ *
*/
-public class Tree<ContainedType> implements ITree<ContainedType> {
- /* The data/label for this node. */
- private ContainedType data;
-
- /* The children of this node. */
- private IList<ITree<ContainedType>> children;
-
- /* Whether this node has children. */
- /*
- * @NOTE Why have both this boolean and childCount? Why not just do a childCount
- * == 0 whenever you'd check hasChildren? - Because hasChildren is set once and
- * not reset, and really what it indicates is that children has been allocated.
+public interface Tree<ContainedType> {
+ /**
+ * Append a child to this node.
+ *
+ * @param child
+ * The child to append to this node.
*/
- private boolean hasChildren;
- /* The number of children this node has. */
- private int childCount = 0;
-
- /* The ID of this node. */
- private int ID;
- /* The next ID to assign to a node. */
- private static int nextID = 0;
+ void addChild(Tree<ContainedType> child);
/**
- * Create a new leaf node in a tree.
+ * Append a child to this node.
+ *
+ * @param child
+ * The child to append to this node.
*/
- public Tree() {
- this(null);
- }
+ void addChild(ContainedType child);
/**
- * Create a new leaf node in a tree.
+ * Prepend a child to this node.
*
- * @param leaf
- * The data to store as a leaf node.
+ * @param child
+ * The child to prepend to this node.
*/
- public Tree(final ContainedType leaf) {
- data = leaf;
-
- hasChildren = false;
-
- ID = nextID++;
- }
+ void prependChild(Tree<ContainedType> child);
/**
- * Create a new tree node with the specified children.
+ * Collapse a tree into a single version.
+ *
+ * @param <NewType>
+ * The intermediate type being folded.
+ *
+ * @param <ReturnedType>
+ * The type that is the end result.
+ *
+ * @param leafTransform
+ * The function to use to convert leaf values.
+ *
+ * @param nodeCollapser
+ * The function to use to convert internal nodes and
+ * their children.
*
- * @param leaf
- * The data to hold in this node.
+ * @param resultTransformer
+ * The function to use to convert a state to the
+ * returned version.
*
- * @param childrn
- * A list of children for this node.
+ * @return The final transformed state.
*/
- public Tree(final ContainedType leaf, final IList<ITree<ContainedType>> childrn) {
- this(leaf);
+ <NewType, ReturnedType> ReturnedType collapse(
+ Function<ContainedType, NewType> leafTransform,
+ BiFunction<ContainedType, ListEx<NewType>, NewType> nodeCollapser,
+ Function<NewType, ReturnedType> resultTransformer);
- hasChildren = true;
-
- childCount = childrn.getSize();
-
- children = childrn;
- }
+ /**
+ * Execute a given action for each of this tree's children.
+ *
+ * @param action
+ * The action to execute for each child.
+ */
+ void doForChildren(Consumer<Tree<ContainedType>> action);
/**
- * Create a new tree node with the specified children.
+ * Expand the nodes of a tree into trees, and then merge the contents of those
+ * trees into a single tree.
*
- * @param leaf
- * The data to hold in this node.
+ * @param mapper
+ * The function to use to map values into trees.
*
- * @param childrn
- * A list of children for this node.
+ * @return A tree, with some nodes expanded into trees.
*/
- @SafeVarargs
- public Tree(final ContainedType leaf, final ITree<ContainedType>... childrn) {
- this(leaf);
-
- hasChildren = true;
-
- childCount = 0;
-
- children = new FunctionalList<>();
-
- for (final ITree<ContainedType> child : childrn) {
- children.add(child);
-
- childCount++;
- }
- }
-
- @Override
- public void addChild(final ContainedType child) {
- addChild(new Tree<>(child));
- }
-
- @Override
- public void addChild(final ITree<ContainedType> child) {
- if (hasChildren == false) {
- hasChildren = true;
+ default Tree<ContainedType>
+ flatMapTree(final Function<ContainedType, Tree<ContainedType>> mapper) {
+ return topDownTransform(dat -> TopDownTransformResult.PUSHDOWN, node -> {
+ if (node.getChildrenCount() > 0) {
+ final Tree<ContainedType> parent = node.transformHead(mapper);
- children = new FunctionalList<>();
- }
-
- childCount++;
-
- children.add(child);
- }
-
- @Override
- public void prependChild(final ITree<ContainedType> child) {
- if (hasChildren == false) {
- hasChildren = true;
-
- children = new FunctionalList<>();
- }
-
- childCount++;
-
- children.prepend(child);
- }
-
- @Override
- public void doForChildren(final Consumer<ITree<ContainedType>> action) {
- if (childCount > 0) {
- children.forEach(action);
- }
- }
-
- @Override
- public int getChildrenCount() {
- return childCount;
- }
-
- @Override
- public int revFind(final Predicate<ITree<ContainedType>> childPred) {
- if (childCount == 0) {
- return -1;
- }
-
- for (int i = childCount - 1; i >= 0; i--) {
- if (childPred.test(getChild(i)))
- return i;
- }
-
- return -1;
- }
-
- @Override
- public void traverse(final TreeLinearizationMethod linearizationMethod,
- final Consumer<ContainedType> action) {
- if (hasChildren) {
- switch (linearizationMethod) {
- case INORDER:
- if (childCount != 2) {
- final String msg = "Can only do in-order traversal for binary trees.";
-
- throw new IllegalArgumentException(msg);
- }
-
- children.getByIndex(0).traverse(linearizationMethod, action);
-
- action.accept(data);
-
- children.getByIndex(1).traverse(linearizationMethod, action);
- break;
- case POSTORDER:
- children.forEach(child -> child.traverse(linearizationMethod, action));
-
- action.accept(data);
- break;
- case PREORDER:
- action.accept(data);
-
- children.forEach(child -> child.traverse(linearizationMethod, action));
- break;
- default:
- break;
+ node.doForChildren(parent::addChild);
+ return parent;
}
- } else {
- action.accept(data);
- }
- }
-
- @Override
- public <NewType, ReturnedType> ReturnedType collapse(
- final Function<ContainedType, NewType> leafTransform,
- final BiFunction<ContainedType, IList<NewType>, NewType> nodeCollapser,
- final Function<NewType, ReturnedType> resultTransformer) {
- return resultTransformer.apply(internalCollapse(leafTransform, nodeCollapser));
- }
-
- @Override
- public ITree<ContainedType>
- flatMapTree(final Function<ContainedType, ITree<ContainedType>> mapper) {
- if (hasChildren) {
- final ITree<ContainedType> flatMappedData = mapper.apply(data);
-
- final IList<ITree<ContainedType>> mappedChildren
- = children.map(child -> child.flatMapTree(mapper));
-
- mappedChildren.forEach(flatMappedData::addChild);
-
- return flatMappedData;
- }
- return mapper.apply(data);
+ return node.transformHead(mapper);
+ });
}
- /*
- * Do a collapse of this tree.
+ /**
+ * Get the specified child of this tree.
+ *
+ * @param childNo
+ * The number of the child to get.
*
- * @NOTE Why is this protected? I can't see any good reason someone'd want to
- * override it.
+ * @return The specified child of this tree.
*/
-
- private <NewType> NewType internalCollapse(
- final Function<ContainedType, NewType> leafTransform,
- final BiFunction<ContainedType, IList<NewType>, NewType> nodeCollapser) {
- if (hasChildren) {
- final IList<NewType> collapsedChildren = children.map(child -> {
- final NewType collapsed = child.collapse(leafTransform, nodeCollapser,
- subTreeVal -> subTreeVal);
-
- return collapsed;
- });
-
- return nodeCollapser.apply(data, collapsedChildren);
- }
-
- return leafTransform.apply(data);
+ default Tree<ContainedType> getChild(final int childNo) {
+ return transformChild(childNo, child -> child);
}
- private void internalToString(final StringBuilder builder, final int indentLevel,
- final boolean initial) {
- if (!initial) {
- for (int i = 0; i < indentLevel; i++) {
- builder.append(">\t");
- }
- }
-
- builder.append("Node #");
- builder.append(ID);
- builder.append(": ");
- builder.append(data == null ? "(null)" : data.toString());
- builder.append("\n");
-
- if (hasChildren) {
- children.forEach(child -> {
- if (child instanceof Tree<?>) {
- final Tree<ContainedType> kid = (Tree<ContainedType>) child;
-
- kid.internalToString(builder, indentLevel + 1, false);
- } else {
- for (int i = 0; i < indentLevel + 1; i++) {
- builder.append(">\t");
- }
-
- builder.append("Unknown node of type ");
- builder.append(child.getClass().getName());
- builder.append("\n");
- }
- });
- }
- }
-
- @Override
- public <MappedType> ITree<MappedType> rebuildTree(
- final Function<ContainedType, MappedType> leafTransformer,
- final Function<ContainedType, MappedType> operatorTransformer) {
- if (hasChildren) {
- final IList<ITree<MappedType>> mappedChildren = children.map(child -> child.rebuildTree(leafTransformer, operatorTransformer));
-
- final MappedType mapData = operatorTransformer.apply(data);
- return new Tree<>(mapData, mappedChildren);
- }
-
- return new Tree<>(leafTransformer.apply(data));
- }
+ /**
+ * Get a count of the number of direct children this node has.
+ *
+ * @return The number of direct children this node has.
+ */
+ int getChildrenCount();
- @Override
- public void selectiveTransform(final Predicate<ContainedType> nodePicker,
- final UnaryOperator<ContainedType> transformer) {
- if (hasChildren) {
- children.forEach(child -> child.selectiveTransform(nodePicker, transformer));
- } else {
- data = transformer.apply(data);
- }
+ /**
+ * Get a count of the number of direct children this node has.
+ *
+ * @return The number of direct children this node has.
+ */
+ default int size() {
+ return getChildrenCount();
}
- @Override
- public ITree<ContainedType> topDownTransform(
- final Function<ContainedType, TopDownTransformResult> transformPicker,
- final UnaryOperator<ITree<ContainedType>> transformer) {
- final TopDownTransformResult transformResult = transformPicker.apply(data);
-
- switch (transformResult) {
- case PASSTHROUGH:
- ITree<ContainedType> result = new Tree<>(data);
-
- if (hasChildren) {
- children.forEach(child -> {
- final ITree<ContainedType> kid
- = child.topDownTransform(transformPicker, transformer);
-
- result.addChild(kid);
- });
- }
-
- return result;
- case SKIP:
- return this;
- case TRANSFORM:
- return transformer.apply(this);
- case RTRANSFORM:
- return transformer.apply(this).topDownTransform(transformPicker, transformer);
- case PUSHDOWN:
- result = new Tree<>(data);
-
- if (hasChildren) {
- children.forEach(child -> {
- final ITree<ContainedType> kid
- = child.topDownTransform(transformPicker, transformer);
-
- result.addChild(kid);
- });
- }
-
- return transformer.apply(result);
- case PULLUP:
- final ITree<ContainedType> intermediateResult = transformer.apply(this);
-
- result = new Tree<>(intermediateResult.getHead());
-
- intermediateResult.doForChildren(child -> {
- final ITree<ContainedType> kid
- = child.topDownTransform(transformPicker, transformer);
-
- result.addChild(kid);
- });
-
- return result;
- default:
- final String msg = String.format("Recieved unknown transform result type %s",
- transformResult);
-
- throw new IllegalArgumentException(msg);
- }
+ /**
+ * Get the data stored in this node.
+ *
+ * @return The data stored in this node.
+ */
+ default ContainedType getHead() {
+ return transformHead(head -> head);
}
- @Override
- public <TransformedType> TransformedType transformChild(final int childNo,
- final Function<ITree<ContainedType>, TransformedType> transformer) {
- if (childNo < 0 || childNo > childCount - 1) {
- final String msg = String.format("Child index #%d is invalid", childNo);
+ /**
+ * Rebuild the tree with the same structure, but different nodes.
+ *
+ * @param <MappedType>
+ * The type of the new tree.
+ *
+ * @param leafTransformer
+ * The function to use to transform leaf tokens.
+ *
+ * @param internalTransformer
+ * The function to use to transform internal tokens.
+ *
+ * @return The tree, with the nodes changed.
+ */
+ <MappedType> Tree<MappedType> rebuildTree(
+ Function<ContainedType, MappedType> leafTransformer,
+ Function<ContainedType, MappedType> internalTransformer);
- throw new IllegalArgumentException(msg);
- }
+ /**
+ * Transform some of the nodes in this tree.
+ *
+ * @param nodePicker
+ * The predicate to use to pick nodes to transform.
+ *
+ * @param transformer
+ * The function to use to transform picked nodes.
+ */
+ void selectiveTransform(Predicate<ContainedType> nodePicker,
+ UnaryOperator<ContainedType> transformer);
- final ITree<ContainedType> selectedKid = children.getByIndex(childNo);
+ /**
+ * Do a top-down transform of the tree.
+ *
+ * @param transformPicker
+ * The function to use to pick how to progress.
+ *
+ * @param transformer
+ * The function used to transform picked subtrees.
+ *
+ * @return The tree with the transform applied to picked subtrees.
+ */
+ Tree<ContainedType> topDownTransform(
+ Function<ContainedType, TopDownTransformResult> transformPicker,
+ UnaryOperator<Tree<ContainedType>> transformer);
- return transformer.apply(selectedKid);
- }
+ /**
+ * Transform one of this nodes children.
+ *
+ * @param <TransformedType>
+ * The type of the transformed value.
+ *
+ * @param childNo
+ * The number of the child to transform.
+ *
+ * @param transformer
+ * The function to use to transform the value.
+ *
+ * @return The transformed value.
+ *
+ * @throws IllegalArgumentException
+ * if the childNo is out of bounds (0 &lt;=
+ * childNo &lt;= childCount()).
+ */
+ <TransformedType> TransformedType transformChild(int childNo,
+ Function<Tree<ContainedType>, TransformedType> transformer);
- @Override
- public <TransformedType> TransformedType
- transformHead(final Function<ContainedType, TransformedType> transformer) {
- return transformer.apply(data);
- }
+ /**
+ * Transform the value that is the head of this node.
+ *
+ * @param <TransformedType>
+ * The type of the transformed value.
+ *
+ * @param transformer
+ * The function to use to transform the value.
+ *
+ * @return The transformed value.
+ */
+ <TransformedType> TransformedType
+ transformHead(Function<ContainedType, TransformedType> transformer);
- @Override
- public void setHead(ContainedType dat) {
- this.data = dat;
+ /**
+ * Transform the tree into a tree with a different type of token.
+ *
+ * @param <MappedType>
+ * The type of the new tree.
+ *
+ * @param transformer
+ * The function to use to transform tokens.
+ *
+ * @return A tree with the token types transformed.
+ */
+ default <MappedType> Tree<MappedType>
+ transformTree(final Function<ContainedType, MappedType> transformer) {
+ return rebuildTree(transformer, transformer);
}
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
-
- result = prime * result + childCount;
- result = prime * result + (children == null ? 0 : children.hashCode());
- result = prime * result + (data == null ? 0 : data.hashCode());
-
- return result;
- }
+ /**
+ * Perform an action on each part of the tree.
+ *
+ * @param linearizationMethod
+ * The way to traverse the tree.
+ *
+ * @param action
+ * The action to perform on each tree node.
+ */
+ void traverse(TreeLinearizationMethod linearizationMethod,
+ Consumer<ContainedType> action);
- @Override
- public String toString() {
- final StringBuilder builder = new StringBuilder();
+ /**
+ * Find the farthest to right child that satisfies the given predicate.
+ *
+ * @param childPred
+ * The predicate to satisfy.
+ *
+ * @return The index of the right-most child that satisfies the predicate, or -1
+ * if one doesn't exist.
+ */
+ int revFind(Predicate<Tree<ContainedType>> childPred);
- internalToString(builder, 1, true);
+ /**
+ * Check if this tree contains any nodes that satisfy the predicate.
+ *
+ * @param pred
+ * The predicate to look for.
+ *
+ * @return Whether or not any items satisfied the predicate.
+ */
+ default boolean containsMatching(Predicate<ContainedType> pred) {
+ Toggle<Boolean> tog = new OneWayToggle<>(false, true);
- /* Delete a trailing nl. */
- builder.deleteCharAt(builder.length() - 1);
+ traverse(TreeLinearizationMethod.POSTORDER, val -> {
+ if (pred.test(val)) tog.get();
+ });
- return builder.toString();
+ return tog.get();
}
- @Override
- public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof Tree<?>))
- return false;
-
- final Tree<?> other = (Tree<?>) obj;
-
- if (data == null) {
- if (other.data != null)
- return false;
- } else if (!data.equals(other.data))
- return false;
-
- if (childCount != other.childCount)
- return false;
-
- if (children == null) {
- if (other.children != null)
- return false;
- } else if (!children.equals(other.children))
- return false;
-
- return true;
- }
+ /**
+ * Set the head of the tree.
+ *
+ * @param dat
+ * The value to set as the head of the tree.
+ */
+ void setHead(ContainedType dat);
}
diff --git a/src/main/java/bjc/data/ValueToggle.java b/src/main/java/bjc/data/ValueToggle.java
index 041a2d5..9e4b83b 100644
--- a/src/main/java/bjc/data/ValueToggle.java
+++ b/src/main/java/bjc/data/ValueToggle.java
@@ -37,20 +37,14 @@ public class ValueToggle<E> implements Toggle<E> {
@Override
public E get() {
- if (alignment.get()) {
- return lft;
- }
-
- return rght;
+ if (alignment.get()) return lft;
+ else return rght;
}
@Override
public E peek() {
- if (alignment.peek()) {
- return lft;
- }
-
- return rght;
+ if (alignment.peek()) return lft;
+ else return rght;
}
@Override
diff --git a/src/main/java/bjc/data/internals/BoundLazy.java b/src/main/java/bjc/data/internals/BoundLazy.java
index a350a2b..0e0e95c 100644
--- a/src/main/java/bjc/data/internals/BoundLazy.java
+++ b/src/main/java/bjc/data/internals/BoundLazy.java
@@ -4,10 +4,10 @@ import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
-import bjc.data.IHolder;
+import bjc.data.Holder;
import bjc.data.Lazy;
import bjc.funcdata.FunctionalList;
-import bjc.funcdata.IList;
+import bjc.funcdata.ListEx;
/**
* Implements a lazy holder that has been bound.
@@ -19,21 +19,21 @@ import bjc.funcdata.IList;
* The type of the new bound value.
*/
public class BoundLazy<OldType, BoundContainedType>
- implements IHolder<BoundContainedType> {
+ implements Holder<BoundContainedType> {
/* The old value. */
- private final Supplier<IHolder<OldType>> oldSupplier;
+ private final Supplier<Holder<OldType>> oldSupplier;
/* The function to use to transform the old value into a new value. */
- private final Function<OldType, IHolder<BoundContainedType>> binder;
+ private final Function<OldType, Holder<BoundContainedType>> binder;
/* The bound value being held. */
- private IHolder<BoundContainedType> boundHolder;
+ private Holder<BoundContainedType> boundHolder;
/* Whether the bound value has been actualized or not. */
private boolean holderBound;
/* Transformations currently pending on the bound value. */
- private final IList<UnaryOperator<BoundContainedType>> actions
+ private final ListEx<UnaryOperator<BoundContainedType>> actions
= new FunctionalList<>();
/**
@@ -45,20 +45,20 @@ public class BoundLazy<OldType, BoundContainedType>
* @param binder
* The function to use to bind the old value to the new one.
*/
- public BoundLazy(final Supplier<IHolder<OldType>> supp,
- final Function<OldType, IHolder<BoundContainedType>> binder) {
+ public BoundLazy(final Supplier<Holder<OldType>> supp,
+ final Function<OldType, Holder<BoundContainedType>> binder) {
oldSupplier = supp;
this.binder = binder;
}
@Override
- public <BoundType> IHolder<BoundType>
- bind(final Function<BoundContainedType, IHolder<BoundType>> bindr) {
+ public <BoundType> Holder<BoundType>
+ bind(final Function<BoundContainedType, Holder<BoundType>> bindr) {
if (bindr == null)
throw new NullPointerException("Binder must not be null");
/* Prepare a list of pending actions. */
- final IList<UnaryOperator<BoundContainedType>> pendingActions
+ final ListEx<UnaryOperator<BoundContainedType>> pendingActions
= new FunctionalList<>();
for (UnaryOperator<BoundContainedType> pendAct : actions) {
@@ -66,8 +66,8 @@ public class BoundLazy<OldType, BoundContainedType>
}
/* Create the new supplier of a value. */
- final Supplier<IHolder<BoundContainedType>> typeSupplier = () -> {
- IHolder<BoundContainedType> oldHolder = boundHolder;
+ final Supplier<Holder<BoundContainedType>> typeSupplier = () -> {
+ Holder<BoundContainedType> oldHolder = boundHolder;
/* Bind the value if it hasn't been bound before. */
if (!holderBound) {
@@ -83,7 +83,7 @@ public class BoundLazy<OldType, BoundContainedType>
}
@Override
- public <NewType> Function<BoundContainedType, IHolder<NewType>>
+ public <NewType> Function<BoundContainedType, Holder<NewType>>
lift(final Function<BoundContainedType, NewType> func) {
if (func == null)
throw new NullPointerException("Function to lift must not be null");
@@ -94,13 +94,13 @@ public class BoundLazy<OldType, BoundContainedType>
}
@Override
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(final Function<BoundContainedType, MappedType> mapper) {
if (mapper == null)
throw new NullPointerException("Mapper must not be null");
/* Prepare a list of pending actions. */
- final IList<UnaryOperator<BoundContainedType>> pendingActions
+ final ListEx<UnaryOperator<BoundContainedType>> pendingActions
= new FunctionalList<>();
for (UnaryOperator<BoundContainedType> pendAct : actions) {
@@ -109,7 +109,7 @@ public class BoundLazy<OldType, BoundContainedType>
/* Prepare the new supplier. */
final Supplier<MappedType> typeSupplier = () -> {
- IHolder<BoundContainedType> oldHolder = boundHolder;
+ Holder<BoundContainedType> oldHolder = boundHolder;
/* Bound the value if it hasn't been bound. */
if (!holderBound) {
@@ -133,7 +133,7 @@ public class BoundLazy<OldType, BoundContainedType>
}
@Override
- public IHolder<BoundContainedType>
+ public Holder<BoundContainedType>
transform(final UnaryOperator<BoundContainedType> transformer) {
if (transformer == null)
throw new NullPointerException("Transformer must not be null");
diff --git a/src/main/java/bjc/data/internals/BoundLazyPair.java b/src/main/java/bjc/data/internals/BoundLazyPair.java
index e081c04..91981de 100644
--- a/src/main/java/bjc/data/internals/BoundLazyPair.java
+++ b/src/main/java/bjc/data/internals/BoundLazyPair.java
@@ -4,8 +4,8 @@ import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
-import bjc.data.IHolder;
-import bjc.data.IPair;
+import bjc.data.Holder;
+import bjc.data.Pair;
import bjc.data.Identity;
import bjc.data.LazyPair;
@@ -16,17 +16,17 @@ import bjc.data.LazyPair;
*/
@SuppressWarnings("javadoc")
public class BoundLazyPair<OldLeft, OldRight, NewLeft, NewRight>
- implements IPair<NewLeft, NewRight> {
+ implements Pair<NewLeft, NewRight> {
/* The supplier of the left value. */
private final Supplier<OldLeft> leftSupplier;
/* The supplier of the right value. */
private final Supplier<OldRight> rightSupplier;
/* The binder to transform values. */
- private final BiFunction<OldLeft, OldRight, IPair<NewLeft, NewRight>> binder;
+ private final BiFunction<OldLeft, OldRight, Pair<NewLeft, NewRight>> binder;
/* The bound pair. */
- private IPair<NewLeft, NewRight> boundPair;
+ private Pair<NewLeft, NewRight> boundPair;
/* Whether the pair has been bound yet. */
private boolean pairBound;
@@ -46,20 +46,20 @@ public class BoundLazyPair<OldLeft, OldRight, NewLeft, NewRight>
*/
public BoundLazyPair(final Supplier<OldLeft> leftSupp,
final Supplier<OldRight> rightSupp,
- final BiFunction<OldLeft, OldRight, IPair<NewLeft, NewRight>> bindr) {
+ final BiFunction<OldLeft, OldRight, Pair<NewLeft, NewRight>> bindr) {
leftSupplier = leftSupp;
rightSupplier = rightSupp;
binder = bindr;
}
@Override
- public <BoundLeft, BoundRight> IPair<BoundLeft, BoundRight> bind(
- final BiFunction<NewLeft, NewRight, IPair<BoundLeft, BoundRight>> bindr) {
+ public <BoundLeft, BoundRight> Pair<BoundLeft, BoundRight> bind(
+ final BiFunction<NewLeft, NewRight, Pair<BoundLeft, BoundRight>> bindr) {
if (bindr == null)
throw new NullPointerException("Binder must not be null");
- final IHolder<IPair<NewLeft, NewRight>> newPair = new Identity<>(boundPair);
- final IHolder<Boolean> newPairMade = new Identity<>(pairBound);
+ final Holder<Pair<NewLeft, NewRight>> newPair = new Identity<>(boundPair);
+ final Holder<Boolean> newPairMade = new Identity<>(pairBound);
final Supplier<NewLeft> leftSupp = () -> {
if (!newPairMade.getValue()) {
@@ -91,13 +91,13 @@ public class BoundLazyPair<OldLeft, OldRight, NewLeft, NewRight>
}
@Override
- public <BoundLeft> IPair<BoundLeft, NewRight>
- bindLeft(final Function<NewLeft, IPair<BoundLeft, NewRight>> leftBinder) {
+ public <BoundLeft> Pair<BoundLeft, NewRight>
+ bindLeft(final Function<NewLeft, Pair<BoundLeft, NewRight>> leftBinder) {
if (leftBinder == null)
throw new NullPointerException("Left binder must not be null");
final Supplier<NewLeft> leftSupp = () -> {
- IPair<NewLeft, NewRight> newPair = boundPair;
+ Pair<NewLeft, NewRight> newPair = boundPair;
if (!pairBound) {
/*
@@ -113,13 +113,13 @@ public class BoundLazyPair<OldLeft, OldRight, NewLeft, NewRight>
}
@Override
- public <BoundRight> IPair<NewLeft, BoundRight>
- bindRight(final Function<NewRight, IPair<NewLeft, BoundRight>> rightBinder) {
+ public <BoundRight> Pair<NewLeft, BoundRight>
+ bindRight(final Function<NewRight, Pair<NewLeft, BoundRight>> rightBinder) {
if (rightBinder == null)
throw new NullPointerException("Right binder must not be null");
final Supplier<NewRight> rightSupp = () -> {
- IPair<NewLeft, NewRight> newPair = boundPair;
+ Pair<NewLeft, NewRight> newPair = boundPair;
if (!pairBound) {
/*
@@ -136,8 +136,8 @@ public class BoundLazyPair<OldLeft, OldRight, NewLeft, NewRight>
@Override
public <OtherLeft, OtherRight, CombinedLeft, CombinedRight>
- IPair<CombinedLeft, CombinedRight>
- combine(final IPair<OtherLeft, OtherRight> otherPair,
+ Pair<CombinedLeft, CombinedRight>
+ combine(final Pair<OtherLeft, OtherRight> otherPair,
final BiFunction<NewLeft, OtherLeft, CombinedLeft> leftCombiner,
final BiFunction<NewRight, OtherRight, CombinedRight> rightCombiner) {
if (otherPair == null) {
@@ -157,7 +157,7 @@ public class BoundLazyPair<OldLeft, OldRight, NewLeft, NewRight>
}
@Override
- public <NewLeftType> IPair<NewLeftType, NewRight>
+ public <NewLeftType> Pair<NewLeftType, NewRight>
mapLeft(final Function<NewLeft, NewLeftType> mapper) {
if (mapper == null)
throw new NullPointerException("Mapper must not be null");
@@ -184,7 +184,7 @@ public class BoundLazyPair<OldLeft, OldRight, NewLeft, NewRight>
}
@Override
- public <NewRightType> IPair<NewLeft, NewRightType>
+ public <NewRightType> Pair<NewLeft, NewRightType>
mapRight(final Function<NewRight, NewRightType> mapper) {
if (mapper == null)
throw new NullPointerException("Mapper must not be null");
diff --git a/src/main/java/bjc/data/internals/BoundListHolder.java b/src/main/java/bjc/data/internals/BoundListHolder.java
index 1193c8d..c944252 100644
--- a/src/main/java/bjc/data/internals/BoundListHolder.java
+++ b/src/main/java/bjc/data/internals/BoundListHolder.java
@@ -3,9 +3,9 @@ package bjc.data.internals;
import java.util.function.Function;
import java.util.function.UnaryOperator;
-import bjc.data.IHolder;
+import bjc.data.Holder;
import bjc.data.ListHolder;
-import bjc.funcdata.IList;
+import bjc.funcdata.ListEx;
/**
* Holds a list, converted into a holder.
@@ -13,9 +13,9 @@ import bjc.funcdata.IList;
* @author Ben Culkin
*/
@SuppressWarnings("javadoc")
-public class BoundListHolder<ContainedType> implements IHolder<ContainedType> {
+public class BoundListHolder<ContainedType> implements Holder<ContainedType> {
/* The list of contained holders. */
- private final IList<IHolder<ContainedType>> heldHolders;
+ private final ListEx<Holder<ContainedType>> heldHolders;
/**
* Create a new list of holders.
@@ -23,17 +23,17 @@ public class BoundListHolder<ContainedType> implements IHolder<ContainedType> {
* @param toHold
* The list of holders to, well, hold.
*/
- public BoundListHolder(final IList<IHolder<ContainedType>> toHold) {
+ public BoundListHolder(final ListEx<Holder<ContainedType>> toHold) {
heldHolders = toHold;
}
@Override
- public <BoundType> IHolder<BoundType>
- bind(final Function<ContainedType, IHolder<BoundType>> binder) {
+ public <BoundType> Holder<BoundType>
+ bind(final Function<ContainedType, Holder<BoundType>> binder) {
if (binder == null)
throw new NullPointerException("Binder must not be null");
- final IList<IHolder<BoundType>> boundHolders
+ final ListEx<Holder<BoundType>> boundHolders
= heldHolders.map(containedHolder -> {
return containedHolder.bind(binder);
});
@@ -42,7 +42,7 @@ public class BoundListHolder<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public <NewType> Function<ContainedType, IHolder<NewType>>
+ public <NewType> Function<ContainedType, Holder<NewType>>
lift(final Function<ContainedType, NewType> func) {
if (func == null)
throw new NullPointerException("Function to lift must not be null");
@@ -53,12 +53,12 @@ public class BoundListHolder<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(final Function<ContainedType, MappedType> mapper) {
if (mapper == null)
throw new NullPointerException("Mapper must not be null");
- final IList<IHolder<MappedType>> mappedHolders
+ final ListEx<Holder<MappedType>> mappedHolders
= heldHolders.map(containedHolder -> {
return containedHolder.map(mapper);
});
@@ -67,7 +67,7 @@ public class BoundListHolder<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public IHolder<ContainedType>
+ public Holder<ContainedType>
transform(final UnaryOperator<ContainedType> transformer) {
if (transformer == null)
throw new NullPointerException("Transformer must not be null");
diff --git a/src/main/java/bjc/data/internals/HalfBoundLazyPair.java b/src/main/java/bjc/data/internals/HalfBoundLazyPair.java
index 6bcb6ae..849b973 100644
--- a/src/main/java/bjc/data/internals/HalfBoundLazyPair.java
+++ b/src/main/java/bjc/data/internals/HalfBoundLazyPair.java
@@ -4,8 +4,8 @@ import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
-import bjc.data.IHolder;
-import bjc.data.IPair;
+import bjc.data.Holder;
+import bjc.data.Pair;
import bjc.data.Identity;
import bjc.data.LazyPair;
@@ -24,15 +24,15 @@ import bjc.data.LazyPair;
*/
@SuppressWarnings("javadoc")
public class HalfBoundLazyPair<OldType, NewLeft, NewRight>
- implements IPair<NewLeft, NewRight> {
+ implements Pair<NewLeft, NewRight> {
/* The supplier of the old value. */
private final Supplier<OldType> oldSupplier;
/* The function to transform the old value into a new pair. */
- private final Function<OldType, IPair<NewLeft, NewRight>> binder;
+ private final Function<OldType, Pair<NewLeft, NewRight>> binder;
/* The new bound pair. */
- private IPair<NewLeft, NewRight> boundPair;
+ private Pair<NewLeft, NewRight> boundPair;
/* Has the pair been bound yet or not? */
private boolean pairBound;
@@ -46,16 +46,16 @@ public class HalfBoundLazyPair<OldType, NewLeft, NewRight>
* The function to use to create the pair from the old value.
*/
public HalfBoundLazyPair(final Supplier<OldType> oldSupp,
- final Function<OldType, IPair<NewLeft, NewRight>> bindr) {
+ final Function<OldType, Pair<NewLeft, NewRight>> bindr) {
oldSupplier = oldSupp;
binder = bindr;
}
@Override
- public <BoundLeft, BoundRight> IPair<BoundLeft, BoundRight> bind(
- final BiFunction<NewLeft, NewRight, IPair<BoundLeft, BoundRight>> bindr) {
- final IHolder<IPair<NewLeft, NewRight>> newPair = new Identity<>(boundPair);
- final IHolder<Boolean> newPairMade = new Identity<>(pairBound);
+ public <BoundLeft, BoundRight> Pair<BoundLeft, BoundRight> bind(
+ final BiFunction<NewLeft, NewRight, Pair<BoundLeft, BoundRight>> bindr) {
+ final Holder<Pair<NewLeft, NewRight>> newPair = new Identity<>(boundPair);
+ final Holder<Boolean> newPairMade = new Identity<>(pairBound);
final Supplier<NewLeft> leftSupp = () -> {
if (!newPairMade.getValue()) {
@@ -81,10 +81,10 @@ public class HalfBoundLazyPair<OldType, NewLeft, NewRight>
}
@Override
- public <BoundLeft> IPair<BoundLeft, NewRight>
- bindLeft(final Function<NewLeft, IPair<BoundLeft, NewRight>> leftBinder) {
+ public <BoundLeft> Pair<BoundLeft, NewRight>
+ bindLeft(final Function<NewLeft, Pair<BoundLeft, NewRight>> leftBinder) {
final Supplier<NewLeft> leftSupp = () -> {
- IPair<NewLeft, NewRight> newPair = boundPair;
+ Pair<NewLeft, NewRight> newPair = boundPair;
if (!pairBound) {
newPair = binder.apply(oldSupplier.get());
@@ -97,10 +97,10 @@ public class HalfBoundLazyPair<OldType, NewLeft, NewRight>
}
@Override
- public <BoundRight> IPair<NewLeft, BoundRight>
- bindRight(final Function<NewRight, IPair<NewLeft, BoundRight>> rightBinder) {
+ public <BoundRight> Pair<NewLeft, BoundRight>
+ bindRight(final Function<NewRight, Pair<NewLeft, BoundRight>> rightBinder) {
final Supplier<NewRight> rightSupp = () -> {
- IPair<NewLeft, NewRight> newPair = boundPair;
+ Pair<NewLeft, NewRight> newPair = boundPair;
if (!pairBound) {
newPair = binder.apply(oldSupplier.get());
@@ -114,8 +114,8 @@ public class HalfBoundLazyPair<OldType, NewLeft, NewRight>
@Override
public <OtherLeft, OtherRight, CombinedLeft, CombinedRight>
- IPair<CombinedLeft, CombinedRight>
- combine(final IPair<OtherLeft, OtherRight> otherPair,
+ Pair<CombinedLeft, CombinedRight>
+ combine(final Pair<OtherLeft, OtherRight> otherPair,
final BiFunction<NewLeft, OtherLeft, CombinedLeft> leftCombiner,
final BiFunction<NewRight, OtherRight, CombinedRight> rightCombiner) {
return otherPair.bind((otherLeft, otherRight) -> bind((leftVal, rightVal) -> {
@@ -127,7 +127,7 @@ public class HalfBoundLazyPair<OldType, NewLeft, NewRight>
}
@Override
- public <NewLeftType> IPair<NewLeftType, NewRight>
+ public <NewLeftType> Pair<NewLeftType, NewRight>
mapLeft(final Function<NewLeft, NewLeftType> mapper) {
final Supplier<NewLeftType> leftSupp = () -> {
if (pairBound)
@@ -149,7 +149,7 @@ public class HalfBoundLazyPair<OldType, NewLeft, NewRight>
}
@Override
- public <NewRightType> IPair<NewLeft, NewRightType>
+ public <NewRightType> Pair<NewLeft, NewRightType>
mapRight(final Function<NewRight, NewRightType> mapper) {
final Supplier<NewLeft> leftSupp = () -> {
if (pairBound)
diff --git a/src/main/java/bjc/data/internals/WrappedLazy.java b/src/main/java/bjc/data/internals/WrappedLazy.java
index cda86fd..624fb1b 100644
--- a/src/main/java/bjc/data/internals/WrappedLazy.java
+++ b/src/main/java/bjc/data/internals/WrappedLazy.java
@@ -3,7 +3,7 @@ package bjc.data.internals;
import java.util.function.Function;
import java.util.function.UnaryOperator;
-import bjc.data.IHolder;
+import bjc.data.Holder;
import bjc.data.Lazy;
/**
@@ -13,9 +13,9 @@ import bjc.data.Lazy;
* @param <ContainedType>
* The type of the wrapped value.
*/
-public class WrappedLazy<ContainedType> implements IHolder<ContainedType> {
+public class WrappedLazy<ContainedType> implements Holder<ContainedType> {
/* Held value. */
- private final IHolder<IHolder<ContainedType>> held;
+ private final Holder<Holder<ContainedType>> held;
/**
* Create a new wrapped lazy value.
@@ -23,7 +23,7 @@ public class WrappedLazy<ContainedType> implements IHolder<ContainedType> {
* @param wrappedHolder
* The holder to make lazy.
*/
- public WrappedLazy(final IHolder<ContainedType> wrappedHolder) {
+ public WrappedLazy(final Holder<ContainedType> wrappedHolder) {
held = new Lazy<>(wrappedHolder);
}
@@ -34,15 +34,15 @@ public class WrappedLazy<ContainedType> implements IHolder<ContainedType> {
* This is a case where reified generics would be useful, because then the
* compiler could know which one we meant without the dummy parameter.
*/
- private WrappedLazy(final IHolder<IHolder<ContainedType>> wrappedHolder,
+ private WrappedLazy(final Holder<Holder<ContainedType>> wrappedHolder,
@SuppressWarnings("unused") final boolean dummy) {
held = wrappedHolder;
}
@Override
- public <BoundType> IHolder<BoundType>
- bind(final Function<ContainedType, IHolder<BoundType>> binder) {
- final IHolder<IHolder<BoundType>> newHolder = held.map(containedHolder -> {
+ public <BoundType> Holder<BoundType>
+ bind(final Function<ContainedType, Holder<BoundType>> binder) {
+ final Holder<Holder<BoundType>> newHolder = held.map(containedHolder -> {
return containedHolder.bind(binder);
});
@@ -50,7 +50,7 @@ public class WrappedLazy<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public <NewType> Function<ContainedType, IHolder<NewType>>
+ public <NewType> Function<ContainedType, Holder<NewType>>
lift(final Function<ContainedType, NewType> func) {
return val -> {
return new Lazy<>(func.apply(val));
@@ -58,9 +58,9 @@ public class WrappedLazy<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(final Function<ContainedType, MappedType> mapper) {
- final IHolder<IHolder<MappedType>> newHolder = held.map(containedHolder -> {
+ final Holder<Holder<MappedType>> newHolder = held.map(containedHolder -> {
return containedHolder.map(mapper);
});
@@ -68,7 +68,7 @@ public class WrappedLazy<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public IHolder<ContainedType>
+ public Holder<ContainedType>
transform(final UnaryOperator<ContainedType> transformer) {
held.transform(containedHolder -> {
return containedHolder.transform(transformer);
diff --git a/src/main/java/bjc/data/internals/WrappedOption.java b/src/main/java/bjc/data/internals/WrappedOption.java
index 6becc16..f46d501 100644
--- a/src/main/java/bjc/data/internals/WrappedOption.java
+++ b/src/main/java/bjc/data/internals/WrappedOption.java
@@ -3,7 +3,7 @@ package bjc.data.internals;
import java.util.function.Function;
import java.util.function.UnaryOperator;
-import bjc.data.IHolder;
+import bjc.data.Holder;
import bjc.data.Option;
/**
@@ -13,9 +13,9 @@ import bjc.data.Option;
* @param <ContainedType>
* The wrapped type.
*/
-public class WrappedOption<ContainedType> implements IHolder<ContainedType> {
+public class WrappedOption<ContainedType> implements Holder<ContainedType> {
/* The held value. */
- private final IHolder<IHolder<ContainedType>> held;
+ private final Holder<Holder<ContainedType>> held;
/**
* Create a new wrapped option.
@@ -23,7 +23,7 @@ public class WrappedOption<ContainedType> implements IHolder<ContainedType> {
* @param seedValue
* The value to wrap.
*/
- public WrappedOption(final IHolder<ContainedType> seedValue) {
+ public WrappedOption(final Holder<ContainedType> seedValue) {
held = new Option<>(seedValue);
}
@@ -31,15 +31,15 @@ public class WrappedOption<ContainedType> implements IHolder<ContainedType> {
* The dummy parameter is to ensure the compiler can pick the right method,
* because without this method erases to the same type as the public one.
*/
- private WrappedOption(final IHolder<IHolder<ContainedType>> toHold,
+ private WrappedOption(final Holder<Holder<ContainedType>> toHold,
@SuppressWarnings("unused") final boolean dummy) {
held = toHold;
}
@Override
- public <BoundType> IHolder<BoundType>
- bind(final Function<ContainedType, IHolder<BoundType>> binder) {
- final IHolder<IHolder<BoundType>> newHolder = held.map(containedHolder -> {
+ public <BoundType> Holder<BoundType>
+ bind(final Function<ContainedType, Holder<BoundType>> binder) {
+ final Holder<Holder<BoundType>> newHolder = held.map(containedHolder -> {
return containedHolder.bind((containedValue) -> {
if (containedValue == null)
return new Option<>(null);
@@ -52,7 +52,7 @@ public class WrappedOption<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public <NewType> Function<ContainedType, IHolder<NewType>>
+ public <NewType> Function<ContainedType, Holder<NewType>>
lift(final Function<ContainedType, NewType> func) {
return val -> {
return new Option<>(func.apply(val));
@@ -60,9 +60,9 @@ public class WrappedOption<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public <MappedType> IHolder<MappedType>
+ public <MappedType> Holder<MappedType>
map(final Function<ContainedType, MappedType> mapper) {
- final IHolder<IHolder<MappedType>> newHolder = held.map(containedHolder -> {
+ final Holder<Holder<MappedType>> newHolder = held.map(containedHolder -> {
return containedHolder.map((containedValue) -> {
if (containedValue == null)
return null;
@@ -75,7 +75,7 @@ public class WrappedOption<ContainedType> implements IHolder<ContainedType> {
}
@Override
- public IHolder<ContainedType>
+ public Holder<ContainedType>
transform(final UnaryOperator<ContainedType> transformer) {
held.transform(containedHolder -> {
return containedHolder.transform((containedValue) -> {
diff --git a/src/main/java/bjc/esodata/AbbrevMap2.java b/src/main/java/bjc/esodata/AbbrevMap2.java
index f131aec..259c963 100644
--- a/src/main/java/bjc/esodata/AbbrevMap2.java
+++ b/src/main/java/bjc/esodata/AbbrevMap2.java
@@ -36,9 +36,7 @@ public class AbbrevMap2 {
*/
public void add(String... words) {
for (String word : words) {
- for (String substr : genAbbrevs(word)) {
- backing.add(substr, word);
- }
+ for (String substr : genAbbrevs(word)) backing.add(substr, word);
}
}
@@ -65,9 +63,7 @@ public class AbbrevMap2 {
*/
public void removeWords(String... words) {
for (String word : words) {
- for (String substr : genAbbrevs(word)) {
- backing.remove(substr, word);
- }
+ for (String substr : genAbbrevs(word)) backing.remove(substr, word);
}
}
@@ -95,10 +91,7 @@ public class AbbrevMap2 {
public String deabbrev(String word) {
Set<String> st = backing.get(word);
- if (st.size() == 1) {
- return st.iterator().next();
- } else {
- return null;
- }
+ if (st.size() == 1) return st.iterator().next();
+ else return null;
}
}
diff --git a/src/main/java/bjc/esodata/DefaultList.java b/src/main/java/bjc/esodata/DefaultList.java
index c4535bd..e622301 100644
--- a/src/main/java/bjc/esodata/DefaultList.java
+++ b/src/main/java/bjc/esodata/DefaultList.java
@@ -90,10 +90,8 @@ public class DefaultList<ValueType> extends AbstractList<ValueType> {
@Override
public ValueType get(int idx) {
- if (idx < 0 || idx >= backing.size())
- return defVal;
-
- return backing.get(idx);
+ if (idx < 0 || idx >= backing.size()) return defVal;
+ else return backing.get(idx);
}
@Override
diff --git a/src/main/java/bjc/esodata/Directory.java b/src/main/java/bjc/esodata/Directory.java
index e083cd8..fa47b6f 100644
--- a/src/main/java/bjc/esodata/Directory.java
+++ b/src/main/java/bjc/esodata/Directory.java
@@ -59,8 +59,7 @@ public interface Directory<K, V> {
* @return The new sub-directory, or null if one by that name already exists.
*/
default Directory<K, V> newSubdirectory(final K key) {
- if (hasSubdirectory(key))
- return null;
+ if (hasSubdirectory(key)) return null;
final Directory<K, V> dir = new SimpleDirectory<>();
diff --git a/src/main/java/bjc/esodata/DoubleTape.java b/src/main/java/bjc/esodata/DoubleTape.java
index cc7cdb9..30d94a1 100644
--- a/src/main/java/bjc/esodata/DoubleTape.java
+++ b/src/main/java/bjc/esodata/DoubleTape.java
@@ -112,9 +112,7 @@ public class DoubleTape<T> implements Tape<T>, DoubleSided {
public boolean left(final int amt) {
final boolean succ = front.left(amt);
- if (succ) {
- back.right(amt);
- }
+ if (succ) back.right(amt);
return succ;
}
@@ -128,9 +126,7 @@ public class DoubleTape<T> implements Tape<T>, DoubleSided {
public boolean right(final int amt) {
final boolean succ = front.right(amt);
- if (succ) {
- back.left(amt);
- }
+ if (succ) back.left(amt);
return succ;
}
@@ -167,26 +163,23 @@ public class DoubleTape<T> implements Tape<T>, DoubleSided {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof DoubleTape<?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof DoubleTape<?>)) return false;
final DoubleTape<?> other = (DoubleTape<?>) obj;
if (back == null) {
- if (other.back != null)
- return false;
- } else if (!back.equals(other.back))
+ if (other.back != null) return false;
+ } else if (!back.equals(other.back)) {
return false;
+ }
if (front == null) {
- if (other.front != null)
- return false;
- } else if (!front.equals(other.front))
+ if (other.front != null) return false;
+ } else if (!front.equals(other.front)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/esodata/FlatNestIterator.java b/src/main/java/bjc/esodata/FlatNestIterator.java
new file mode 100644
index 0000000..42981f5
--- /dev/null
+++ b/src/main/java/bjc/esodata/FlatNestIterator.java
@@ -0,0 +1,103 @@
+package bjc.esodata;
+
+import java.util.*;
+
+import bjc.data.*;
+
+final class FlatNestIterator<Element> implements ListIterator<Element>
+{
+ private Deque<ListIterator<Either<Element, NestList<Element>>>> iterators;
+
+ public FlatNestIterator(ListIterator<Either<Element, NestList<Element>>> iterator)
+ {
+ this.iterators = new ArrayDeque<>();
+ this.iterators.add(iterator);
+ }
+
+ @Override
+ public boolean hasNext() {
+ boolean result = false;
+ do {
+ result = iterators.peek().hasNext();
+
+ if (result == false) iterators.pop();
+ } while (result == false && iterators.isEmpty() == false);
+
+ return result;
+ }
+
+ @Override
+ public Element next() {
+ Holder<Element> element = Holder.of(null);
+ do {
+ element = iterators.peek().next().extract((ele) -> {
+ return Holder.of(ele);
+ }, (lst) -> {
+ // Ignore empty sublists.
+ if (lst.size() != 0) iterators.push(lst.listIterator());
+
+ return null;
+ });
+ } while (element == null && hasNext());
+
+ if (element == null) throw new NoSuchElementException();
+
+ return element.getValue();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ boolean result = false;
+ do {
+ result = iterators.peek().hasPrevious();
+
+ if (result == false) iterators.pop();
+ } while (result == false && iterators.isEmpty() == false);
+
+ return result;
+ }
+
+ @Override
+ public Element previous() {
+ Holder<Element> element = Holder.of(null);
+ do {
+ element = iterators.peek().previous().extract((ele) -> {
+ return Holder.of(ele);
+ }, (lst) -> {
+ // Ignore empty sublists.
+ if (lst.size() != 0) iterators.push(lst.listIterator());
+
+ return null;
+ });
+ } while (element == null && hasPrevious());
+
+ if (element == null) throw new NoSuchElementException();
+
+ return element.getValue();
+ }
+
+ @Override
+ public int nextIndex() {
+ return iterators.peek().nextIndex();
+ }
+
+ @Override
+ public int previousIndex() {
+ return iterators.peek().previousIndex();
+ }
+
+ @Override
+ public void remove() {
+ iterators.peek().remove();
+ }
+
+ @Override
+ public void set(Element e) {
+ iterators.peek().set(Either.left(e));
+ }
+
+ @Override
+ public void add(Element e) {
+ iterators.peek().add(Either.left(e));
+ }
+} \ No newline at end of file
diff --git a/src/main/java/bjc/esodata/MapSet.java b/src/main/java/bjc/esodata/MapSet.java
index a80c482..7d77ad6 100644
--- a/src/main/java/bjc/esodata/MapSet.java
+++ b/src/main/java/bjc/esodata/MapSet.java
@@ -116,8 +116,7 @@ public class MapSet<KeyType, ValueType> extends AbstractMap<KeyType, ValueType>
* @return False if there is no map attached to the key, true otherwise.
*/
public boolean setMap(String key) {
- if (!backing.containsKey(key))
- return false;
+ if (!backing.containsKey(key)) return false;
currentMap = backing.get(key);
@@ -165,16 +164,14 @@ public class MapSet<KeyType, ValueType> extends AbstractMap<KeyType, ValueType>
@Override
public Set<Map.Entry<KeyType, ValueType>> entrySet() {
- if (currentMap == null)
- throw new NullPointerException("Current map is not set");
+ if (currentMap == null) throw new NullPointerException("Current map is not set");
return currentMap.entrySet();
}
@Override
public ValueType put(KeyType key, ValueType value) {
- if (currentMap == null)
- throw new NullPointerException("Current map is not set");
+ if (currentMap == null) throw new NullPointerException("Current map is not set");
return currentMap.put(key, value);
}
diff --git a/src/main/java/bjc/esodata/MinMaxList.java b/src/main/java/bjc/esodata/MinMaxList.java
new file mode 100644
index 0000000..6747831
--- /dev/null
+++ b/src/main/java/bjc/esodata/MinMaxList.java
@@ -0,0 +1,238 @@
+package bjc.esodata;
+
+import java.util.*;
+
+// @FIXME Nov 15th, 2020 Ben Culkin :RecalcMinMax
+// Is there some sort of way to avoid having to recalculate these elements when
+// that element is removed?
+/**
+ * A list that automatically tracks the minimum & maximum element of a list.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ValueType> The type of element stored in the list.
+ *
+ */
+public class MinMaxList<ValueType> extends AbstractList<ValueType> {
+ private final List<ValueType> backing;
+ private final Comparator<ValueType> picker;
+
+ private ValueType minElement;
+ private boolean recalcMin = false;
+
+ private ValueType maxElement;
+ private boolean recalcMax = false;
+
+ // Create constructors
+
+ /**
+ * Create a new min/max list using the given comparator.
+ *
+ * @param picker The comparator to use to determine min/max elements.
+ */
+ public MinMaxList(Comparator<ValueType> picker) {
+ this(picker, new ArrayList<>());
+ }
+
+ /**
+ * Create a new min/max list using the given comparator.
+ *
+ * @param picker The comparator to use to determine min/max elements.
+ * @param values The values to fill the list from.
+ */
+ @SafeVarargs
+ public MinMaxList(Comparator<ValueType> picker, ValueType... values) {
+ this(picker);
+
+ for (ValueType value : values) {
+ add(value);
+ }
+ }
+ /**
+ * Create a new min/max list using the given comparator.
+ *
+ * @param picker The comparator to use to determine min/max elements.
+ * @param backing The collection to use values from.
+ */
+ public MinMaxList(Comparator<ValueType> picker, Collection<ValueType> backing) {
+ this(picker, new ArrayList<>(backing));
+ }
+
+ /**
+ * Create a new min/max list using the given comparator.
+ *
+ * @param picker The comparator to use to determine min/max elements.
+ * @param backing The list to use as a backing list.
+ */
+ public MinMaxList(Comparator<ValueType> picker, List<ValueType> backing) {
+ this.backing = backing;
+ this.picker = picker;
+
+ calculateBoth();
+ }
+
+ @Override
+ public ValueType get(int index) {
+ return backing.get(index);
+ }
+
+ @Override
+ public ValueType set(int index, ValueType element) {
+ ValueType oldElement = backing.set(index, element);
+
+ if (minElement == null) {
+ minElement = element;
+ } else if (picker.compare(element, minElement) < 0) {
+ minElement = element;
+ recalcMin = false;
+ } else if (oldElement.equals(minElement)) {
+ minElement = null;
+ recalcMin = true;
+ }
+
+ if (maxElement == null) {
+ maxElement = element;
+ } else if (picker.compare(element, maxElement) > 0) {
+ maxElement = element;
+ recalcMax = false;
+ } else if (oldElement.equals(maxElement)) {
+ maxElement = null;
+ recalcMax = true;
+ }
+
+ return oldElement;
+ }
+
+ @Override
+ public void add(int index, ValueType element) {
+ backing.add(index, element);
+
+ if (minElement == null) {
+ minElement = element;
+ } else if (picker.compare(element, minElement) < 0) {
+ minElement = element;
+ recalcMin = false;
+ }
+
+ if (maxElement == null) {
+ maxElement = element;
+ } else if (picker.compare(element, maxElement) > 0) {
+ maxElement = element;
+ recalcMax = false;
+ }
+ }
+
+ @Override
+ public ValueType remove(int index) {
+ ValueType oldElement = backing.remove(index);
+
+ if (oldElement.equals(minElement)) {
+ minElement = null;
+ recalcMin = true;
+ }
+
+ if (oldElement.equals(maxElement)) {
+ maxElement = null;
+ recalcMax = true;
+ }
+
+ return oldElement;
+ }
+
+ @Override
+ public int size() {
+ return backing.size();
+ }
+
+ /**
+ * Get the minimum element currently stored in this list.
+ *
+ * @return The minimum element stored in the list.
+ */
+ public ValueType minimum() {
+ if (recalcMin) calculateMinimum();
+
+ return minElement;
+ }
+
+ /**
+ * Get the maximum element currently stored in this list.
+ *
+ * @return The maximum element stored in the list.
+ */
+ public ValueType maximum() {
+ if (recalcMax) calculateMaximum();
+
+ return maxElement;
+ }
+
+ private void calculateMinimum() {
+ for (ValueType element : backing) {
+ if (minElement == null) {
+ minElement = element;
+ } else if (picker.compare(element, minElement) < 0) {
+ minElement = element;
+ }
+ }
+
+ recalcMin = false;
+ }
+
+ private void calculateMaximum() {
+ for (ValueType element : backing) {
+ if (maxElement == null) {
+ maxElement = element;
+ } else if (picker.compare(element, maxElement) > 0) {
+ maxElement = element;
+ }
+ }
+
+ recalcMax = false;
+ }
+
+ private void calculateBoth() {
+ for (ValueType element : backing) {
+ if (minElement == null) {
+ minElement = element;
+ } else if (picker.compare(element, minElement) < 0) {
+ minElement = element;
+ }
+
+ if (maxElement == null) {
+ maxElement = element;
+ } else if (picker.compare(element, maxElement) > 0) {
+ maxElement = element;
+ }
+ }
+
+ recalcMin = false;
+ recalcMax = false;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s (min: %s) (max: %s)", backing,
+ recalcMin ? "(unknown)" : minElement,
+ recalcMax ? "(unknown)" : maxElement);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + Objects.hash(backing, picker);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!super.equals(obj)) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ MinMaxList<?> other = (MinMaxList<?>) obj;
+
+ return Objects.equals(backing, other.backing)
+ && Objects.equals(picker, other.picker);
+ }
+}
diff --git a/src/main/java/bjc/esodata/Multimap.java b/src/main/java/bjc/esodata/Multimap.java
index a79f1b0..fae872e 100644
--- a/src/main/java/bjc/esodata/Multimap.java
+++ b/src/main/java/bjc/esodata/Multimap.java
@@ -51,9 +51,8 @@ public class Multimap<KeyType, ValueType> {
*/
public void remove(KeyType key, ValueType value) {
// We have no values for that key; bail.
- if (!backing.containsKey(key))
- return;
-
+ if (!backing.containsKey(key)) return;
+
backing.get(key).remove(value);
}
@@ -76,10 +75,8 @@ public class Multimap<KeyType, ValueType> {
* @return A set containing all of the values that have been mapped to that key.
*/
public Set<ValueType> get(KeyType key) {
- if (!backing.containsKey(key))
- return new HashSet<>();
-
- return backing.get(key).values();
+ if (!backing.containsKey(key)) return new HashSet<>();
+ else return backing.get(key).values();
}
/**
@@ -107,8 +104,7 @@ public class Multimap<KeyType, ValueType> {
* mapping.
*/
public boolean contains(KeyType key, ValueType value) {
- if (!backing.containsKey(key))
- return false;
+ if (!backing.containsKey(key)) return false;
return backing.get(key).contains(value) > 0;
}
diff --git a/src/main/java/bjc/esodata/NestList.java b/src/main/java/bjc/esodata/NestList.java
new file mode 100644
index 0000000..eccaf9d
--- /dev/null
+++ b/src/main/java/bjc/esodata/NestList.java
@@ -0,0 +1,389 @@
+package bjc.esodata;
+
+import static bjc.functypes.Combinators.*;
+
+import java.util.*;
+import java.util.function.*;
+
+import bjc.data.*;
+
+/**
+ * A list which can contain sublists of itself.
+ *
+ * N.B: Be careful if you form a recursive list, as there is no form of detection
+ * in place for that. Some operations may work, but those that do a deep traversal
+ * of the list will not.
+ *
+ * @author Ben Culkin
+ *
+ * @param <Element> The type contained in the list.
+ */
+public class NestList<Element> extends AbstractList<Either<Element, NestList<Element>>>
+{
+ private final List<Either<Element, NestList<Element>>> backing;
+
+ /**
+ * Create a new empty nesting list.
+ */
+ public NestList() {
+ backing = new ArrayList<>();
+ }
+
+ /**
+ * Create a new empty nesting list with the given capacity.
+ *
+ * @param cap The capacity for the nesting list.
+ */
+ public NestList(int cap) {
+ backing = new ArrayList<>(cap);
+ }
+
+ /**
+ * Add an element to this list.
+ *
+ * @param element The element to add to the list.
+ *
+ * @return Whether we could add the element.
+ */
+ public boolean addItem(Element element) {
+ return backing.add(Either.left(element));
+ }
+
+ /**
+ * Add a sublist to this list.
+ *
+ * @param element The sublist to add to this list.
+ *
+ * @return Whether we could add the sublist.
+ */
+ public boolean addItem(NestList<Element> element) {
+ return backing.add(Either.right(element));
+ }
+ /**
+ * Add elements to this list.
+ *
+ * @param elements The elements to add to the list.
+ *
+ * @return Whether we could add each element.
+ */
+ public boolean[] addItems(@SuppressWarnings("unchecked") Element... elements) {
+ boolean[] vals = new boolean[elements.length];
+
+ for (int i = 0; i < vals.length; i++)
+ {
+ vals[i] = addItem(elements[i]);
+ }
+
+ return vals;
+ }
+
+ /**
+ * Add sublists to this list.
+ *
+ * @param elements The sublists to add to this list.
+ *
+ * @return Whether we could add each sublist.
+ */
+ public boolean[] addItems(@SuppressWarnings("unchecked") NestList<Element>... elements) {
+ boolean[] vals = new boolean[elements.length];
+
+ for (int i = 0; i < vals.length; i++)
+ {
+ vals[i] = addItem(elements[i]);
+ }
+
+ return vals;
+ }
+
+ /**
+ * Add a sublist with the given elements to this list.
+ *
+ * @param elements The elements of the sublist.
+ *
+ * @return Whether or not we could add the sublist.
+ */
+ public boolean addSublist(@SuppressWarnings("unchecked") Element... elements) {
+ NestList<Element> container = new NestList<>(elements.length);
+
+ for (Element ele : elements) {
+ container.addItem(ele);
+ }
+
+ return addItem(container);
+ }
+
+ /**
+ * Return an iterator over a flattened version of this list.
+ *
+ * N.B: In certain cases involving empty sublists, the hasNext() operation\
+ * may not be 100% accurate. Be warned.
+ *
+ * @return An iterator over a flattened variant of this list.
+ */
+ public ListIterator<Element> flatIterator() {
+ return new FlatNestIterator<>(listIterator());
+ }
+
+ /**
+ * Flatten one level of nesting from this list.
+ *
+ * @return The list with one level of nesting flattened.
+ */
+ public NestList<Element> flatten() {
+ NestList<Element> flatterList = new NestList<>(size());
+
+ backing.forEach((element) ->
+ element.pick(flatterList::addItem, flatterList::addAll)
+ );
+
+ return flatterList;
+ }
+
+ /**
+ * Flatten this list recursively.
+ *
+ * @return A flattened form of this list.
+ */
+ public List<Element> deepFlatten() {
+ List<Element> flatList = new ArrayList<>();
+
+ flatIterator().forEachRemaining(flatList::add);
+
+ return flatList;
+ }
+
+ /**
+ * Get the total number of elements contained in this list and all sublists.
+ *
+ * @return The total number of elements contained in this list.
+ */
+ public int deepSize() {
+ int size = 0;
+
+ for (Either<Element, NestList<Element>> element : backing)
+ {
+ size += element.extract((ele) -> 1, (lst) -> lst.deepSize());
+ }
+
+ return size;
+ }
+
+ /**
+ * Replace all of the elements in this list in-place with transformed versions.
+ *
+ * @param elementOperator The operator to apply to elements.
+ * @param listOperator The operator to apply to sublists.
+ */
+ public void replace(
+ UnaryOperator<Element> elementOperator,
+ UnaryOperator<NestList<Element>> listOperator) {
+ backing.replaceAll((ele) -> ele.map(elementOperator, listOperator));
+ }
+
+ /**
+ * Perform a shallow mapping over this list.
+ *
+ * @param <NewElement> The new element type.
+ *
+ * @param elementMapper The function to map elements.
+ * @param listMapper The function to map lists.
+ *
+ * @return A new list containing the mapped elements.
+ */
+ public <NewElement> NestList<NewElement> map(
+ Function<Element, NewElement> elementMapper,
+ Function<NestList<Element>, NestList<NewElement>> listMapper)
+ {
+ NestList<NewElement> nest = new NestList<>(backing.size());
+
+ for (Either<Element, NestList<Element>> element : backing)
+ {
+ nest.add(element.map(elementMapper, listMapper));
+ }
+
+ return nest;
+ }
+
+ /**
+ * Perform a recursive mapping over this list.
+ *
+ * @param <NewElement> The new element type.
+ *
+ * @param mapper The element mapper.
+ *
+ * @return A new list with the same structure, but transformed elements.
+ */
+ public <NewElement> NestList<NewElement> deepMap(
+ Function<Element, NewElement> mapper)
+ {
+ return map(mapper, (lst) -> lst.deepMap(mapper));
+ }
+
+ /**
+ * Perform a mapping on the list with controllable recursion.
+ *
+ * Inspired by the function of the same name from Raku.
+ *
+ * @param <NewElement> The new element type.
+ *
+ * @param recurPredicate Determines whether to recur into a list or not.
+ * @param elementMapper The mapper on elements.
+ * @param listMapper The mapper on lists we aren't recursing into.
+ *
+ * @return A new list with its elements mapped.
+ */
+ public <NewElement> NestList<NewElement> duckMap(
+ Predicate<NestList<Element>> recurPredicate,
+ Function<Element, NewElement> elementMapper,
+ Function<NestList<Element>, NestList<NewElement>> listMapper)
+ {
+ return map(
+ elementMapper,
+ iftt(recurPredicate,
+ (list) -> list.duckMap(
+ recurPredicate, elementMapper, listMapper),
+ listMapper
+ )
+ );
+ }
+
+ /**
+ * Perform a reduction over this list.
+ *
+ * @param <Output> The type of the output value.
+ *
+ * @param initial The initial state of the output value.
+ * @param elementFolder The function to fold elements with.
+ * @param listFolder The function to fold lists with.
+ *
+ * @return The result of reducing the list.
+ */
+ public <Output> Output reduce(
+ Output initial,
+ BiFunction<Output, Element, Output> elementFolder,
+ BiFunction<Output, NestList<Element>, Output> listFolder)
+ {
+ Holder<Output> out = Holder.of(initial);
+ for (Either<Element, NestList<Element>> item : backing)
+ {
+ out.transform((state) -> item.extract(
+ (ele) -> elementFolder.apply(state, ele),
+ (lst) -> listFolder.apply(state, lst))
+ );
+ }
+
+ return out.getValue();
+ }
+
+ /**
+ * Perform a recursive reduction over this list.
+ *
+ * @param <Output> The type of the output value.
+ *
+ * @param initial The initial state of the output value.
+ * @param elementFolder The function to fold elements with.
+ *
+ * @return The result of recursively reducing the list.
+ */
+ public <Output> Output deepReduce(
+ Output initial,
+ BiFunction<Output, Element, Output> elementFolder)
+ {
+ return reduce(
+ initial,
+ elementFolder,
+ (state, lst) -> lst.deepReduce(state, elementFolder));
+ }
+
+ /**
+ * Conditionally expand elements of this list into the provided list.
+ *
+ * @param nest The list to expand elements into.
+ * @param expandPicker Picks whether or not to expand a list.
+ * @param recur Whether or not to recursively expand lists.
+ *
+ * @return The provided list.
+ */
+ public NestList<Element> expandInto(
+ NestList<Element> nest,
+ Predicate<NestList<Element>> expandPicker,
+ boolean recur)
+ {
+ return reduce(nest,
+ (state, item) -> with(state, (stat) -> stat.addItem(item)),
+ (state, list) -> {
+ if (expandPicker.test(list)) {
+ return list.reduce(nest,
+ (substate, subitem)
+ -> with(substate,
+ (subst) -> subst.addItem(subitem)),
+ (substate, sublist)
+ -> with(substate, (subst) -> {
+ if (recur) {
+ sublist.expandInto(
+ subst,
+ expandPicker,
+ recur);
+ } else {
+ subst.addItem(sublist);
+ }
+ }));
+ } else {
+ state.addItem(list);
+ return state;
+ }
+ });
+ }
+ // List methods and other things.
+
+ @Override
+ public boolean add(Either<Element, NestList<Element>> e) {
+ return backing.add(e);
+ }
+
+ @Override
+ public void add(int index, Either<Element, NestList<Element>> element) {
+ backing.add(index, element);
+ }
+
+ @Override
+ public Either<Element, NestList<Element>> get(int index) {
+ return backing.get(index);
+ }
+
+ @SuppressWarnings("unlikely-arg-type")
+ @Override
+ public boolean remove(Object o) {
+ return backing.remove(o);
+ }
+
+
+ @Override
+ public Either<Element, NestList<Element>> remove(int index) {
+ return backing.remove(index);
+ }
+
+ @Override
+ public int size() {
+ return backing.size();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + Objects.hash(backing);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!super.equals(obj)) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ NestList<?> other = (NestList<?>) obj;
+
+ return Objects.equals(backing, other.backing);
+ }
+}
diff --git a/src/main/java/bjc/esodata/PushdownMap.java b/src/main/java/bjc/esodata/PushdownMap.java
index 54ae939..76dd2db 100644
--- a/src/main/java/bjc/esodata/PushdownMap.java
+++ b/src/main/java/bjc/esodata/PushdownMap.java
@@ -1,12 +1,10 @@
package bjc.esodata;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
+import java.util.*;
+import java.util.function.*;
-import bjc.funcdata.FunctionalMap;
-import bjc.funcdata.IList;
-import bjc.funcdata.IMap;
+import bjc.data.*;
+import bjc.funcdata.*;
/**
* A variant of a map where inserting a duplicate key shadows the existing value
@@ -22,22 +20,22 @@ import bjc.funcdata.IMap;
* @param <ValueType>
* The values in the map.
*/
-public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
+public class PushdownMap<KeyType, ValueType> implements MapEx<KeyType, ValueType> {
/* Our backing storage. */
- private final IMap<KeyType, Stack<ValueType>> backing;
+ private final MapEx<KeyType, Stack<ValueType>> backing;
+ private boolean isFrozen = false;
+ private boolean thawEnabled = true;
+
/** Create a new empty stack-based map. */
public PushdownMap() {
backing = new FunctionalMap<>();
}
- /** Create a new empty stack-based map using the specified backing. */
- private PushdownMap(final IMap<KeyType, Stack<ValueType>> back) {
- backing = back;
- }
-
@Override
public void clear() {
+ if (isFrozen) throw new ObjectFrozen("Can't clear frozen map");
+
backing.clear();
}
@@ -45,30 +43,15 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType>
public boolean containsKey(final KeyType key) {
return backing.containsKey(key);
}
-
- @Override
- public IMap<KeyType, ValueType> extend() {
- return new PushdownMap<>(backing.extend());
- }
-
+
@Override
public void forEach(final BiConsumer<KeyType, ValueType> action) {
backing.forEach((key, stk) -> action.accept(key, stk.top()));
}
@Override
- public void forEachKey(final Consumer<KeyType> action) {
- backing.forEachKey(action);
- }
-
- @Override
- public void forEachValue(final Consumer<ValueType> action) {
- backing.forEachValue(stk -> action.accept(stk.top()));
- }
-
- @Override
- public ValueType get(final KeyType key) {
- return backing.get(key).top();
+ public Optional<ValueType> get(final KeyType key) {
+ return backing.get(key).map((stack) -> stack.top());
}
@Override
@@ -77,23 +60,16 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType>
}
@Override
- public IList<KeyType> keyList() {
+ public ListEx<KeyType> keyList() {
return backing.keyList();
}
@Override
- public <V2> IMap<KeyType, V2> transform(final Function<ValueType, V2> transformer) {
- /*
- * @NOTE Can and should we support this? More to the point, maybe this should be
- * a map sub-type that does what it needs to?
- */
- throw new UnsupportedOperationException("Cannot transform pushdown maps.");
- }
-
- @Override
public ValueType put(final KeyType key, final ValueType val) {
+ if (isFrozen) throw new ObjectFrozen("Can't insert key " + key + " into frozen map");
+
if (backing.containsKey(key)) {
- final Stack<ValueType> stk = backing.get(key);
+ final Stack<ValueType> stk = backing.get(key).get();
final ValueType vl = stk.top();
@@ -111,18 +87,19 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType>
@Override
public ValueType remove(final KeyType key) {
- final Stack<ValueType> stk = backing.get(key);
+ if (isFrozen) throw new ObjectFrozen("Can't remove key " + key + " from frozen map");
- if (stk.size() > 1) {
- return stk.pop();
- }
+ Holder<ValueType> result = Holder.of(null);
+
+ backing.get(key).ifPresent((stk) -> {
+ if (stk.size() > 1) {
+ result.replace(stk.pop());
+ } else {
+ result.replace(backing.remove(key).top());
+ }
+ });
- return backing.remove(key).top();
- }
-
- @Override
- public IList<ValueType> valueList() {
- return backing.valueList().map(Stack::top);
+ return result.getValue();
}
@Override
@@ -137,20 +114,17 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType>
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof PushdownMap<?, ?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof PushdownMap<?, ?>)) return false;
final PushdownMap<?, ?> other = (PushdownMap<?, ?>) obj;
if (backing == null) {
- if (other.backing != null)
- return false;
- } else if (!backing.equals(other.backing))
+ if (other.backing != null) return false;
+ } else if (!backing.equals(other.backing)) {
return false;
+ }
return true;
}
@@ -159,4 +133,43 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType>
public String toString() {
return String.format("PushdownMap [backing=%s]", backing);
}
+
+ @Override
+ public boolean freeze() {
+ isFrozen = true;
+
+ return true;
+ }
+
+ @Override
+ public boolean thaw() {
+ if (thawEnabled) {
+ isFrozen = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean deepFreeze() {
+ thawEnabled = true;
+
+ return freeze();
+ }
+
+ @Override
+ public boolean canFreeze() {
+ return true;
+ }
+
+ @Override
+ public boolean canThaw() {
+ return thawEnabled;
+ }
+
+ @Override
+ public boolean isFrozen() {
+ return isFrozen;
+ }
}
diff --git a/src/main/java/bjc/esodata/QueueStack.java b/src/main/java/bjc/esodata/QueueStack.java
index c40721a..e310f16 100644
--- a/src/main/java/bjc/esodata/QueueStack.java
+++ b/src/main/java/bjc/esodata/QueueStack.java
@@ -29,16 +29,14 @@ public class QueueStack<T> extends Stack<T> {
@Override
public T pop() {
- if (backing.isEmpty())
- throw new StackUnderflow();
+ if (backing.isEmpty()) throw new StackUnderflow();
return backing.remove();
}
@Override
public T top() {
- if (backing.isEmpty())
- throw new StackUnderflow();
+ if (backing.isEmpty()) throw new StackUnderflow();
return backing.peek();
}
@@ -76,20 +74,17 @@ public class QueueStack<T> extends Stack<T> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof QueueStack<?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof QueueStack<?>)) return false;
final QueueStack<?> other = (QueueStack<?>) obj;
if (backing == null) {
- if (other.backing != null)
- return false;
- } else if (!backing.equals(other.backing))
+ if (other.backing != null) return false;
+ } else if (!backing.equals(other.backing)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/esodata/SimpleDirectory.java b/src/main/java/bjc/esodata/SimpleDirectory.java
index 672d92f..13a9f3e 100644
--- a/src/main/java/bjc/esodata/SimpleDirectory.java
+++ b/src/main/java/bjc/esodata/SimpleDirectory.java
@@ -1,7 +1,7 @@
package bjc.esodata;
import bjc.funcdata.FunctionalMap;
-import bjc.funcdata.IMap;
+import bjc.funcdata.MapEx;
/**
* Simple implementation of {@link Directory}.
@@ -18,9 +18,9 @@ import bjc.funcdata.IMap;
*/
public class SimpleDirectory<K, V> implements Directory<K, V> {
/* Our sub-directories. */
- private final IMap<K, Directory<K, V>> children;
+ private final MapEx<K, Directory<K, V>> children;
/* Our data. */
- private final IMap<K, V> data;
+ private final MapEx<K, V> data;
/** Create a new directory. */
public SimpleDirectory() {
@@ -30,7 +30,7 @@ public class SimpleDirectory<K, V> implements Directory<K, V> {
@Override
public Directory<K, V> getSubdirectory(final K key) {
- return children.get(key);
+ return children.get(key).orElse(null);
}
@Override
@@ -50,7 +50,7 @@ public class SimpleDirectory<K, V> implements Directory<K, V> {
@Override
public V getKey(final K key) {
- return data.get(key);
+ return data.get(key).orElse(null);
}
@Override
@@ -71,26 +71,23 @@ public class SimpleDirectory<K, V> implements Directory<K, V> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof SimpleDirectory<?, ?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof SimpleDirectory<?, ?>)) return false;
final SimpleDirectory<?, ?> other = (SimpleDirectory<?, ?>) obj;
if (children == null) {
- if (other.children != null)
- return false;
- } else if (!children.equals(other.children))
+ if (other.children != null) return false;
+ } else if (!children.equals(other.children)) {
return false;
+ }
if (data == null) {
- if (other.data != null)
- return false;
- } else if (!data.equals(other.data))
+ if (other.data != null) return false;
+ } else if (!data.equals(other.data)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/esodata/SimpleStack.java b/src/main/java/bjc/esodata/SimpleStack.java
index b50521e..9c016c3 100644
--- a/src/main/java/bjc/esodata/SimpleStack.java
+++ b/src/main/java/bjc/esodata/SimpleStack.java
@@ -27,16 +27,14 @@ public class SimpleStack<T> extends Stack<T> {
@Override
public T pop() {
- if (backing.isEmpty())
- throw new StackUnderflow();
+ if (backing.isEmpty()) throw new StackUnderflow();
return backing.pop();
}
@Override
public T top() {
- if (backing.isEmpty())
- throw new StackUnderflow();
+ if (backing.isEmpty()) throw new StackUnderflow();
return backing.peek();
}
@@ -69,20 +67,17 @@ public class SimpleStack<T> extends Stack<T> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof SimpleStack<?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof SimpleStack<?>)) return false;
final SimpleStack<?> other = (SimpleStack<?>) obj;
if (backing == null) {
- if (other.backing != null)
- return false;
- } else if (!backing.equals(other.backing))
+ if (other.backing != null) return false;
+ } else if (!backing.equals(other.backing)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/esodata/SingleTape.java b/src/main/java/bjc/esodata/SingleTape.java
index ec56e2b..7c3a34f 100644
--- a/src/main/java/bjc/esodata/SingleTape.java
+++ b/src/main/java/bjc/esodata/SingleTape.java
@@ -45,9 +45,7 @@ public class SingleTape<T> implements Tape<T> {
backing = new ArrayList<>(vals.length);
- for (T val : vals) {
- backing.add(val);
- }
+ for (T val : vals) backing.add(val);
}
/**
@@ -66,9 +64,7 @@ public class SingleTape<T> implements Tape<T> {
public SingleTape(Iterable<T> itr) {
this(false);
- for (T itm : itr) {
- backing.add(itm);
- }
+ for (T itm : itr) backing.add(itm);
}
/**
@@ -86,8 +82,7 @@ public class SingleTape<T> implements Tape<T> {
@Override
public T item() {
- if (pos < 0 || pos >= backing.size())
- return null;
+ if (pos < 0 || pos >= backing.size()) return null;
return backing.get(pos);
}
@@ -114,19 +109,16 @@ public class SingleTape<T> implements Tape<T> {
@Override
public void insertAfter(final T itm) {
- if (pos == backing.size() - 1) {
- backing.add(itm);
- } else {
- backing.add(pos + 1, itm);
- }
+ if (pos == backing.size() - 1) backing.add(itm);
+ else backing.add(pos + 1, itm);
}
@Override
public T remove() {
final T res = backing.remove(pos);
- if (pos != 0) {
- pos -= 1;
- }
+
+ if (pos != 0) pos -= 1;
+
return res;
}
@@ -147,8 +139,7 @@ public class SingleTape<T> implements Tape<T> {
@Override
public boolean left(final int amt) {
- if (pos - amt < 0)
- return false;
+ if (pos - amt < 0) return false;
pos -= amt;
return true;
@@ -163,11 +154,10 @@ public class SingleTape<T> implements Tape<T> {
public boolean right(final int amt) {
if (pos + amt > backing.size()) {
if (autoExtend) {
- while (pos + amt >= backing.size() - 1) {
- backing.add(null);
- }
- } else
+ while (pos + amt >= backing.size() - 1) backing.add(null);
+ } else {
return false;
+ }
}
pos += amt;
@@ -176,15 +166,15 @@ public class SingleTape<T> implements Tape<T> {
@Override
public boolean seekTo(int tgtPos) {
- if (tgtPos < 0)
- return false;
+ if (tgtPos < 0) return false;
- if (tgtPos >= backing.size() - 1)
- if (autoExtend)
- while (tgtPos >= backing.size() - 1)
- backing.add(null);
- else
+ if (tgtPos >= backing.size() - 1) {
+ if (autoExtend) {
+ while (tgtPos >= backing.size() - 1) backing.add(null);
+ } else {
return false;
+ }
+ }
pos = tgtPos;
@@ -208,20 +198,17 @@ public class SingleTape<T> implements Tape<T> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof SingleTape<?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof SingleTape<?>)) return false;
final SingleTape<?> other = (SingleTape<?>) obj;
if (backing == null) {
- if (other.backing != null)
- return false;
- } else if (!backing.equals(other.backing))
+ if (other.backing != null) return false;
+ } else if (!backing.equals(other.backing)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/esodata/SpaghettiStack.java b/src/main/java/bjc/esodata/SpaghettiStack.java
index fc3e154..4bd77d3 100644
--- a/src/main/java/bjc/esodata/SpaghettiStack.java
+++ b/src/main/java/bjc/esodata/SpaghettiStack.java
@@ -37,16 +37,14 @@ class SpaghettiStack<T> extends Stack<T> {
@Override
public T pop() {
- if (backing.isEmpty())
- return parent.pop();
+ if (backing.isEmpty()) return parent.pop();
return backing.pop();
}
@Override
public T top() {
- if (backing.isEmpty())
- return parent.top();
+ if (backing.isEmpty()) return parent.top();
return backing.top();
}
@@ -82,26 +80,23 @@ class SpaghettiStack<T> extends Stack<T> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof SpaghettiStack<?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof SpaghettiStack<?>)) return false;
final SpaghettiStack<?> other = (SpaghettiStack<?>) obj;
if (backing == null) {
- if (other.backing != null)
- return false;
- } else if (!backing.equals(other.backing))
+ if (other.backing != null) return false;
+ } else if (!backing.equals(other.backing)) {
return false;
+ }
if (parent == null) {
- if (other.parent != null)
- return false;
- } else if (!parent.equals(other.parent))
+ if (other.parent != null) return false;
+ } else if (!parent.equals(other.parent)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/esodata/Stack.java b/src/main/java/bjc/esodata/Stack.java
index 5ee5ef2..360e57d 100644
--- a/src/main/java/bjc/esodata/Stack.java
+++ b/src/main/java/bjc/esodata/Stack.java
@@ -96,9 +96,7 @@ public abstract class Stack<T> {
* The elements to insert.
*/
public void pushAll(@SuppressWarnings("unchecked") T... elms) {
- for (T elm : elms) {
- push(elm);
- }
+ for (T elm : elms) push(elm);
}
/**
@@ -108,9 +106,7 @@ public abstract class Stack<T> {
* The elements to insert.
*/
public void pushAll(List<T> elms) {
- for (T elm : elms) {
- push(elm);
- }
+ for (T elm : elms) push(elm);
}
/**
@@ -124,9 +120,7 @@ public abstract class Stack<T> {
public List<T> multipop(int n) {
List<T> lst = new LinkedList<>();
- for (int i = 0; i < n; i++) {
- lst.add(pop());
- }
+ for (int i = 0; i < n; i++) lst.add(pop());
return lst;
}
@@ -142,9 +136,7 @@ public abstract class Stack<T> {
public List<T> multipoprev(int n) {
LinkedList<T> lst = new LinkedList<>();
- for (int i = 0; i < n; i++) {
- lst.addFirst(pop());
- }
+ for (int i = 0; i < n; i++) lst.addFirst(pop());
return lst;
}
@@ -160,9 +152,7 @@ public abstract class Stack<T> {
* The number of items to drop.
*/
public void drop(final int n) {
- for (int i = 0; i < n; i++) {
- pop();
- }
+ for (int i = 0; i < n; i++) pop();
}
/** Drop one item from the stack. */
@@ -201,9 +191,7 @@ public abstract class Stack<T> {
public void multidup(final int n, final int m) {
List<T> lst = multipoprev(n);
- for (int i = 0; i <= m; i++) {
- pushAll(lst);
- }
+ for (int i = 0; i <= m; i++) pushAll(lst);
}
/**
@@ -235,15 +223,11 @@ public abstract class Stack<T> {
List<T> lst = multipoprev(n);
- for (final T nelm : lst) {
- push(nelm);
- }
+ for (final T nelm : lst) push(nelm);
push(elm);
- for (int i = 0; i < m; i++) {
- pushAll(lst);
- }
+ for (int i = 0; i < m; i++) pushAll(lst);
}
/**
@@ -555,9 +539,7 @@ public abstract class Stack<T> {
public void multiapply(final int n, final int m, final Consumer<Stack<T>> action) {
final List<Consumer<Stack<T>>> actions = new ArrayList<>(m);
- for (int i = 0; i < m; i++) {
- actions.add(action);
- }
+ for (int i = 0; i < m; i++) actions.add(action);
multispread(n, actions);
}
diff --git a/src/main/java/bjc/esodata/TapeLibrary.java b/src/main/java/bjc/esodata/TapeLibrary.java
new file mode 100644
index 0000000..922833f
--- /dev/null
+++ b/src/main/java/bjc/esodata/TapeLibrary.java
@@ -0,0 +1,163 @@
+package bjc.esodata;
+
+import java.util.*;
+
+/**
+ * Represents a library of possible tapes, with a single tape 'mounted' or active
+ * at a time.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ElementType> The type stored on each tape.
+ */
+public class TapeLibrary<ElementType> implements TapeView<ElementType>
+{
+ private final Map<String, Tape<ElementType>> library;
+
+ private String currentLabel;
+ private Tape<ElementType> currentTape;
+
+ private boolean allowAutoCreation;
+
+ /**
+ * Get a view of this tape library as a map.
+ *
+ * Modifications to this map will apply to the library, but will not
+ * affect whether a given tape is mounted or not. Be forewarned.
+ *
+ * @return A view onto this tape library as a map.
+ */
+ public Map<String, Tape<ElementType>> asMap()
+ {
+ return library;
+ }
+
+ /**
+ * Create a new empty tape library, with no tape mounted.
+ */
+ public TapeLibrary()
+ {
+ library = new HashMap<>();
+ }
+
+ /**
+ * Create a new tape, with a given tape mounted by default.
+ *
+ * @param label The label of the tape.
+ * @param tape The tape to mount.
+ */
+ public TapeLibrary(String label, Tape<ElementType> tape)
+ {
+ this();
+ library.put(label, tape);
+
+ this.currentLabel = label;
+ this.currentTape = tape;
+ }
+
+ @Override
+ public Tape<ElementType> tapeView()
+ {
+ return currentTape;
+ }
+
+ /**
+ * Insert a tape into this library.
+ *
+ * @param label The label to use for the tape.
+ * @param tape The tape to add.
+ *
+ * @return The tape which previously had that label, or null if there was not one.
+ */
+ public Tape<ElementType> insertTape(String label, Tape<ElementType> tape)
+ {
+ return library.put(label, tape);
+ }
+
+ /**
+ * Remove a tape from this library.
+ *
+ * @param label The label of the tape to remove.
+ *
+ * @return The tape which had that label, or null if there was not one.
+ */
+ public Tape<ElementType> removeTape(String label)
+ {
+ return library.remove(label);
+ }
+
+ /**
+ * Check if this library has a tape with a given label.
+ *
+ * @param label The label of the tape to check for.
+ *
+ * @return Whether or not the library contains a tape with that label.
+ */
+ public boolean hasTape(String label)
+ {
+ return allowAutoCreation ? true : library.containsKey(label);
+ }
+
+ /**
+ * Mount a different tape in the library.
+ *
+ * @param label The label of the tape to mount.
+ *
+ * @return True if the tape was successfully mounted, false otherwise.
+ */
+ public boolean mountTape(String label)
+ {
+ if (library.containsKey(label) || allowAutoCreation)
+ {
+ currentLabel = label;
+ currentTape = library.computeIfAbsent(
+ label,
+ (ignored) -> new SingleTape<>());
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the label of the current tape.
+ *
+ * @return The label of the current tape, or null if no tape is
+ * currently 'mounted'.
+ */
+ public String currentLabel()
+ {
+ return currentLabel;
+ }
+
+ /**
+ * Unmount the currently mounted tape.
+ */
+ public void ejectTape()
+ {
+ currentTape = null;
+ currentLabel = null;
+ }
+
+ /**
+ * Check if this tape library currently allows auto-creation of
+ * non-existing tapes.
+ *
+ * @return Whether or not auto-creation of tapes is currently allowed.
+ */
+ public boolean isAllowAutoCreation() {
+ return allowAutoCreation;
+ }
+
+ /**
+ * Set whether or not this library allows auto-creation of non-existing
+ * tapes.
+ *
+ * @param allowAutoCreation Whether tape auto-creation is allowed.
+ */
+ public void setAllowAutoCreation(boolean allowAutoCreation)
+ {
+ this.allowAutoCreation = allowAutoCreation;
+ }
+}
diff --git a/src/main/java/bjc/esodata/TapeView.java b/src/main/java/bjc/esodata/TapeView.java
new file mode 100644
index 0000000..bfc33a9
--- /dev/null
+++ b/src/main/java/bjc/esodata/TapeView.java
@@ -0,0 +1,102 @@
+package bjc.esodata;
+
+/**
+ * An interface which allows you to view a given type as a tape.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ElementType> The type of element contained in the tape.
+ */
+public interface TapeView<ElementType> extends Tape<ElementType>
+{
+ /**
+ * Return a view of this object as a tape.
+ *
+ * @return A view of this object as a tape.
+ */
+ public Tape<ElementType> tapeView();
+
+ @Override
+ public default ElementType item()
+ {
+ return tapeView().item();
+ }
+
+ @Override
+ public default void item(ElementType itm)
+ {
+ tapeView().item(itm);
+ }
+
+ @Override
+ public default int size()
+ {
+ return tapeView().size();
+ }
+
+ @Override
+ public default int position()
+ {
+ return tapeView().position();
+ }
+
+ @Override
+ public default void insertBefore(ElementType itm)
+ {
+ tapeView().insertBefore(itm);
+ }
+
+ @Override
+ public default void insertAfter(ElementType itm)
+ {
+ tapeView().insertAfter(itm);
+ }
+
+ @Override
+ public default ElementType remove()
+ {
+ return tapeView().remove();
+ }
+
+ @Override
+ public default void first()
+ {
+ tapeView().first();
+ }
+
+ @Override
+ public default void last()
+ {
+ tapeView().last();
+ }
+
+ @Override
+ public default boolean left()
+ {
+ return tapeView().left();
+ }
+
+ @Override
+ public default boolean left(int amt)
+ {
+ return tapeView().left(amt);
+ }
+
+ @Override
+ public default boolean right()
+ {
+ return tapeView().right();
+ }
+
+ @Override
+ public default boolean right(int amt)
+ {
+ return tapeView().right(amt);
+ }
+
+ @Override
+ public default boolean seekTo(int pos)
+ {
+ return tapeView().seekTo(pos);
+ }
+}
diff --git a/src/main/java/bjc/esodata/ThresholdSet.java b/src/main/java/bjc/esodata/ThresholdSet.java
index ae277e1..9b8560b 100644
--- a/src/main/java/bjc/esodata/ThresholdSet.java
+++ b/src/main/java/bjc/esodata/ThresholdSet.java
@@ -37,8 +37,7 @@ public class ThresholdSet<KeyType> {
int ret = ThresholdSet.this.add(key);
// No change to set contents
- if (ret > 2)
- return false;
+ if (ret > 2) return false;
return true;
}
@@ -52,8 +51,7 @@ public class ThresholdSet<KeyType> {
int ret = ThresholdSet.this.remove(k);
// We removed the element.
- if (ret == 0)
- return true;
+ if (ret == 0) return true;
return false;
}
@@ -67,8 +65,7 @@ public class ThresholdSet<KeyType> {
int ret = ThresholdSet.this.contains(k);
// The object is set-visible
- if (ret == 1)
- return true;
+ if (ret == 1) return true;
return false;
}
@@ -112,9 +109,7 @@ public class ThresholdSet<KeyType> {
public int[] addKeys(@SuppressWarnings("unchecked") KeyType... keys) {
int[] ret = new int[keys.length];
- for (int i = 0; i < keys.length; i++) {
- ret[i] = add(keys[i]);
- }
+ for (int i = 0; i < keys.length; i++) ret[i] = add(keys[i]);
return ret;
}
@@ -162,9 +157,7 @@ public class ThresholdSet<KeyType> {
public int[] removeKeys(@SuppressWarnings("unchecked") KeyType... keys) {
int[] ret = new int[keys.length];
- for (int i = 0; i < keys.length; i++) {
- ret[i] = remove(keys[i]);
- }
+ for (int i = 0; i < keys.length; i++) ret[i] = remove(keys[i]);
return ret;
}
@@ -217,9 +210,7 @@ public class ThresholdSet<KeyType> {
public int[] containsKeys(@SuppressWarnings("unchecked") KeyType... keys) {
int[] ret = new int[keys.length];
- for (int i = 0; i < keys.length; i++) {
- ret[i] = contains(keys[i]);
- }
+ for (int i = 0; i < keys.length; i++) ret[i] = contains(keys[i]);
return ret;
}
@@ -233,12 +224,9 @@ public class ThresholdSet<KeyType> {
* @return The number of times the key occurs; -1 if it doesn't occur.
*/
public int contains(KeyType key) {
- if (keySet.contains(key))
- return 1;
- if (!keyMap.containsKey(key))
- return -1;
-
- return keyMap.get(key);
+ if (keySet.contains(key)) return 1;
+ if (!keyMap.containsKey(key)) return -1;
+ else return keyMap.get(key);
}
/**
diff --git a/src/main/java/bjc/esodata/UnifiedDirectory.java b/src/main/java/bjc/esodata/UnifiedDirectory.java
index 2221615..1b630eb 100644
--- a/src/main/java/bjc/esodata/UnifiedDirectory.java
+++ b/src/main/java/bjc/esodata/UnifiedDirectory.java
@@ -1,7 +1,7 @@
package bjc.esodata;
import bjc.funcdata.FunctionalMap;
-import bjc.funcdata.IMap;
+import bjc.funcdata.MapEx;
/**
* Simple implementation of {@link Directory}.
@@ -18,9 +18,9 @@ import bjc.funcdata.IMap;
*/
public class UnifiedDirectory<K, V> implements Directory<K, V> {
/* Our directory children. */
- private final IMap<K, Directory<K, V>> children;
+ private final MapEx<K, Directory<K, V>> children;
/* Our data children. */
- private final IMap<K, V> data;
+ private final MapEx<K, V> data;
/** Create a new directory. */
public UnifiedDirectory() {
@@ -30,7 +30,7 @@ public class UnifiedDirectory<K, V> implements Directory<K, V> {
@Override
public Directory<K, V> getSubdirectory(final K key) {
- return children.get(key);
+ return children.get(key).orElse(null);
}
@Override
@@ -56,7 +56,7 @@ public class UnifiedDirectory<K, V> implements Directory<K, V> {
@Override
public V getKey(final K key) {
- return data.get(key);
+ return data.get(key).orElse(null);
}
@Override
@@ -82,26 +82,23 @@ public class UnifiedDirectory<K, V> implements Directory<K, V> {
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof UnifiedDirectory<?, ?>))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof UnifiedDirectory<?, ?>)) return false;
final UnifiedDirectory<?, ?> other = (UnifiedDirectory<?, ?>) obj;
if (children == null) {
- if (other.children != null)
- return false;
- } else if (!children.equals(other.children))
+ if (other.children != null) return false;
+ } else if (!children.equals(other.children)) {
return false;
+ }
if (data == null) {
- if (other.data != null)
- return false;
- } else if (!data.equals(other.data))
+ if (other.data != null) return false;
+ } else if (!data.equals(other.data)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/bjc/funcdata/ExtendedMap.java b/src/main/java/bjc/funcdata/ExtendedMap.java
index bd500f4..379ff65 100644
--- a/src/main/java/bjc/funcdata/ExtendedMap.java
+++ b/src/main/java/bjc/funcdata/ExtendedMap.java
@@ -1,8 +1,7 @@
package bjc.funcdata;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
+import java.util.*;
+import java.util.function.*;
/**
* An extended version of a map, that stores values into a map, but can look
@@ -16,12 +15,15 @@ import java.util.function.Function;
* @param <ValueType>
* The type of the values of the map.
*/
-class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
+class ExtendedMap<KeyType, ValueType> implements MapEx<KeyType, ValueType> {
/* The map we delegate lookups to. */
- private final IMap<KeyType, ValueType> delegate;
+ private final MapEx<KeyType, ValueType> delegate;
/* The map we store things in. */
- private final IMap<KeyType, ValueType> store;
+ private final MapEx<KeyType, ValueType> store;
+ private boolean isFrozen = false;
+ private boolean thawEnabled = true;
+
/**
* Create a new extended map.
*
@@ -31,28 +33,23 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
* @param store
* The map to store things in.
*/
- public ExtendedMap(final IMap<KeyType, ValueType> delegate,
- final IMap<KeyType, ValueType> store) {
+ public ExtendedMap(final MapEx<KeyType, ValueType> delegate,
+ final MapEx<KeyType, ValueType> store) {
this.delegate = delegate;
this.store = store;
}
@Override
public void clear() {
+ if (isFrozen) return;
+
store.clear();
}
@Override
public boolean containsKey(final KeyType key) {
- if (store.containsKey(key))
- return true;
-
- return delegate.containsKey(key);
- }
-
- @Override
- public IMap<KeyType, ValueType> extend() {
- return new ExtendedMap<>(this, new FunctionalMap<>());
+ if (store.containsKey(key)) return true;
+ else return delegate.containsKey(key);
}
@Override
@@ -63,25 +60,9 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
}
@Override
- public void forEachKey(final Consumer<KeyType> action) {
- store.forEachKey(action);
-
- delegate.forEachKey(action);
- }
-
- @Override
- public void forEachValue(final Consumer<ValueType> action) {
- store.forEachValue(action);
-
- delegate.forEachValue(action);
- }
-
- @Override
- public ValueType get(final KeyType key) {
- if (store.containsKey(key))
- return store.get(key);
-
- return delegate.get(key);
+ public Optional<ValueType> get(final KeyType key) {
+ if (store.containsKey(key)) return store.get(key);
+ else return delegate.get(key);
}
@Override
@@ -90,8 +71,8 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
}
@Override
- public IList<KeyType> keyList() {
- IList<KeyType> ilst = new FunctionalList<>();
+ public ListEx<KeyType> keyList() {
+ ListEx<KeyType> ilst = new FunctionalList<>();
ilst.addAll(store.keyList());
ilst.addAll(delegate.keyList());
@@ -100,70 +81,84 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
}
@Override
- public <MappedValue> IMap<KeyType, MappedValue>
- transform(final Function<ValueType, MappedValue> transformer) {
- return new TransformedValueMap<>(this, transformer);
- }
-
- @Override
public ValueType put(final KeyType key, final ValueType val) {
+ if (isFrozen)
+ throw new ObjectFrozen("Can't insert key " + key + " into frozen map");
+
return store.put(key, val);
}
@Override
public ValueType remove(final KeyType key) {
- if (!store.containsKey(key))
- return delegate.remove(key);
-
- return store.remove(key);
+ if (isFrozen)
+ throw new ObjectFrozen("Can't remove key " + key + " from frozen map");
+
+ if (!store.containsKey(key)) return delegate.remove(key);
+ else return store.remove(key);
}
@Override
- public IList<ValueType> valueList() {
- IList<ValueType> ilst = new FunctionalList<>();
+ public int hashCode() {
+ // isFrozen isn't counted
+ return Objects.hash(delegate, store);
+ }
- ilst.addAll(store.valueList());
- ilst.addAll(delegate.valueList());
+ /* Misc. object support. */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
- return ilst;
- }
+ ExtendedMap<?, ?> other = (ExtendedMap<?, ?>) obj;
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + (delegate == null ? 0 : delegate.hashCode());
- result = prime * result + (store == null ? 0 : store.hashCode());
- return result;
+ // isFrozen isn't counted
+ return Objects.equals(delegate, other.delegate) && Objects.equals(store, other.store);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("ExtendedMap [delegate=%s, store=%s]", delegate, store);
}
+ /* IFreezable support */
+
@Override
- public boolean equals(final Object obj) {
- if (this == obj)
+ public boolean freeze() {
+ isFrozen = true;
+
return true;
- if (obj == null)
- return false;
- if (!(obj instanceof ExtendedMap))
- return false;
+ }
- final ExtendedMap<?, ?> other = (ExtendedMap<?, ?>) obj;
+ @Override
+ public boolean deepFreeze() {
+ thawEnabled = false;
- if (delegate == null) {
- if (other.delegate != null)
- return false;
- } else if (!delegate.equals(other.delegate))
- return false;
- if (store == null) {
- if (other.store != null)
- return false;
- } else if (!store.equals(other.store))
+ return freeze();
+ }
+
+ @Override
+ public boolean thaw() {
+ if (thawEnabled) {
+ isFrozen = false;
+ return true;
+ } else {
return false;
+ }
+ }
+ @Override
+ public boolean isFrozen() {
+ return isFrozen;
+ }
+
+ @Override
+ public boolean canFreeze() {
return true;
}
-
+
@Override
- public String toString() {
- return String.format("ExtendedMap [delegate=%s, store=%s]", delegate, store);
+ public boolean canThaw() {
+ return thawEnabled;
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/bjc/funcdata/Freezable.java b/src/main/java/bjc/funcdata/Freezable.java
new file mode 100644
index 0000000..e83accb
--- /dev/null
+++ b/src/main/java/bjc/funcdata/Freezable.java
@@ -0,0 +1,73 @@
+package bjc.funcdata;
+
+/**
+ * Indicates that an object can switch between immutable and mutable modes.
+ *
+ * Note that this only implements 'shallow' immutability. Namely, any sub-objects
+ * are not made immutable, and if the type is a collection, the elements are still
+ * as mutable as they were before.
+ *
+ * Implementations of this interface may choose to throw {@link ObjectFrozen} if
+ * you attempt to modify a frozen object, but they may also choose not to.
+ *
+ * @author Ben Culkin
+ */
+public interface Freezable {
+ /**
+ * Freezes the internal state of this object, making it immutable.
+ *
+ * @return True if the object is frozen, false if it couldn't be frozen.
+ */
+ public boolean freeze();
+ /**
+ * Thaws the internal state of this object, making it mutable.
+ *
+ * @return True if the object is thawed, false if it couldn't be thawed.
+ */
+ public boolean thaw();
+
+ /**
+ * 'Deep-freeze' this object, making it immutable and disabling the ability to
+ * thaw it.
+ *
+ * @return True if the object was deep-frozen, false if that couldn't happen.
+ */
+ default boolean deepFreeze() {
+ return false;
+ }
+
+ /**
+ * Check if this object can be frozen.
+ *
+ * @return Whether or not the object can be frozen.
+ */
+ default boolean canFreeze() {
+ return false;
+ }
+
+ /**
+ * Checks if this object can be thawed.
+ *
+ * @return Whether or not the object can be thawed.
+ */
+ default boolean canThaw() {
+ return false;
+ }
+
+ /**
+ * Determines if this object is frozen.
+ *
+ * @return True if the object is frozen, false if the object is thawed.
+ */
+ public boolean isFrozen();
+
+
+ /**
+ * Determines if this object is thawed.
+ *
+ * @return True if the object is thawed, false if the object is thawed.
+ */
+ default boolean isThawed() {
+ return !isFrozen();
+ }
+}
diff --git a/src/main/java/bjc/funcdata/FunctionalList.java b/src/main/java/bjc/funcdata/FunctionalList.java
index 2cdfa27..88f49c4 100644
--- a/src/main/java/bjc/funcdata/FunctionalList.java
+++ b/src/main/java/bjc/funcdata/FunctionalList.java
@@ -12,10 +12,10 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
-import bjc.data.IHolder;
-import bjc.data.IPair;
-import bjc.data.Identity;
+import bjc.data.Holder;
import bjc.data.Pair;
+import bjc.data.Identity;
+import bjc.data.SimplePair;
/**
* A wrapper over another list that provides eager functional operations over
@@ -29,7 +29,7 @@ import bjc.data.Pair;
* @param <E>
* The type in this list
*/
-public class FunctionalList<E> implements Cloneable, IList<E> {
+public class FunctionalList<E> implements Cloneable, ListEx<E> {
/* The list used as a backing store */
private final List<E> wrapped;
@@ -60,20 +60,21 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
*
* Takes O(n) time, where n is the number of items specified
*
- * @param items
- * The items to put into this functional list.
+ * @param <T> The type of items to put into the list.
+ *
+ * @param items The items to put into this functional list.
+ *
* @return The returned list.
*/
@SafeVarargs
- public static <T> IList<T> listOf(final T... items) {
+ public static <T> ListEx<T> listOf(final T... items) {
return new FunctionalList<>(items);
}
/**
* Create a new functional list with the specified size.
*
- * @param size
- * The size of the backing list .
+ * @param size The size of the backing list .
*/
private FunctionalList(final int size) {
wrapped = new ArrayList<>(size);
@@ -84,8 +85,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
*
* Takes O(1) time, since it doesn't copy the list.
*
- * @param backing
- * The list to use as a backing list.
+ * @param backing The list to use as a backing list.
*/
public FunctionalList(final List<E> backing) {
if (backing == null)
@@ -137,8 +137,8 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
* @return A copy of the list.
*/
@Override
- public IList<E> clone() {
- final IList<E> cloned = new FunctionalList<>();
+ public ListEx<E> clone() {
+ final ListEx<E> cloned = new FunctionalList<>();
for (final E element : wrapped) {
cloned.add(element);
@@ -148,7 +148,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public <T, F> IList<F> combineWith(final IList<T> rightList,
+ public <T, F> ListEx<F> combineWith(final ListEx<T> rightList,
final BiFunction<E, T, F> itemCombiner) {
if (rightList == null) {
throw new NullPointerException("Target combine list must not be null");
@@ -156,7 +156,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
throw new NullPointerException("Combiner must not be null");
}
- final IList<F> returned = new FunctionalList<>();
+ final ListEx<F> returned = new FunctionalList<>();
/* Get the iterator for the other list. */
final Iterator<T> rightIterator = rightList.toIterable().iterator();
@@ -222,14 +222,14 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public <T> IList<T> flatMap(final Function<E, IList<T>> expander) {
+ public <T> ListEx<T> flatMap(final Function<E, ListEx<T>> expander) {
if (expander == null)
throw new NullPointerException("Expander must not be null");
- final IList<T> returned = new FunctionalList<>(this.wrapped.size());
+ final ListEx<T> returned = new FunctionalList<>(this.wrapped.size());
forEach(element -> {
- final IList<T> expandedElement = expander.apply(element);
+ final ListEx<T> expandedElement = expander.apply(element);
if (expandedElement == null)
throw new NullPointerException("Expander returned null list");
@@ -257,7 +257,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
/*
* This is held b/c ref'd variables must be final/effectively final.
*/
- final IHolder<Integer> currentIndex = new Identity<>(0);
+ final Holder<Integer> currentIndex = new Identity<>(0);
wrapped.forEach(element -> {
/* Call the action with the index and the value. */
@@ -283,11 +283,11 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public IList<E> getMatching(final Predicate<E> predicate) {
+ public ListEx<E> getMatching(final Predicate<E> predicate) {
if (predicate == null)
throw new NullPointerException("Predicate must not be null");
- final IList<E> returned = new FunctionalList<>();
+ final ListEx<E> returned = new FunctionalList<>();
wrapped.forEach(element -> {
if (predicate.test(element)) {
@@ -313,17 +313,17 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
/* Check if a partition has room for another item. */
private Boolean isPartitionFull(final int numberPerPartition,
- final IHolder<IList<E>> currentPartition) {
+ final Holder<ListEx<E>> currentPartition) {
return currentPartition
.unwrap(partition -> partition.getSize() >= numberPerPartition);
}
@Override
- public <T> IList<T> map(final Function<E, T> elementTransformer) {
+ public <T> ListEx<T> map(final Function<E, T> elementTransformer) {
if (elementTransformer == null)
throw new NullPointerException("Transformer must be not null");
- final IList<T> returned = new FunctionalList<>(this.wrapped.size());
+ final ListEx<T> returned = new FunctionalList<>(this.wrapped.size());
forEach(element -> {
// Add the transformed item to the result
@@ -334,12 +334,12 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public <T> IList<IPair<E, T>> pairWith(final IList<T> rightList) {
- return combineWith(rightList, Pair<E, T>::new);
+ public <T> ListEx<Pair<E, T>> pairWith(final ListEx<T> rightList) {
+ return combineWith(rightList, SimplePair<E, T>::new);
}
@Override
- public IList<IList<E>> partition(final int numberPerPartition) {
+ public ListEx<ListEx<E>> partition(final int numberPerPartition) {
if (numberPerPartition < 1 || numberPerPartition > wrapped.size()) {
final String fmt
= "%s is an invalid partition size. Must be between 1 and %d";
@@ -348,10 +348,10 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
throw new IllegalArgumentException(msg);
}
- final IList<IList<E>> returned = new FunctionalList<>();
+ final ListEx<ListEx<E>> returned = new FunctionalList<>();
/* The current partition being filled. */
- final IHolder<IList<E>> currentPartition = new Identity<>(new FunctionalList<>());
+ final Holder<ListEx<E>> currentPartition = new Identity<>(new FunctionalList<>());
this.forEach(element -> {
if (isPartitionFull(numberPerPartition, currentPartition)) {
@@ -395,7 +395,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
/* The current collapsed list. */
- final IHolder<T> currentState = new Identity<>(initialValue);
+ final Holder<T> currentState = new Identity<>(initialValue);
wrapped.forEach(element -> {
/* Accumulate a new value into the state. */
@@ -444,7 +444,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public IList<E> tail() {
+ public ListEx<E> tail() {
return new FunctionalList<>(wrapped.subList(1, getSize()));
}
diff --git a/src/main/java/bjc/funcdata/FunctionalMap.java b/src/main/java/bjc/funcdata/FunctionalMap.java
index aba3dd1..3427a91 100644
--- a/src/main/java/bjc/funcdata/FunctionalMap.java
+++ b/src/main/java/bjc/funcdata/FunctionalMap.java
@@ -1,15 +1,12 @@
package bjc.funcdata;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
+import java.util.*;
+import java.util.function.*;
-import bjc.data.IPair;
+import bjc.data.*;
/**
- * Basic implementation of {@link IMap}
+ * Basic implementation of {@link MapEx}
*
* @author ben
*
@@ -19,10 +16,13 @@ import bjc.data.IPair;
* @param <ValueType>
* The type of the map's values.
*/
-public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
+public class FunctionalMap<KeyType, ValueType> implements MapEx<KeyType, ValueType> {
/* Our backing store. */
private Map<KeyType, ValueType> wrappedMap;
+ private boolean isFrozen = false;
+ private boolean thawEnabled = true;
+
/** Create a new blank functional map */
public FunctionalMap() {
wrappedMap = new HashMap<>();
@@ -35,13 +35,11 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
* The entries to put into the map.
*/
@SafeVarargs
- public FunctionalMap(final IPair<KeyType, ValueType>... entries) {
+ public FunctionalMap(final Pair<KeyType, ValueType>... entries) {
this();
- for (final IPair<KeyType, ValueType> entry : entries) {
- entry.doWith((key, val) -> {
- wrappedMap.put(key, val);
- });
+ for (final Pair<KeyType, ValueType> entry : entries) {
+ entry.doWith(wrappedMap::put);
}
}
@@ -52,14 +50,15 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
* The map to wrap.
*/
public FunctionalMap(final Map<KeyType, ValueType> wrap) {
- if (wrap == null)
- throw new NullPointerException("Map to wrap must not be null");
+ if (wrap == null) throw new NullPointerException("Map to wrap must not be null");
wrappedMap = wrap;
}
@Override
public void clear() {
+ if (isFrozen) throw new ObjectFrozen("Can't clear frozen map");
+
wrappedMap.clear();
}
@@ -69,37 +68,19 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
}
@Override
- public IMap<KeyType, ValueType> extend() {
- return new ExtendedMap<>(this, new FunctionalMap<>());
- }
-
- @Override
public void forEach(final BiConsumer<KeyType, ValueType> action) {
wrappedMap.forEach(action);
}
@Override
- public void forEachKey(final Consumer<KeyType> action) {
- wrappedMap.keySet().forEach(action);
- }
+ public Optional<ValueType> get(final KeyType key) {
+ if (key == null) throw new NullPointerException("Key must not be null");
- @Override
- public void forEachValue(final Consumer<ValueType> action) {
- wrappedMap.values().forEach(action);
- }
-
- @Override
- public ValueType get(final KeyType key) {
- if (key == null)
- throw new NullPointerException("Key must not be null");
-
- if (!wrappedMap.containsKey(key)) {
- final String msg = String.format("Key %s is not present in the map", key);
-
- throw new IllegalArgumentException(msg);
+ if (wrappedMap.containsKey(key)) {
+ return Optional.of(wrappedMap.get(key));
+ } else {
+ return Optional.empty();
}
-
- return wrappedMap.get(key);
}
@Override
@@ -108,35 +89,26 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
}
@Override
- public IList<KeyType> keyList() {
+ public ListEx<KeyType> keyList() {
final FunctionalList<KeyType> keys = new FunctionalList<>();
- wrappedMap.keySet().forEach(key -> {
- keys.add(key);
- });
+ wrappedMap.keySet().forEach(keys::add);
return keys;
}
@Override
- public <MappedValue> IMap<KeyType, MappedValue>
- transform(final Function<ValueType, MappedValue> transformer) {
- if (transformer == null)
- throw new NullPointerException("Transformer must not be null");
-
- return new TransformedValueMap<>(this, transformer);
- }
-
- @Override
public ValueType put(final KeyType key, final ValueType val) {
- if (key == null)
- throw new NullPointerException("Key must not be null");
+ if (isFrozen) throw new ObjectFrozen("Can't put key " + key + " into frozen map");
+ if (key == null) throw new NullPointerException("Key must not be null");
return wrappedMap.put(key, val);
}
@Override
public ValueType remove(final KeyType key) {
+ if (isFrozen) throw new ObjectFrozen("Can't remove key " + key + " from frozen map");
+
return wrappedMap.remove(key);
}
@@ -144,18 +116,7 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
public String toString() {
return wrappedMap.toString();
}
-
- @Override
- public IList<ValueType> valueList() {
- final FunctionalList<ValueType> values = new FunctionalList<>();
-
- wrappedMap.values().forEach(value -> {
- values.add(value);
- });
-
- return values;
- }
-
+
@Override
public int hashCode() {
final int prime = 31;
@@ -166,20 +127,58 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
@Override
public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof FunctionalMap))
- return false;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof FunctionalMap)) return false;
final FunctionalMap<?, ?> other = (FunctionalMap<?, ?>) obj;
if (wrappedMap == null) {
- if (other.wrappedMap != null)
- return false;
- } else if (!wrappedMap.equals(other.wrappedMap))
+ if (other.wrappedMap != null) return false;
+ } else if (!wrappedMap.equals(other.wrappedMap)) {
return false;
+ }
+
+ return true;
+ }
+
+ // IFreezable support
+ @Override
+ public boolean freeze() {
+ isFrozen = true;
+
return true;
}
+
+ @Override
+ public boolean thaw() {
+ if (thawEnabled) {
+ isFrozen = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean deepFreeze() {
+ thawEnabled = false;
+
+ return freeze();
+ }
+
+ @Override
+ public boolean canFreeze() {
+ return true;
+ }
+
+ @Override
+ public boolean canThaw() {
+ return thawEnabled;
+ }
+
+ @Override
+ public boolean isFrozen() {
+ return isFrozen;
+ }
}
diff --git a/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java b/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java
index 856c153..3344e05 100644
--- a/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java
+++ b/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java
@@ -128,7 +128,7 @@ public class FunctionalStringTokenizer {
*
* @return This tokenizer, converted into a list of strings.
*/
- public IList<String> toList() {
+ public ListEx<String> toList() {
return toList((final String element) -> element);
}
@@ -144,11 +144,11 @@ public class FunctionalStringTokenizer {
*
* @return A list containing all of the converted tokens.
*/
- public <E> IList<E> toList(final Function<String, E> transformer) {
+ public <E> ListEx<E> toList(final Function<String, E> transformer) {
if (transformer == null)
throw new NullPointerException("Transformer must not be null");
- final IList<E> returned = new FunctionalList<>();
+ final ListEx<E> returned = new FunctionalList<>();
/* Add each token to the list after transforming it. */
forEachToken(token -> {
diff --git a/src/main/java/bjc/funcdata/IList.java b/src/main/java/bjc/funcdata/ListEx.java
index 0ff8e30..164a71d 100644
--- a/src/main/java/bjc/funcdata/IList.java
+++ b/src/main/java/bjc/funcdata/ListEx.java
@@ -9,7 +9,7 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
-import bjc.data.IPair;
+import bjc.data.Pair;
import bjc.functypes.ID;
/**
@@ -20,7 +20,7 @@ import bjc.functypes.ID;
* @param <ContainedType>
* The type in this list
*/
-public interface IList<ContainedType> extends Iterable<ContainedType> {
+public interface ListEx<ContainedType> extends Iterable<ContainedType> {
/**
* Add an item to this list.
*
@@ -40,7 +40,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
* @return True if every item was successfully added to the list, false
* otherwise.
*/
- default boolean addAll(final IList<ContainedType> items) {
+ default boolean addAll(final ListEx<ContainedType> items) {
return items.map(this::add).anyMatch(bl -> bl == false);
}
@@ -136,7 +136,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return A new list containing the merged pairs of lists.
*/
- <OtherType, CombinedType> IList<CombinedType> combineWith(IList<OtherType> list,
+ <OtherType, CombinedType> ListEx<CombinedType> combineWith(ListEx<OtherType> list,
BiFunction<ContainedType, OtherType, CombinedType> combiner);
/**
@@ -191,8 +191,8 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
* @return A new list containing the flattened results of applying the provided
* function.
*/
- <MappedType> IList<MappedType>
- flatMap(Function<ContainedType, IList<MappedType>> expander);
+ <MappedType> ListEx<MappedType>
+ flatMap(Function<ContainedType, ListEx<MappedType>> expander);
/**
* Apply a given action for each member of the list.
@@ -230,7 +230,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return A list containing all elements that match the predicate.
*/
- IList<ContainedType> getMatching(Predicate<ContainedType> predicate);
+ ListEx<ContainedType> getMatching(Predicate<ContainedType> predicate);
/**
* Retrieve the size of the wrapped list.
@@ -259,7 +259,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return A new list containing the mapped elements of this list.
*/
- <MappedType> IList<MappedType> map(Function<ContainedType, MappedType> transformer);
+ <MappedType> ListEx<MappedType> map(Function<ContainedType, MappedType> transformer);
/**
* Zip two lists into a list of pairs.
@@ -272,7 +272,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return A list containing pairs of this element and the specified list.
*/
- <OtherType> IList<IPair<ContainedType, OtherType>> pairWith(IList<OtherType> list);
+ <OtherType> ListEx<Pair<ContainedType, OtherType>> pairWith(ListEx<OtherType> list);
/**
* Partition this list into a list of sublists.
@@ -285,7 +285,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
* partition may not be completely full if the size of the list is not a
* multiple of partitionSize.
*/
- IList<IList<ContainedType>> partition(int partitionSize);
+ ListEx<ListEx<ContainedType>> partition(int partitionSize);
/**
* Prepend an item to the list.
@@ -429,7 +429,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return The list without the first element.
*/
- IList<ContainedType> tail();
+ ListEx<ContainedType> tail();
/**
* Convert this list into an array.
diff --git a/src/main/java/bjc/funcdata/IMap.java b/src/main/java/bjc/funcdata/MapEx.java
index dc5ee00..a7af4c5 100644
--- a/src/main/java/bjc/funcdata/IMap.java
+++ b/src/main/java/bjc/funcdata/MapEx.java
@@ -1,8 +1,7 @@
package bjc.funcdata;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
+import java.util.*;
+import java.util.function.*;
/**
* Functional wrapper over map providing some useful things.
@@ -15,7 +14,7 @@ import java.util.function.Function;
* @param <ValueType>
* The type of this map's values.
*/
-public interface IMap<KeyType, ValueType> {
+public interface MapEx<KeyType, ValueType> extends Freezable {
/**
* Execute an action for each entry in the map.
*
@@ -62,31 +61,7 @@ public interface IMap<KeyType, ValueType> {
*
* @return The value of the key.
*/
- ValueType get(KeyType key);
-
- /**
- * Get a value from the map, and return a default value if the key doesn't
- * exist.
- *
- * @param key
- * The key to attempt to retrieve.
- *
- * @param defaultValue
- * The value to return if the key doesn't exist.
- *
- * @return The value associated with the key, or the default value if the key
- * doesn't exist.
- */
- default ValueType getOrDefault(final KeyType key, final ValueType defaultValue) {
- try {
- return get(key);
- } catch (final IllegalArgumentException iaex) {
- /*
- * We don't care about this, because it indicates a key is missing.
- */
- return defaultValue;
- }
- }
+ Optional<ValueType> get(KeyType key);
/**
* Add an entry to the map.
@@ -109,7 +84,9 @@ public interface IMap<KeyType, ValueType> {
/** Delete all the values in the map. */
default void clear() {
- keyList().forEach(key -> remove(key));
+ if (isFrozen()) throw new ObjectFrozen("Can't clear a frozen map");
+
+ keyList().forEach(MapEx.this::remove);
}
/**
@@ -121,11 +98,47 @@ public interface IMap<KeyType, ValueType> {
return keyList().getSize();
}
+ /**
+ * Remove the value bound to the key.
+ *
+ * @param key
+ * The key to remove from the map.
+ *
+ * @return The previous value for the key in the map, or null if the key wasn't
+ * in the class. NOTE: Just because you received null, doesn't mean the
+ * map wasn't changed. It may mean that someone put a null value for
+ * that key into the map.
+ */
+ ValueType remove(KeyType key);
+
+ /**
+ * Get a list of all the keys in this map.
+ *
+ * @return A list of all the keys in this map.
+ */
+ ListEx<KeyType> keyList();
+
+ /**
+ * Get a list of the values in this map.
+ *
+ * @return A list of values in this map.
+ */
+ default ListEx<ValueType> valueList() {
+ final ListEx<ValueType> returns = new FunctionalList<>();
+
+ for (final KeyType key : keyList()) {
+ returns.add(get(key).orElse(null));
+ }
+
+ return returns;
+ }
+
/*
* @NOTE Do we want this to be the semantics for transform, or do we want to go
* to semantics using something like Isomorphism, or doing a one-time bulk
* conversion of the values?
*/
+
/**
* Transform the values returned by this map.
*
@@ -141,7 +154,7 @@ public interface IMap<KeyType, ValueType> {
*
* @return The map where each value will be transformed after lookup.
*/
- default <V2> IMap<KeyType, V2> transform(final Function<ValueType, V2> transformer) {
+ default <V2> MapEx<KeyType, V2> transform(final Function<ValueType, V2> transformer) {
return new TransformedValueMap<>(this, transformer);
}
@@ -151,40 +164,53 @@ public interface IMap<KeyType, ValueType> {
*
* @return An extended map.
*/
- IMap<KeyType, ValueType> extend();
+ default MapEx<KeyType, ValueType> extend() {
+ return extend(new FunctionalMap<>());
+ };
+
/**
- * Remove the value bound to the key.
- *
- * @param key
- * The key to remove from the map.
- *
- * @return The previous value for the key in the map, or null if the key wasn't
- * in the class. NOTE: Just because you received null, doesn't mean the
- * map wasn't changed. It may mean that someone put a null value for
- * that key into the map.
+ * Extend this map, creating a new map that will delegate queries to
+ * the current map but store any added values in the provided map.
+ *
+ * @param backer The map to store values in.
+ *
+ * @return An extended map, with the specified backing map.
*/
- ValueType remove(KeyType key);
-
+ default MapEx<KeyType, ValueType> extend(MapEx<KeyType, ValueType> backer) {
+ return new ExtendedMap<>(this, backer);
+ };
+
/**
- * Get a list of all the keys in this map.
- *
- * @return A list of all the keys in this map.
+ * Static method to create a basic instance of IMap.
+ *
+ * @param <KeyType2> The key type of the map.
+ * @param <ValueType2> The value type of the map.
+ *
+ * @param args A series of key-value pairs. You will get an error if you don't
+ * provide the correct number of arguments (a number divisible by 2);
+ * however, if you pass arguments of the wrong type, you will not
+ * get a compile error, and usually won't get a runtime error in
+ * construction. However, you will get a ClassCastException as soon
+ * as you do something that attempts to iterate over the keys or values
+ * of the map.
+ *
+ * @return A map, constructed from the provided values.
+ *
+ * @throws IllegalArgumentException If you provide an incomplete pair of arguments.
*/
- IList<KeyType> keyList();
-
- /**
- * Get a list of the values in this map.
- *
- * @return A list of values in this map.
- */
- default IList<ValueType> valueList() {
- final IList<ValueType> returns = new FunctionalList<>();
-
- for (final KeyType key : keyList()) {
- returns.add(get(key));
+ @SuppressWarnings("unchecked")
+ static <KeyType2, ValueType2> MapEx<KeyType2, ValueType2> of(Object... args) {
+ if (args.length % 2 != 0) throw new IllegalArgumentException("Args must be in the form of key-value pairs");
+
+ MapEx<KeyType2, ValueType2> map = new FunctionalMap<>();
+ for (int index = 0; index < args.length; index += 2) {
+ KeyType2 key = (KeyType2) args[index];
+ ValueType2 value = (ValueType2) args[index + 1];
+
+ map.put(key, value);
}
-
- return returns;
+
+ return map;
}
}
diff --git a/src/main/java/bjc/funcdata/ObjectFrozen.java b/src/main/java/bjc/funcdata/ObjectFrozen.java
new file mode 100644
index 0000000..a9f58b8
--- /dev/null
+++ b/src/main/java/bjc/funcdata/ObjectFrozen.java
@@ -0,0 +1,47 @@
+package bjc.funcdata;
+
+/**
+ * Exception that implementations of {@link Freezable} can throw if you attempt
+ * to modify a frozen object.
+ *
+ * @author Ben Culkin
+ *
+ */
+public class ObjectFrozen extends RuntimeException {
+ private static final long serialVersionUID = -1567447627139090728L;
+
+ /**
+ * Create a new ObjectFrozen exception.
+ */
+ public ObjectFrozen() {
+ super();
+ }
+
+ /**
+ * Create a new ObjectFrozen exception.
+ *
+ * @param message The message of the exception.
+ */
+ public ObjectFrozen(String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new ObjectFrozen exception.
+ *
+ * @param cause The root cause of this exception.
+ */
+ public ObjectFrozen(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Create a new ObjectFrozen exception.
+ *
+ * @param message The message of the exception.
+ * @param cause The root cause of this exception.
+ */
+ public ObjectFrozen(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/bjc/funcdata/TransformedValueMap.java b/src/main/java/bjc/funcdata/TransformedValueMap.java
index 5de6fc3..643f610 100644
--- a/src/main/java/bjc/funcdata/TransformedValueMap.java
+++ b/src/main/java/bjc/funcdata/TransformedValueMap.java
@@ -1,8 +1,7 @@
package bjc.funcdata;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
+import java.util.*;
+import java.util.function.*;
/**
* A map that transforms values from one type to another
@@ -20,12 +19,15 @@ import java.util.function.Function;
*
*/
final class TransformedValueMap<OldKey, OldValue, NewValue>
- implements IMap<OldKey, NewValue> {
+ implements MapEx<OldKey, NewValue> {
/* Our backing map. */
- private final IMap<OldKey, OldValue> backing;
+ private final MapEx<OldKey, OldValue> backing;
/* Our transforming function. */
private final Function<OldValue, NewValue> transformer;
+ private boolean isFrozen = false;
+ private boolean thawEnabled = true;
+
/**
* Create a new transformed-value loop.
*
@@ -35,7 +37,7 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
* @param transform
* The function to use for the transform.
*/
- public TransformedValueMap(final IMap<OldKey, OldValue> backingMap,
+ public TransformedValueMap(final MapEx<OldKey, OldValue> backingMap,
final Function<OldValue, NewValue> transform) {
backing = backingMap;
transformer = transform;
@@ -43,6 +45,8 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
@Override
public void clear() {
+ if (isFrozen) throw new ObjectFrozen("Can't clear frozen map");
+
backing.clear();
}
@@ -52,11 +56,6 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
}
@Override
- public IMap<OldKey, NewValue> extend() {
- return new ExtendedMap<>(this, new FunctionalMap<>());
- }
-
- @Override
public void forEach(final BiConsumer<OldKey, NewValue> action) {
backing.forEach((key, value) -> {
action.accept(key, transformer.apply(value));
@@ -64,20 +63,8 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
}
@Override
- public void forEachKey(final Consumer<OldKey> action) {
- backing.forEachKey(action);
- }
-
- @Override
- public void forEachValue(final Consumer<NewValue> action) {
- backing.forEachValue(value -> {
- action.accept(transformer.apply(value));
- });
- }
-
- @Override
- public NewValue get(final OldKey key) {
- return transformer.apply(backing.get(key));
+ public Optional<NewValue> get(final OldKey key) {
+ return backing.get(key).map(transformer);
}
@Override
@@ -86,23 +73,19 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
}
@Override
- public IList<OldKey> keyList() {
+ public ListEx<OldKey> keyList() {
return backing.keyList();
}
@Override
- public <MappedValue> IMap<OldKey, MappedValue>
- transform(final Function<NewValue, MappedValue> transform) {
- return new TransformedValueMap<>(this, transform);
- }
-
- @Override
public NewValue put(final OldKey key, final NewValue value) {
throw new UnsupportedOperationException("Can't add items to transformed map");
}
@Override
public NewValue remove(final OldKey key) {
+ if (isFrozen) throw new ObjectFrozen("Can't remove key " + key + " from frozen map");
+
return transformer.apply(backing.remove(key));
}
@@ -110,9 +93,43 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
public String toString() {
return backing.toString();
}
+
+ @Override
+ public boolean freeze() {
+ isFrozen = true;
+
+ return true;
+ }
+
+ @Override
+ public boolean thaw() {
+ if (thawEnabled) {
+ isFrozen = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
@Override
- public IList<NewValue> valueList() {
- return backing.valueList().map(transformer);
+ public boolean deepFreeze() {
+ thawEnabled = false;
+
+ return freeze();
+ }
+
+ @Override
+ public boolean canFreeze() {
+ return true;
+ }
+
+ @Override
+ public boolean canThaw() {
+ return thawEnabled;
+ }
+
+ @Override
+ public boolean isFrozen() {
+ return isFrozen;
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTree.java b/src/main/java/bjc/funcdata/bst/BinarySearchTree.java
index e22a8da..99b67cd 100644
--- a/src/main/java/bjc/funcdata/bst/BinarySearchTree.java
+++ b/src/main/java/bjc/funcdata/bst/BinarySearchTree.java
@@ -6,7 +6,7 @@ import java.util.List;
import java.util.function.Predicate;
import bjc.funcdata.FunctionalList;
-import bjc.funcdata.IList;
+import bjc.funcdata.ListEx;
/**
* A binary search tree, with some mild support for functional traversal.
@@ -24,7 +24,7 @@ public class BinarySearchTree<T> {
private int elementCount;
/* The root element of the tree */
- private ITreePart<T> root;
+ private TreePart<T> root;
/**
* Create a new tree using the specified way to compare elements.
@@ -33,8 +33,7 @@ public class BinarySearchTree<T> {
* The thing to use for comparing elements
*/
public BinarySearchTree(final Comparator<T> cmp) {
- if (cmp == null)
- throw new NullPointerException("Comparator must not be null");
+ if (cmp == null) throw new NullPointerException("Comparator must not be null");
elementCount = 0;
comparator = cmp;
@@ -49,11 +48,8 @@ public class BinarySearchTree<T> {
public void addNode(final T element) {
elementCount++;
- if (root == null) {
- root = new BinarySearchTreeNode<>(element, null, null);
- } else {
- root.add(element, comparator);
- }
+ if (root == null) root = new BinarySearchTreeNode<>(element, null, null);
+ else root.add(element, comparator);
}
/**
@@ -70,7 +66,7 @@ public class BinarySearchTree<T> {
*
* @return Whether the adjusted pivot is with the list.
*/
- private boolean adjustedPivotInBounds(final IList<T> elements, final int pivot,
+ private boolean adjustedPivotInBounds(final ListEx<T> elements, final int pivot,
final int pivotAdjustment) {
return ((pivot - pivotAdjustment) >= 0)
&& ((pivot + pivotAdjustment) < elements.getSize());
@@ -82,7 +78,7 @@ public class BinarySearchTree<T> {
* Takes O(N) time, but also O(N) space.
*/
public void balance() {
- final IList<T> elements = new FunctionalList<>();
+ final ListEx<T> elements = new FunctionalList<>();
/* Add each element to the list in sorted order. */
root.forEach(TreeLinearizationMethod.INORDER, element -> elements.add(element));
@@ -139,7 +135,7 @@ public class BinarySearchTree<T> {
*
* @return The root of the tree.
*/
- public ITreePart<T> getRoot() {
+ public TreePart<T> getRoot() {
return root;
}
@@ -184,6 +180,7 @@ public class BinarySearchTree<T> {
*/
traverse(TreeLinearizationMethod.PREORDER, node -> {
nodes.add(node);
+
return true;
});
diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java b/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java
index 0b99cad..9532555 100644
--- a/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java
+++ b/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java
@@ -13,7 +13,7 @@ import java.util.function.Predicate;
* @param <T>
* The data stored in the tree.
*/
-public class BinarySearchTreeLeaf<T> implements ITreePart<T> {
+public class BinarySearchTreeLeaf<T> implements TreePart<T> {
/** The data held in this tree leaf */
protected T data;
diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java b/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java
index a73f81a..0eef92a 100644
--- a/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java
+++ b/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java
@@ -20,10 +20,10 @@ import java.util.function.Predicate;
*/
public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> {
/* The left child of this node */
- private ITreePart<T> left;
+ private TreePart<T> left;
/* The right child of this node */
- private ITreePart<T> right;
+ private TreePart<T> right;
/**
* Create a new node with the specified data and children.
@@ -37,8 +37,8 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> {
* @param rght
* The right child of this node.
*/
- public BinarySearchTreeNode(final T element, final ITreePart<T> lft,
- final ITreePart<T> rght) {
+ public BinarySearchTreeNode(final T element, final TreePart<T> lft,
+ final TreePart<T> rght) {
super(element);
this.left = lft;
this.right = rght;
diff --git a/src/main/java/bjc/funcdata/bst/ITreePart.java b/src/main/java/bjc/funcdata/bst/TreePart.java
index bac640d..b451463 100644
--- a/src/main/java/bjc/funcdata/bst/ITreePart.java
+++ b/src/main/java/bjc/funcdata/bst/TreePart.java
@@ -13,7 +13,7 @@ import java.util.function.Predicate;
* @param <T>
* The data contained in this part of the tree.
*/
-public interface ITreePart<T> {
+public interface TreePart<T> {
/**
* Add a element below this tree part somewhere.
*
diff --git a/src/main/java/bjc/functypes/ArrayChooser.java b/src/main/java/bjc/functypes/ArrayChooser.java
new file mode 100644
index 0000000..3baa00c
--- /dev/null
+++ b/src/main/java/bjc/functypes/ArrayChooser.java
@@ -0,0 +1,20 @@
+package bjc.functypes;
+
+/**
+ * Function which picks a single element from an array of elements.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ElementType> The type of element stored in the array.
+ */
+@FunctionalInterface
+public interface ArrayChooser<ElementType> {
+ /**
+ * Select a single element from an array of elements.
+ *
+ * @param elements The elements to pick from.
+ *
+ * @return The selected element.
+ */
+ public ElementType choose(@SuppressWarnings("unchecked") ElementType... elements);
+}
diff --git a/src/main/java/bjc/functypes/Combinators.java b/src/main/java/bjc/functypes/Combinators.java
new file mode 100644
index 0000000..fde13df
--- /dev/null
+++ b/src/main/java/bjc/functypes/Combinators.java
@@ -0,0 +1,297 @@
+package bjc.functypes;
+
+import static java.util.stream.Collectors.*;
+
+import java.util.*;
+import java.util.function.*;
+
+import bjc.data.*;
+
+/**
+ * A bunch of only slightly related function combinators.
+ *
+ * @author Ben Culkin
+ *
+ */
+public class Combinators {
+ /**
+ * If-then-else combinator.
+ *
+ * @param <Input> The input type.
+ * @param <Output> The output type.
+ *
+ * @param in The predicate to run.
+ * @param ifTrue The condition to run when it is true.
+ * @param ifFalse The condition to run when it is false.
+ *
+ * @return A function which will invoke one or the other action, based on the predicate.
+ */
+ @SuppressWarnings("unused")
+ public static <Input, Output> Function<Input, Output> iftt(
+ Predicate<Input> in,
+ Function<Input, Output> ifTrue,
+ Function<Input, Output> ifFalse)
+ {
+ return arg -> in.test(arg) ? ifTrue.apply(arg) : ifFalse.apply(arg);
+ }
+
+ /**
+ * If-then-else expression.
+ *
+ * @param <Output> The output type.
+ *
+ * @param in The boolean to use.
+ * @param ifTrue The condition to run when it is true.
+ * @param ifFalse The condition to run when it is false.
+ *
+ * @return A value, based on the provided parameter
+ */
+ public static <Output> Output iftt(
+ boolean in,
+ Supplier<Output> ifTrue,
+ Supplier<Output> ifFalse)
+ {
+ return in ? ifTrue.get() : ifFalse.get();
+ }
+
+ /**
+ * Execute an action before calling a function.
+ *
+ * @param <Input> The input to the function.
+ * @param <Output> The output from the function.
+ *
+ * @param action The action to run on the input.
+ * @param terminal The function to call.
+ *
+ * @return A function that runs the provided action before calling the function.
+ */
+ public static <Input, Output> Function<Input, Output> beforeThis(
+ Consumer<Input> action,
+ Function<Input, Output> terminal)
+ {
+ return (arg) -> {
+ action.accept(arg);
+ return terminal.apply(arg);
+ };
+ }
+
+ /**
+ * Execute an action after calling a function.
+ *
+ * @param <Input> The input to the function.
+ * @param <Output> The output to the function.
+ *
+ * @param initial The function to call.
+ * @param action The action to call after doing the function.
+ *
+ * @return A function that calls the provided action after the function.
+ */
+ public static <Input, Output> Consumer<Input> andThen(
+ Function<Input, Output> initial,
+ Consumer<Output> action)
+ {
+ return (arg) -> action.accept(initial.apply(arg));
+ }
+
+ /**
+ * Standalone function composer.
+ *
+ * @param <Left> The input type of the initial function.
+ * @param <Middle> The shared input/output type.
+ * @param <Right> The output type of the terminal function.
+ *
+ * @param initial The first function to call.
+ * @param terminal The second function to call.
+ *
+ * @return A function that composes the provided functions together.
+ */
+ public static <Left, Middle, Right> Function<Left, Right> compose(
+ Function<Left, Middle> initial,
+ Function<Middle, Right> terminal)
+ {
+ return (arg) -> terminal.apply(initial.apply(arg));
+ }
+
+ /**
+ * Execute a function with some internal state.
+ *
+ * @param <Input> The input type of the function.
+ * @param <Output> The output type of the function.
+ * @param <State> The type of the internal state.
+ *
+ * @param source The function which provides internal state.
+ * @param action The function to call.
+ *
+ * @return A function which hides the production of the internal state.
+ */
+ public static <Input, Output, State> Function<Input, Output> introducing(
+ Supplier<State> source,
+ BiFunction<State, Input, Output> action)
+ {
+ return (input) -> action.apply(source.get(), input);
+ }
+
+ /**
+ * Invoke a given function with null to produce its result.
+ *
+ * @param <Input> The input type of the function.
+ * @param <Output> The output type of the function.
+ *
+ * @param action The function to invoke.
+ *
+ * @return The result of calling the function with null.
+ */
+ public static <Input, Output> Output invoke(Function<Input, Output> action)
+ {
+ return action.apply(null);
+ }
+
+ /**
+ * Execute an action a given number of times.
+ *
+ * @param times The number of times to execute the action.
+ * @param action The action to execute.
+ */
+ public static void times(int times, Consumer<Integer> action)
+ {
+ for (int i = 0; i < times; i++) action.accept(i);
+ }
+
+ /**
+ * Invoke a wrapper instead of invoking a normal function.
+ *
+ * @param <Input> The input type to the function.
+ * @param <Output> The output type to the function.
+ *
+ * @param function The function to apply.
+ * @param wrapper The wrapper around a function.
+ *
+ * @return A function that invokes the wrapper instead.
+ */
+ public static <Input, Output> Function<Input, Output> around(
+ Function<Input, Output> function,
+ BiFunction<Input, Function<Input, Output>, Output> wrapper)
+ {
+ return (input) -> wrapper.apply(input, function);
+ }
+
+ /**
+ * Only run a given function when the argument satisfies a condition.
+ *
+ * @param <Input> The input type of the function.
+ * @param <Output> The output type of the function.
+ *
+ * @param function The function to run.
+ * @param guard The guard to use for checking the input.
+ *
+ * @return A function which takes the given input, and only calls the
+ * function if the guard returns true. Otherwise, it will return
+ * an empty optional.
+ */
+ public static <Input, Output> Function<Input, Optional<Output>> provided(
+ Function<Input, Output> function,
+ Predicate<Input> guard)
+ {
+ return iftt(guard,
+ (arg) -> Optional.ofNullable(function.apply(arg)),
+ (ignored) -> Optional.empty()
+ );
+ }
+
+ /**
+ * Concatenate two functions together, so that they run on the same argument.
+ *
+ * @param <Input> The type of the input.
+ * @param <Output1> The type of the first output.
+ * @param <Output2> The type of the second output.
+ *
+ * @param funcA The first function to call.
+ * @param funcB The second function to call.
+ *
+ * @return A function that returns a pair of the results of calling both
+ * functions.
+ */
+ public static
+ <Input, Output1, Output2> Function<Input, Pair<Output1, Output2>>
+ concat(Function<Input, Output1> funcA, Function<Input, Output2> funcB)
+ {
+ return (arg) -> Pair.pair(funcA.apply(arg), funcB.apply(arg));
+ }
+
+ /**
+ * Concatenate a series of functions together, returning a list of their
+ * results.
+ *
+ * @param <Input> The input type for the functions.
+ * @param <Output> The output type for the functions.
+ *
+ * @param funcs The series of functions to call.
+ *
+ * @return A function that calls each of those functions, and returns a
+ * list of their results.
+ */
+ @SafeVarargs
+ public static
+ <Input, Output> Function<Input, List<Output>>
+ concat(Function<Input, Output>... funcs)
+ {
+ List<Function<Input, Output>> funcList = Arrays.asList(funcs);
+
+ // Kind of a nuisance that Java can't properly guess the type of
+ // our mapper function, but oh well.
+
+ return (arg) ->
+ funcList.stream()
+ .map((Function<Function<Input, Output>, Output>)
+ (func) -> func.apply(arg))
+ .collect(toList());
+ }
+
+ /**
+ * Return a function that does a series of actions upon a value, then returns
+ * that value.
+ *
+ * @param <Type> The type given as an argument
+ *
+ * @param consumers The actions to perform on the value.
+ *
+ * @return A function that performs those arguments on a value.
+ */
+ @SafeVarargs
+ public static <Type> Function<Type, Type> doWith(Consumer<Type>... consumers)
+ {
+ return (arg) -> {
+ for (Consumer<Type> consumer : consumers) consumer.accept(arg);
+ return arg;
+ };
+ }
+
+ /**
+ * Perform a series of actions upon a value, then return that value.
+ *
+ * @param <Type> The type given as an argument
+ *
+ * @param input The value to use.
+ * @param consumers The actions to perform on the value.
+ *
+ * @return A function that performs those arguments on a value.
+ */
+ @SafeVarargs
+ public static <Type> Type with(Type input, Consumer<Type>... consumers)
+ {
+ return doWith(consumers).apply(input);
+ }
+
+ /**
+ * Convert a function into a consumer, ignoring its output.
+ *
+ * @param <Input> The input to the function.
+ *
+ * @param func The function to convert.
+ *
+ * @return A consumer which calls the function, and ignores the output.
+ */
+ public static <Input> Consumer<Input> ignore(Function<Input, ?> func) {
+ return (inp) -> func.apply(inp);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/bjc/functypes/Fixpoints.java b/src/main/java/bjc/functypes/Fixpoints.java
new file mode 100644
index 0000000..6d5a250
--- /dev/null
+++ b/src/main/java/bjc/functypes/Fixpoints.java
@@ -0,0 +1,58 @@
+package bjc.functypes;
+
+import java.util.*;
+import java.util.function.*;
+
+import bjc.data.*;
+
+/**
+ * Function combinators to form 'fixpoints' or recursive functions.
+ *
+ * @author Ben Culkin
+ *
+ */
+public interface Fixpoints {
+ /**
+ * Convert a two-argument function to a single argument recursive one.
+ *
+ * @param <InputType> The input type for the function.
+ * @param <ReturnType> The output type for the function.
+ *
+ * @param func The function to make recursive.
+ *
+ * @return The newly recursive function.
+ */
+ static <InputType, ReturnType> Function<InputType, ReturnType> fix(
+ BiFunction<InputType, Function<InputType, ReturnType>, ReturnType> func) {
+ Holder<Function<InputType, ReturnType>> inner = Holder.of(null);
+ inner.replace((arg) -> {
+ return func.apply(arg, inner.getValue());
+ });
+ return inner.getValue();
+ }
+
+ /**
+ * Convert a two-argument function to a single argument recursive memoized one.
+ *
+ * @param <InputType> The input type for the function.
+ * @param <ReturnType> The output type for the function.
+ *
+ * @param func The function to make recursive.
+ *
+ * @return The newly recursive memoized function.
+ */
+ static <InputType, ReturnType> Function<InputType, ReturnType> memoFix(
+ BiFunction<InputType, Function<InputType, ReturnType>, ReturnType> func) {
+ Map<InputType, ReturnType> memoMap = new HashMap<>();
+
+ Holder<Function<InputType, ReturnType>> inner = Holder.of(null);
+ inner.replace((arg) ->
+ memoMap.computeIfAbsent(
+ arg,
+ (newArg) -> func.apply(newArg, inner.getValue())
+ )
+ );
+
+ return inner.getValue();
+ }
+}
diff --git a/src/main/java/bjc/functypes/FunctionalIsomorphism.java b/src/main/java/bjc/functypes/FunctionalIsomorphism.java
new file mode 100644
index 0000000..8518338
--- /dev/null
+++ b/src/main/java/bjc/functypes/FunctionalIsomorphism.java
@@ -0,0 +1,50 @@
+package bjc.functypes;
+
+import java.util.function.Function;
+
+/**
+ * A pair of functions to transform between a pair of types.
+ *
+ * @author bjculkin
+ *
+ * @param <Source>
+ * The source type of the isomorphism.
+ *
+ * @param <Dest>
+ * The destination type of isomorphism.
+ */
+public class FunctionalIsomorphism<Source, Dest> implements Isomorphism<Source, Dest>
+{
+ /* The function to the destination type. */
+ private Function<Source, Dest> toFunc;
+ /* The function to the source type. */
+ private Function<Dest, Source> fromFunc;
+
+ /**
+ * Create a new isomorphism.
+ *
+ * @param to
+ * The 'forward' function, from the source to the definition.
+ *
+ * @param from
+ * The 'backward' function, from the definition to the source.
+ */
+ public FunctionalIsomorphism(Function<Source, Dest> to,
+ Function<Dest, Source> from)
+ {
+ toFunc = to;
+ fromFunc = from;
+ }
+
+ @Override
+ public Dest to(Source val)
+ {
+ return toFunc.apply(val);
+ }
+
+ @Override
+ public Source from(Dest val)
+ {
+ return fromFunc.apply(val);
+ }
+}
diff --git a/src/main/java/bjc/functypes/ID.java b/src/main/java/bjc/functypes/ID.java
index f830d66..53a4c84 100644
--- a/src/main/java/bjc/functypes/ID.java
+++ b/src/main/java/bjc/functypes/ID.java
@@ -10,6 +10,8 @@ import java.util.function.UnaryOperator;
public class ID {
/**
* Create an identity function.
+ *
+ * @param <A> The type of the function.
*
* @return A identity function.
*/
diff --git a/src/main/java/bjc/functypes/IntArrayChooser.java b/src/main/java/bjc/functypes/IntArrayChooser.java
new file mode 100644
index 0000000..416487e
--- /dev/null
+++ b/src/main/java/bjc/functypes/IntArrayChooser.java
@@ -0,0 +1,19 @@
+package bjc.functypes;
+
+/**
+ * Int-specialized variant of ArrayChooser.
+ *
+ * @author Ben Culkin
+ *
+ */
+@FunctionalInterface
+public interface IntArrayChooser {
+ /**
+ * Choose a single int from an array of ints.
+ *
+ * @param ints The array of ints to choose.
+ *
+ * @return The chosen int.
+ */
+ public int choose(int... ints);
+}
diff --git a/src/main/java/bjc/functypes/Isomorphism.java b/src/main/java/bjc/functypes/Isomorphism.java
new file mode 100644
index 0000000..db633e6
--- /dev/null
+++ b/src/main/java/bjc/functypes/Isomorphism.java
@@ -0,0 +1,54 @@
+package bjc.functypes;
+
+import java.util.function.*;
+
+/**
+ * Denotes that two types are essentially equivalent, by providing a means to
+ * convert between them.
+ *
+ * @author Ben Culkin
+ *
+ * @param <Source> The first type.
+ * @param <Dest> The second type.
+ */
+public interface Isomorphism<Source, Dest>
+{
+
+ /**
+ * Apply the isomorphism forward.
+ *
+ * @param val
+ * The source value.
+ *
+ * @return The destination value.
+ */
+ Dest to(Source val);
+
+ /**
+ * Apply the isomorphism backward.
+ *
+ * @param val
+ * The destination value.
+ *
+ * @return The source value.
+ */
+ Source from(Dest val);
+
+ /**
+ * Create an isomorphism from a pair of functions.
+ *
+ * @param <Src> The source type.
+ * @param <Dst> The destination type.
+ *
+ * @param forward The function from source to destination.
+ * @param backward The function from destination to source.
+ *
+ * @return An isomorphism between the two types.
+ */
+ static <Src, Dst> Isomorphism<Src, Dst> from(
+ Function<Src, Dst> forward,
+ Function<Dst, Src> backward)
+ {
+ return new FunctionalIsomorphism<>(forward, backward);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/bjc/functypes/ListFlattener.java b/src/main/java/bjc/functypes/ListFlattener.java
index 3768b84..91abe9d 100644
--- a/src/main/java/bjc/functypes/ListFlattener.java
+++ b/src/main/java/bjc/functypes/ListFlattener.java
@@ -2,7 +2,7 @@ package bjc.functypes;
import java.util.function.Function;
-import bjc.funcdata.IList;
+import bjc.funcdata.ListEx;
/**
* A function that flattens a list.
@@ -12,7 +12,7 @@ import bjc.funcdata.IList;
* @param <S>
* The type of value in the list.
*/
-public interface ListFlattener<S> extends Function<IList<S>, S> {
+public interface ListFlattener<S> extends Function<ListEx<S>, S> {
/*
* Alias
*/
diff --git a/src/main/java/bjc/functypes/ThrowFunction.java b/src/main/java/bjc/functypes/ThrowFunction.java
new file mode 100644
index 0000000..3714cad
--- /dev/null
+++ b/src/main/java/bjc/functypes/ThrowFunction.java
@@ -0,0 +1,184 @@
+package bjc.functypes;
+
+import java.util.*;
+import java.util.function.*;
+
+/**
+ * An instance of {@link Function} that can throw an exception.
+ *
+ * @author Ben Culkin
+ *
+ * @param <InputType> The input to the function.
+ * @param <ReturnType> The output to the function.
+ * @param <ExType> The type of exception thrown.
+ */
+public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable>
+{
+ /**
+ * Does the possibly throwing computation embodied by this function.
+ *
+ * @param arg The input to the function.
+ *
+ * @return The result of the function.
+ *
+ * @throws ExType If something went wrong with the function.
+ */
+ public ReturnType apply(InputType arg) throws ExType;
+
+ /**
+ * Converts this into a {@link Function} by handling any thrown exceptions,
+ * then mapping the return type to get a consistent return type.
+ *
+ * @param <NewReturn> The new return type.
+ *
+ * @param clasz The class of the handled exception. This needs to be provided
+ * because you can't catch a generic exception, and we want to
+ * make sure that we aren't catching any types of exception that
+ * we aren't supposed to.
+ * @param mapper The function which maps the normal return.
+ * @param handler The handler to use.
+ *
+ * @return A function which will either return its proper value, or the result
+ * of invoking the handler.
+ */
+ @SuppressWarnings("unchecked")
+ default <NewReturn> Function<InputType, NewReturn> swallowMap(
+ Class<ExType> clasz,
+ Function<ReturnType, NewReturn> mapper,
+ Function<ExType, NewReturn> handler)
+ {
+ return (inp) -> {
+ try {
+ return mapper.apply(this.apply(inp));
+ } catch (Throwable ex) {
+ if (clasz.isInstance(ex)) {
+ // Swallow this
+ return handler.apply((ExType) ex);
+ } else {
+ String msg = "Exception of incorrect type to be handled, only "
+ + clasz.getName()
+ + " are handled";
+
+ throw new RuntimeException(msg, ex);
+ }
+ }
+ };
+ }
+
+ /**
+ * Converts this into a {@link Function} by handling any thrown exceptions.
+ *
+ * @param clasz The class of the handled exception. This needs to be provided
+ * because you can't catch a generic exception, and we want to
+ * make sure that we aren't catching any types of exception that
+ * we aren't supposed to.
+ *
+ * @param handler The handler to use.
+ *
+ * @return A function which will either return its proper value, or the result
+ * of invoking the handler.
+ */
+ default Function<InputType, ReturnType> swallow(
+ Class<ExType> clasz, Function<ExType, ReturnType> handler)
+ {
+ return swallowMap(clasz, (arg) -> arg, handler);
+ }
+
+ /**
+ * Convert this to a function which will attempt to recover from the thrown exception.
+ *
+ * @param clasz The class of the exception. Needed for type-safety reasons.
+ * @param rescue The function to use to convert an exception into a safe input value.
+ *
+ * @return A function which attempts to recover from a exception.
+ */
+ @SuppressWarnings("unchecked")
+ default Function<InputType, ReturnType> recover(
+ Class<ExType> clasz, BiFunction<InputType, ExType, InputType> rescue)
+ {
+ return Fixpoints.fix((arg, self) -> {
+ try {
+ return this.apply(arg);
+ } catch (Throwable ex) {
+ if (clasz.isInstance(ex)) {
+ // Swallow this
+ return self.apply(rescue.apply(arg, (ExType) ex));
+ } else {
+ String msg = "Exception of incorrect type to be handled, only "
+ + clasz.getName()
+ + " are handled";
+
+ throw new RuntimeException(msg, ex);
+ }
+ }
+ });
+ }
+
+ /**
+ * Create a {@link Thrower} which will yield the result of calling this
+ * function with the given argument.
+ *
+ * @param arg The argument to use.
+ *
+ * @return A thrower which will call this function with the given value.
+ */
+ default Thrower<ReturnType, ExType> suspend(InputType arg)
+ {
+ return () -> this.apply(arg);
+ }
+
+ /**
+ * Compose two throwing functions together.
+ *
+ * @param <NewOutput> The newly output type.
+ *
+ * @param func The throwing function to compose this with.
+ *
+ * @return A throwing function that composes the two.
+ */
+ default <NewOutput> ThrowFunction<InputType, NewOutput, ExType>
+ compose(
+ ThrowFunction<ReturnType, NewOutput, ExType> func) {
+ return (arg) -> func.apply(this.apply(arg));
+ }
+
+ /** Convert this function into one which will return an empty optional if an
+ * exception is thrown, returning an optional containing the value otherwise.
+ *
+ * Note that if this function returns a null value by itself, that will also
+ * yield an empty nullable.
+ *
+ * @param clasz The class of the exception. Needed because of type erasure,
+ * to ensure that we are catching the proper class.
+ *
+ * @return A function which returns an optional instead.
+ */
+ default Function<InputType, Optional<ReturnType>>
+ makeTotal(Class<ExType> clasz)
+ {
+ return swallowMap(clasz, Optional::ofNullable, (ignored) -> Optional.empty());
+ }
+ /**
+ * ThrowFunctions and functions which return a {@link Thrower} are isomorphic.
+ *
+ * @param <InpType> The function input type.
+ * @param <OutType> The function output type.
+ * @param <ExType> The exception type.
+ *
+ * @return The isomorphism between them.
+ */
+ static <InpType, OutType, ExType extends Throwable>
+ Isomorphism<ThrowFunction<InpType, OutType, ExType>, Function<InpType, Thrower<OutType, ExType>>>
+ getIso()
+ {
+ // @FIXME Nov 23, 2020 Ben Culkin :EquivISO
+ // Fix this to strip wrappers when appropriate, so that going
+ // backwards and forwards leaves you where you started, not under
+ // two levels of indirection.
+ return Isomorphism.from((throwFun) -> {
+ return (arg) -> throwFun.suspend(arg);
+ }, (thrower) -> {
+ return (arg) -> thrower.apply(arg).extract();
+ });
+ }
+}
diff --git a/src/main/java/bjc/functypes/Thrower.java b/src/main/java/bjc/functypes/Thrower.java
new file mode 100644
index 0000000..eb03eaf
--- /dev/null
+++ b/src/main/java/bjc/functypes/Thrower.java
@@ -0,0 +1,165 @@
+package bjc.functypes;
+
+import java.util.*;
+import java.util.function.*;
+
+/**
+ * A source for a value that could produce an exception.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ValueType> The type of value possibly produced.
+ * @param <ExceptionType> The type of exception possibly thrown.
+ */
+@FunctionalInterface
+public interface Thrower<ValueType, ExceptionType extends Throwable>
+{
+ /**
+ * Attempt to get the value.
+ *
+ * @return The value from this source.
+ *
+ * @throws ExceptionType If something goes wrong getting the value.
+ */
+ ValueType extract() throws ExceptionType;
+
+ /**
+ * Converts this thrower into a memoized one.
+ *
+ * Note that if this does throw an exception, it won't be 'memoized' so
+ * that the next call will call this Thrower again.
+ *
+ * @return A memoizing thrower.
+ */
+ default Thrower<ValueType, ExceptionType> memoize() {
+ return new MemoizedThrower<>(this);
+ }
+
+ /**
+ * Applies a function to the result of this thrower.
+ *
+ * @param <NewOutputType> The new type output by the function.
+ *
+ * @param func The function to apply.
+ *
+ * @return A thrower that is the result of applying the given function.
+ */
+ default <NewOutputType> Thrower<NewOutputType, ExceptionType> bind(
+ Function<ValueType, Thrower<NewOutputType, ExceptionType>> func)
+ {
+ return () -> func.apply(extract()).extract();
+ }
+ /**
+ * Create a thrower that yields a given value.
+ *
+ * @param <ValType> The type of the value.
+ *
+ * @param val The value to yield.
+ *
+ * @return A thrower that will always yield that value.
+ */
+ static <ValType>
+ Thrower<ValType, ? extends Throwable> from(ValType val)
+ {
+ return () -> val;
+ }
+
+ /**
+ * Create a thrower that yields a given value.
+ *
+ * @param <ValType> The type of the value.
+ *
+ * @param val The value to yield.
+ *
+ * @return A thrower that will always yield that value.
+ */
+ static <ValType>
+ Thrower<ValType, ? extends Throwable> from(Supplier<ValType> val)
+ {
+ return val::get;
+ }
+
+ /**
+ * Convert a function on values to one over throwers.
+ *
+ * @param <Input> The function input type.
+ * @param <Output> The function output type.
+ * @param <ExType> The exception possibly thrown.
+ *
+ * @param func The function to convert.
+ *
+ * @return A function which operates on throwers instead.
+ */
+ static <Input, Output, ExType extends Throwable>
+ Function<Thrower<Input, ExType>,Thrower<Output, ExType>> fmap(
+ Function<Input, Output> func)
+ {
+ return (input) -> () -> func.apply(input.extract());
+ }
+
+ /**
+ * Convert a list of throwers into a thrower that returns a list.
+ *
+ * @param <Output> The type output by the thrower.
+ * @param <ExType> The type of exception thrown.
+ *
+ * @param throwers The list of throwers.
+ *
+ * @return A thrower that returns a list of results.
+ */
+ static <Output, ExType extends Throwable>
+ Thrower<List<Output>, ExType> seq(List<Thrower<Output, ExType>> throwers)
+ {
+ return () -> {
+ List<Output> results = new ArrayList<>(throwers.size());
+ for (Thrower<Output, ExType> thrower : throwers)
+ {
+ results.add(thrower.extract());
+ }
+ return results;
+ };
+ }
+
+ /**
+ * Convert a array of throwers into a thrower that returns a list.
+ *
+ * @param <Output> The type output by the thrower.
+ * @param <ExType> The type of exception thrown.
+ *
+ * @param throwers The array of throwers.
+ *
+ * @return A thrower that returns a list of results.
+ */
+ @SafeVarargs
+ static <Output, ExType extends Throwable>
+ Thrower<List<Output>, ExType> seq(Thrower<Output, ExType>... throwers)
+ {
+ return () -> {
+ List<Output> results = new ArrayList<>(throwers.length);
+ for (Thrower<Output, ExType> thrower : throwers)
+ {
+ results.add(thrower.extract());
+ }
+ return results;
+ };
+ }
+}
+
+class MemoizedThrower<ValueType, ExceptionType extends Throwable>
+implements Thrower<ValueType, ExceptionType>
+{
+ private final Thrower<ValueType, ExceptionType> source;
+ private ValueType val;
+
+ public MemoizedThrower(Thrower<ValueType, ExceptionType> source)
+ {
+ this.source = source;
+ }
+
+ @Override
+ public ValueType extract() throws ExceptionType
+ {
+ if (val == null) val = source.extract();
+ return val;
+ }
+}