From db8270b5722acfd8a8fdd983b888b92bfe903320 Mon Sep 17 00:00:00 2001 From: Tomas Mikula Date: Mon, 11 Apr 2016 01:05:29 -0400 Subject: [PATCH] Only use semigroup instead of monoid for FingerTree summary. --- .../org/reactfx/collection/ListReduction.java | 13 +- .../java/org/reactfx/util/FingerTree.java | 116 ++++++++++-------- .../src/main/java/org/reactfx/util/Lists.java | 11 +- .../java/org/reactfx/util/MapToMonoid.java | 5 - .../util/{Monoid.java => Semigroup.java} | 3 +- .../java/org/reactfx/util/SparseList.java | 19 ++- .../java/org/reactfx/util/ToSemigroup.java | 5 + 7 files changed, 84 insertions(+), 88 deletions(-) delete mode 100644 reactfx/src/main/java/org/reactfx/util/MapToMonoid.java rename reactfx/src/main/java/org/reactfx/util/{Monoid.java => Semigroup.java} (56%) create mode 100644 reactfx/src/main/java/org/reactfx/util/ToSemigroup.java diff --git a/reactfx/src/main/java/org/reactfx/collection/ListReduction.java b/reactfx/src/main/java/org/reactfx/collection/ListReduction.java index 5091518..263438c 100644 --- a/reactfx/src/main/java/org/reactfx/collection/ListReduction.java +++ b/reactfx/src/main/java/org/reactfx/collection/ListReduction.java @@ -9,14 +9,14 @@ import org.reactfx.Subscription; import org.reactfx.util.Experimental; import org.reactfx.util.FingerTree; -import org.reactfx.util.MapToMonoid; +import org.reactfx.util.ToSemigroup; import org.reactfx.value.Val; import org.reactfx.value.ValBase; class ListReduction extends ValBase { private final ObservableList input; private final BinaryOperator reduction; - private final MapToMonoid monoid; + private final ToSemigroup monoid; private FingerTree tree = null; @@ -25,18 +25,13 @@ class ListReduction extends ValBase { BinaryOperator reduction) { this.input = input; this.reduction = reduction; - monoid = new MapToMonoid() { + monoid = new ToSemigroup() { @Override public T apply(T t) { return t; } - @Override - public T unit() { - return null; - } - @Override public T reduce(T left, T right) { return reduction.apply(left, right); @@ -73,7 +68,7 @@ protected final T computeValue() { if(isObservingInputs()) { assert tree != null; int max = tree.getLeafCount(); - return tree.getSummaryBetween(getFrom(max), getTo(max)); + return tree.getSummaryBetween(getFrom(max), getTo(max)).orElse(null); } else { assert tree == null; int max = input.size(); diff --git a/reactfx/src/main/java/org/reactfx/util/FingerTree.java b/reactfx/src/main/java/org/reactfx/util/FingerTree.java index 425a57c..1fc8cb6 100644 --- a/reactfx/src/main/java/org/reactfx/util/FingerTree.java +++ b/reactfx/src/main/java/org/reactfx/util/FingerTree.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; +import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.ToIntFunction; @@ -17,8 +18,8 @@ public abstract class FingerTree { public static abstract class NonEmptyFingerTree extends FingerTree { - private NonEmptyFingerTree(MapToMonoid monoid) { - super(monoid); + private NonEmptyFingerTree(ToSemigroup semigroup) { + super(semigroup); } @Override @@ -26,6 +27,8 @@ public Either, NonEmptyFingerTree> caseEmpty() { return right(this); } + public abstract S getSummary(); + public Tuple3, Tuple2, FingerTree> split( ToIntFunction metric, int position) { Lists.checkPosition(position, measure(metric)); @@ -58,8 +61,8 @@ abstract Tuple3, Tuple2, FingerTree> split0( private static final class Empty extends FingerTree { - Empty(MapToMonoid monoid) { - super(monoid); + Empty(ToSemigroup semigroup) { + super(semigroup); } @Override @@ -101,8 +104,8 @@ T getData() { @Override public - S getSummary() { - return monoid.unit(); + Optional getSummaryOpt() { + return Optional.empty(); } @Override @@ -145,8 +148,7 @@ R foldBetween0( @Override S getSummaryBetween0(int startLeaf, int endLeaf) { - assert Lists.isValidRange(startLeaf, endLeaf, 0); - return monoid.unit(); + throw new AssertionError("Unreachable code"); } @Override @@ -155,8 +157,7 @@ S getSummaryBetween0( int startPosition, int endPosition, TriFunction subSummary) { - assert Lists.isValidRange(startPosition, endPosition, 0); - return monoid.unit(); + throw new AssertionError("Unreachable code"); } @Override @@ -185,10 +186,10 @@ private static class Leaf extends NonEmptyFingerTree { private final T data; private final S summary; - Leaf(MapToMonoid monoid, T data) { - super(monoid); + Leaf(ToSemigroup semigroup, T data) { + super(semigroup); this.data = data; - this.summary = monoid.apply(data); + this.summary = semigroup.apply(data); } @Override @@ -228,6 +229,11 @@ public S getSummary() { return summary; } + @Override + public Optional getSummaryOpt() { + return Optional.of(summary); + } + @Override BiIndex locateProgressively0(ToIntFunction metric, int position) { assert Lists.isValidPosition(position, measure(metric)); @@ -278,9 +284,8 @@ R foldBetween0( @Override S getSummaryBetween0(int startLeaf, int endLeaf) { - assert Lists.isNonEmptyRange(startLeaf, endLeaf, getLeafCount()) - : "Didn't expect empty range [" + startLeaf + ", " + endLeaf + ")"; - return getSummary(); + assert startLeaf == 0 && endLeaf == 1; + return summary; } @Override @@ -343,9 +348,9 @@ private static final class Branch extends NonEmptyFingerTree { private final S summary; private Branch( - MapToMonoid monoid, + ToSemigroup semigroup, Cons> children) { - super(monoid); + super(semigroup); assert children.size() == 2 || children.size() == 3; FingerTree head = children.head(); int headDepth = head.getDepth(); @@ -354,8 +359,8 @@ private Branch( this.depth = 1 + headDepth; this.leafCount = children.fold(0, (s, n) -> s + n.getLeafCount()); this.summary = children.mapReduce1( - FingerTree::getSummary, - monoid::reduce); + NonEmptyFingerTree::getSummary, + semigroup::reduce); } @Override @@ -529,12 +534,15 @@ public S getSummary() { } @Override - final S getSummaryBetween0( - int startLeaf, - int endLeaf) { + public Optional getSummaryOpt() { + return Optional.of(summary); + } + + @Override + final S getSummaryBetween0(int startLeaf, int endLeaf) { assert Lists.isNonEmptyRange(startLeaf, endLeaf, getLeafCount()); if(startLeaf == 0 && endLeaf == getLeafCount()) { - return getSummary(); + return summary; } else { return getSummaryBetween0(startLeaf, endLeaf, children); } @@ -550,7 +558,7 @@ private S getSummaryBetween0( int tailFrom = Math.max(startLeaf - headSize, 0); int tailTo = endLeaf - headSize; if(startLeaf < headTo && tailFrom < tailTo) { - return monoid.reduce( + return semigroup.reduce( head.getSummaryBetween0(startLeaf, headTo), getSummaryBetween0(tailFrom, tailTo, nodes.tail())); } else if(startLeaf < headTo) { @@ -590,7 +598,7 @@ private S getSummaryBetween0( int tailFrom = Math.max(startPosition - headLen, 0); int tailTo = endPosition - headLen; if(startPosition < headTo && tailFrom < tailTo) { - return monoid.reduce( + return semigroup.reduce( head.getSummaryBetween0( metric, startPosition, headTo, subSummary), getSummaryBetween0(metric, tailFrom, tailTo, subSummary, nodes.tail())); } else if(startPosition < headTo) { @@ -691,13 +699,13 @@ private Tuple3, Tuple2, FingerTree> split0( } public static FingerTree empty( - MapToMonoid statisticsProvider) { + ToSemigroup statisticsProvider) { return new Empty<>(statisticsProvider); } public static FingerTree mkTree( List initialItems, - MapToMonoid statisticsProvider) { + ToSemigroup statisticsProvider) { if(initialItems.isEmpty()) { return new Empty<>(statisticsProvider); } @@ -738,21 +746,25 @@ private static FingerTree concat( (v, w) -> v.appendTree(w)); } - final MapToMonoid monoid; + final ToSemigroup semigroup; - private FingerTree(MapToMonoid monoid) { - this.monoid = monoid; + private FingerTree(ToSemigroup semigroup) { + this.semigroup = semigroup; } public abstract int getDepth(); public abstract int getLeafCount(); - public abstract S getSummary(); + public abstract Optional getSummaryOpt(); public abstract Either, NonEmptyFingerTree> caseEmpty(); public final boolean isEmpty() { return getDepth() == 0; } + public S getSummary(S whenEmpty) { + return getSummaryOpt().orElse(whenEmpty); + } + public T getLeaf(int index) { Lists.checkIndex(index, getLeafCount()); return getLeaf0(index); @@ -763,10 +775,14 @@ public T getLeaf(int index) { public Tuple2 get( ToIntFunction metric, int index) { - int size = metric.applyAsInt(getSummary()); - Lists.checkIndex(index, size); - BiIndex location = locateProgressively(metric, index); - return t(getLeaf(location.major), location); + return caseEmpty().unify( + emptyTree -> { throw new IndexOutOfBoundsException("empty tree"); }, + neTree -> { + int size = metric.applyAsInt(neTree.getSummary()); + Lists.checkIndex(index, size); + BiIndex location = locateProgressively(metric, index); + return t(getLeaf(location.major), location); + }); } public E get( @@ -862,30 +878,26 @@ abstract R foldBetween0( int endPosition, TetraFunction rangeReduction); - public S getSummaryBetween(int startLeaf, int endLeaf) { + public Optional getSummaryBetween(int startLeaf, int endLeaf) { Lists.checkRange(startLeaf, endLeaf, getLeafCount()); - if(startLeaf == endLeaf) { - return monoid.unit(); - } else { - return getSummaryBetween0(startLeaf, endLeaf); - } + return startLeaf == endLeaf + ? Optional.empty() + : Optional.of(getSummaryBetween0(startLeaf, endLeaf)); } abstract S getSummaryBetween0( int startLeaf, int endLeaf); - public S getSummaryBetween( + public Optional getSummaryBetween( ToIntFunction metric, int startPosition, int endPosition, TriFunction subSummary) { Lists.checkRange(startPosition, endPosition, measure(metric)); - if(startPosition == endPosition) { - return monoid.unit(); - } else { - return getSummaryBetween0(metric, startPosition, endPosition, subSummary); - } + return startPosition == endPosition + ? Optional.empty() + : Optional.of(getSummaryBetween0(metric, startPosition, endPosition, subSummary)); } abstract S getSummaryBetween0( @@ -959,11 +971,11 @@ public FingerTree prepend(T data) { abstract T getData(); // valid for leafs only Empty empty() { - return new Empty<>(monoid); + return new Empty<>(semigroup); } Leaf leaf(T data) { - return new Leaf<>(monoid, data); + return new Leaf<>(semigroup, data); } Branch branch(NonEmptyFingerTree left, NonEmptyFingerTree right) { @@ -978,10 +990,10 @@ Branch branch( } Branch branch(Cons> children) { - return new Branch<>(monoid, children); + return new Branch<>(semigroup, children); } final int measure(ToIntFunction metric) { - return metric.applyAsInt(getSummary()); + return getSummaryOpt().map(metric::applyAsInt).orElse(0); } } \ No newline at end of file diff --git a/reactfx/src/main/java/org/reactfx/util/Lists.java b/reactfx/src/main/java/org/reactfx/util/Lists.java index aed69e2..4a85eca 100644 --- a/reactfx/src/main/java/org/reactfx/util/Lists.java +++ b/reactfx/src/main/java/org/reactfx/util/Lists.java @@ -275,19 +275,14 @@ public int size() { class ListConcatenation extends AbstractList { - private static final MapToMonoid, Integer> LIST_SIZE_MONOID = - new MapToMonoid, Integer>() { + private static final ToSemigroup, Integer> LIST_SIZE_MONOID = + new ToSemigroup, Integer>() { @Override public Integer apply(List t) { return t.size(); } - @Override - public Integer unit() { - return 0; - } - @Override public Integer reduce(Integer left, Integer right) { return left + right; @@ -323,7 +318,7 @@ public E get(int index) { @Override public int size() { - return ft.getSummary(); + return ft.getSummary(0); } @Override diff --git a/reactfx/src/main/java/org/reactfx/util/MapToMonoid.java b/reactfx/src/main/java/org/reactfx/util/MapToMonoid.java deleted file mode 100644 index 73109fa..0000000 --- a/reactfx/src/main/java/org/reactfx/util/MapToMonoid.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.reactfx.util; - -import java.util.function.Function; - -public interface MapToMonoid extends Function, Monoid {} \ No newline at end of file diff --git a/reactfx/src/main/java/org/reactfx/util/Monoid.java b/reactfx/src/main/java/org/reactfx/util/Semigroup.java similarity index 56% rename from reactfx/src/main/java/org/reactfx/util/Monoid.java rename to reactfx/src/main/java/org/reactfx/util/Semigroup.java index 9e02f04..d53e5b1 100644 --- a/reactfx/src/main/java/org/reactfx/util/Monoid.java +++ b/reactfx/src/main/java/org/reactfx/util/Semigroup.java @@ -1,6 +1,5 @@ package org.reactfx.util; -public interface Monoid { - T unit(); +public interface Semigroup { T reduce(T left, T right); } \ No newline at end of file diff --git a/reactfx/src/main/java/org/reactfx/util/SparseList.java b/reactfx/src/main/java/org/reactfx/util/SparseList.java index 2264b2d..52fc3c8 100644 --- a/reactfx/src/main/java/org/reactfx/util/SparseList.java +++ b/reactfx/src/main/java/org/reactfx/util/SparseList.java @@ -209,13 +209,8 @@ private static final class Stats { int getPresentCount() { return presentCount; } } - private static final MapToMonoid, Stats> SEGMENT_STATS = - new MapToMonoid, Stats>() { - - @Override - public Stats unit() { - return Stats.ZERO; - } + private static final ToSemigroup, Stats> SEGMENT_STATS = + new ToSemigroup, Stats>() { @Override public Stats reduce(Stats left, Stats right) { @@ -241,11 +236,11 @@ public SparseList() { } public int size() { - return tree.getSummary().size; + return tree.getSummary(Stats.ZERO).size; } public int getPresentCount() { - return tree.getSummary().presentCount; + return tree.getSummary(Stats.ZERO).presentCount; } public boolean isPresent(int index) { @@ -272,7 +267,7 @@ public int getPresentCountBefore(int position) { return tree.getSummaryBetween( Stats::getSize, 0, position, - Segment::getStatsBetween).getPresentCount(); + Segment::getStatsBetween).orElse(Stats.ZERO).getPresentCount(); } public int getPresentCountAfter(int position) { @@ -303,7 +298,7 @@ public IndexRange getPresentItemsRange() { } private int locationToPosition(int major, int minor) { - return tree.getSummaryBetween(0, major).size + minor; + return tree.getSummaryBetween(0, major).orElse(Stats.ZERO).size + minor; } public List collect() { @@ -418,7 +413,7 @@ public void spliceByVoid(int from, int to, int length) { } private void spliceSegments(int from, int to, List> middle) { - Lists.checkRange(from, to, tree.getSummary().getSize()); + Lists.checkRange(from, to, tree.getSummary(Stats.ZERO).getSize()); tree = tree.caseEmpty() .mapLeft(emptyTree -> join(emptyTree, middle, emptyTree)) .toLeft(nonEmptyTree -> nonEmptyTree.split(Stats::getSize, from).map((left, lSuffix, r) -> { diff --git a/reactfx/src/main/java/org/reactfx/util/ToSemigroup.java b/reactfx/src/main/java/org/reactfx/util/ToSemigroup.java new file mode 100644 index 0000000..45c1bfc --- /dev/null +++ b/reactfx/src/main/java/org/reactfx/util/ToSemigroup.java @@ -0,0 +1,5 @@ +package org.reactfx.util; + +import java.util.function.Function; + +public interface ToSemigroup extends Function, Semigroup {} \ No newline at end of file