Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions core/src/main/java/fj/Monoid.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ public A zero() {
return zero;
}

/**
* Returns a value summed <code>n</code> times (<code>a + a + ... + a</code>)
* @param n multiplier
* @param a the value to multiply
* @return <code>a</code> summed <code>n</code> times. If <code>n <= 0</code>, returns <code>zero()</code>
*/
public A multiply(final int n, final A a) {
A m = zero();
for (int i = 0; i < n; i++) { m = sum(m, a); }
return m;
}

/**
* Sums the given values with right-fold.
*
Expand Down
27 changes: 20 additions & 7 deletions core/src/main/java/fj/Ord.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ public A min(final A a1, final A a2) {
*/
public final F<A, F<A, A>> min = curry((a, a1) -> min(a, a1));

public final Ord<A> reverse() { return ord(Function.flip(f)); }

/**
* Returns an order instance that uses the given equality test and ordering function.
*
Expand Down Expand Up @@ -343,14 +345,25 @@ public static <A, B> Ord<Validation<A, B>> validationOrd(final Ord<A> oa, final
*/
public static <A> Ord<List<A>> listOrd(final Ord<A> oa) {
return ord(l1 -> l2 -> {
if (l1.isEmpty())
return l2.isEmpty() ? Ordering.EQ : Ordering.LT;
else if (l2.isEmpty())
return l1.isEmpty() ? Ordering.EQ : Ordering.GT;
else {
final Ordering c = oa.compare(l1.head(), l2.head());
return c == Ordering.EQ ? listOrd(oa).f.f(l1.tail()).f(l2.tail()) : c;
List<A> x1 = l1;
List<A> x2 = l2;

while (x1.isNotEmpty() && x2.isNotEmpty()) {
final Ordering o = oa.compare(x1.head(), x2.head());
if (o == Ordering.LT || o == Ordering.GT) {
return o;
}
x1 = x1.tail();
x2 = x2.tail();
}

if (x1.isEmpty() && x2.isEmpty()) {
return Ordering.EQ;
} else if (x1.isEmpty()) {
return Ordering.LT;
} else {
return Ordering.GT;
}
});
}

Expand Down
9 changes: 9 additions & 0 deletions core/src/main/java/fj/Ordering.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ public enum Ordering {
GT;

public int toInt() { return ordinal() - 1 ; }

public Ordering reverse() {
switch (this) {
case LT: return GT;
case GT: return LT;
}
return EQ;
}

public static Ordering fromInt(int cmp) {
return cmp == 0 ? EQ : cmp > 0 ? GT : LT;
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/fj/P.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public static <A> P1<A> p(final A a) {
return a;
}
@Override public P1<A> memo() { return this; }
@Override public P1<A> weakMemo() { return this; }
@Override public P1<A> softMemo() { return this; }
};
}

Expand Down
116 changes: 79 additions & 37 deletions core/src/main/java/fj/P1.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package fj;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

import fj.data.Array;
import fj.data.List;
Expand Down Expand Up @@ -208,53 +210,93 @@ public <X> P1<X> map(final F<A, X> f) {
}

/**
* Provides a memoising P1 that remembers its value.
*
* @return A P1 that calls this P1 once and remembers the value for subsequent calls.
*/
public P1<A> memo() {
final P1<A> self = this;
return new P1<A>() {
private final Object latch = new Object();
private volatile SoftReference<Option<A>> v = null;

@Override
public A _1() {
Option<A> o = v != null ? v.get() : null;
if (o == null) {
synchronized (latch) {
o = v != null ? v.get() : null;
if (o == null) {
o = Option.some(self._1());
v = new SoftReference<>(o);
}
}
}
return o.some();
}
* Returns a P1 that remembers its value.
*
* @return A P1 that calls this P1 once and remembers the value for subsequent calls.
*/
public P1<A> memo() { return new Memo<>(this); }

@Override
public P1<A> memo() {
return this;
}
/**
* Like <code>memo</code>, but the memoized value is wrapped into a <code>WeakReference</code>
*/
public P1<A> weakMemo() { return new WeakReferenceMemo<>(this); }

};
}
/**
* Like <code>memo</code>, but the memoized value is wrapped into a <code>SoftReference</code>
*/
public P1<A> softMemo() { return new SoftReferenceMemo<>(this); }

static <A> P1<A> memo(F<Unit, A> f) {
return P.lazy(f).memo();
}

/**
* Returns a constant function that always uses this value.
*
* @return A constant function that always uses this value.
*/
public <B> F<B, A> constant() {
static class Memo<A> extends P1<A> {
private final P1<A> self;
private volatile boolean initialized;
private A value;

Memo(P1<A> self) { this.self = self; }

@Override public A _1() {
if (!initialized) {
synchronized (this) {
if (!initialized) {
A a = self._1();
value = a;
initialized = true;
return a;
}
}
}
return value;
}

@Override public P1<A> memo() { return this; }
}

abstract static class ReferenceMemo<A> extends P1<A> {
private final P1<A> self;
private final Object latch = new Object();
private volatile Reference<Option<A>> v = null;

ReferenceMemo(final P1<A> self) { this.self = self; }

return b -> P1.this._1();
@Override public A _1() {
Option<A> o = v != null ? v.get() : null;
if (o == null) {
synchronized (latch) {
o = v != null ? v.get() : null;
if (o == null) {
o = Option.some(self._1());
v = newReference(o);
}
}
}
return o.some();
}

abstract Reference<Option<A>> newReference(Option<A> o);
}

static class WeakReferenceMemo<A> extends ReferenceMemo<A> {
WeakReferenceMemo(P1<A> self) { super(self); }
@Override Reference<Option<A>> newReference(final Option<A> o) { return new WeakReference<>(o); }
@Override public P1<A> weakMemo() { return this; }
}

static class SoftReferenceMemo<A> extends ReferenceMemo<A> {
SoftReferenceMemo(P1<A> self) { super(self); }
@Override Reference<Option<A>> newReference(final Option<A> o) { return new SoftReference<>(o); }
@Override public P1<A> softMemo() { return this; }
}

/**
* Returns a constant function that always uses this value.
*
* @return A constant function that always uses this value.
*/
public <B> F<B, A> constant() { return Function.constant(_1()); }

@Override
public String toString() {
return Show.p1Show(Show.<A>anyShow()).showS(this);
Expand Down
51 changes: 37 additions & 14 deletions core/src/main/java/fj/data/List.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
import fj.P2;
import fj.Show;
import fj.Unit;
import static fj.Function.curry;
import static fj.Function.constant;
import static fj.Function.identity;
import static fj.Function.compose;

import static fj.Function.*;
import static fj.P.p;
import static fj.P.p2;
import static fj.Unit.unit;
Expand Down Expand Up @@ -169,9 +167,10 @@ public final Stream<A> toStream() {
*/
@SuppressWarnings({"unchecked"})
public final Array<A> toArray() {
final Object[] a = new Object[length()];
final int length = length();
final Object[] a = new Object[length];
List<A> x = this;
for (int i = 0; i < length(); i++) {
for (int i = 0; i < length; i++) {
a[i] = x.head();
x = x.tail();
}
Expand Down Expand Up @@ -625,18 +624,18 @@ public final <B> List<B> apply(final List<F<A, B>> lf) {
* @return A new list that has appended the given list.
*/
public final List<A> append(final List<A> as) {
return fromList(this).append(as).toList();
return Buffer.fromList(this).prependToList(as);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List.append() was O(n+m). I made it O(n)

}

/**
* Performs a right-fold reduction across this list. This function uses O(length) stack space.
* Performs a right-fold reduction across this list.
*
* @param f The function to apply on each element of the list.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public final <B> B foldRight(final F<A, F<B, B>> f, final B b) {
return isEmpty() ? b : f.f(head()).f(tail().foldRight(f, b));
return reverse().foldLeft(flip(f), b);
}

/**
Expand Down Expand Up @@ -1581,7 +1580,9 @@ public static <A, B> P2<List<A>, List<B>> unzip(final List<P2<A, B>> xs) {
* @return A list of the given value replicated the given number of times.
*/
public static <A> List<A> replicate(final int n, final A a) {
return n <= 0 ? List.<A>nil() : replicate(n - 1, a).cons(a);
List<A> list = List.nil();
for (int i = 0; i < n; i++) { list = list.cons(a); }
return list;
}

/**
Expand Down Expand Up @@ -1786,7 +1787,7 @@ public Iterator<A> iterator() {
* Appends (snoc) the given element to this buffer to produce a new buffer.
*
* @param a The element to append to this buffer.
* @return A new buffer with the given element appended.
* @return This buffer.
*/
public Buffer<A> snoc(final A a) {
if (exported)
Expand All @@ -1805,10 +1806,10 @@ public Buffer<A> snoc(final A a) {
}

/**
* Appends the given buffer to this buffer.
* Appends the given list to this buffer.
*
* @param as The buffer to append to this one.
* @return A new buffer that has appended the given buffer.
* @param as The list to append to this buffer.
* @return This buffer.
*/
public Buffer<A> append(final List<A> as) {
for (List<A> xs = as; xs.isNotEmpty(); xs = xs.tail())
Expand All @@ -1817,6 +1818,28 @@ public Buffer<A> append(final List<A> as) {
return this;
}

/**
* Prepends the elements of this buffer to the given list.
*
* @param as the list to which elements are prepended.
*/
public List<A> prependToList(final List<A> as) {
if (isEmpty()) {
return as;
} else {
if (exported)
copy();

tail.tail(as);
return toList();
}
}

/**
* Returns <code>true</code> if this buffer is empty, <code>false</code> otherwise.
*/
public boolean isEmpty() { return start.isEmpty(); }

/**
* Returns an immutable list projection of this buffer. Modifications to the underlying buffer
* will <em>not</em> be reflected in returned lists.
Expand Down
7 changes: 5 additions & 2 deletions core/src/main/java/fj/data/Stream.java
Original file line number Diff line number Diff line change
Expand Up @@ -1233,7 +1233,10 @@ public final A index(final int i) {
* <code>false</code> otherwise.
*/
public final boolean forall(final F<A, Boolean> f) {
return isEmpty() || f.f(head()) && tail()._1().forall(f);
for (final A a : this) {
if (!f.f(a)) return false;
}
return true;
}

@Override
Expand Down Expand Up @@ -1430,7 +1433,7 @@ private static final class Cons<A> extends Stream<A> {

Cons(final A head, final P1<Stream<A>> tail) {
this.head = head;
this.tail = tail.memo();
this.tail = tail.weakMemo();
}

public A head() {
Expand Down
13 changes: 13 additions & 0 deletions props-core/src/test/java/fj/data/properties/ListProperties.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package fj.data.properties;

import fj.Ord;
import fj.P;
import fj.P2;
import fj.data.List;
import fj.test.reflect.CheckParams;
import fj.test.runner.PropertyTestRunner;
import fj.test.Gen;
import fj.test.Property;
Expand All @@ -18,6 +20,7 @@
* Created by Zheka Kozlov on 02.06.2015.
*/
@RunWith(PropertyTestRunner.class)
@CheckParams(maxSize = 10000)
public class ListProperties {

public Property isPrefixOf() {
Expand Down Expand Up @@ -53,4 +56,14 @@ public Property isSuffixOfDifferentHeads() {
return property(arbList(arbInteger), arbList(arbInteger), arbInteger, arbInteger, (list1, list2, h1, h2) ->
implies(intEqual.notEq(h1, h2), () -> prop(!list1.snoc(h1).isSuffixOf(intEqual, list2.snoc(h2)))));
}

public Property listOrdEqual() {
return property(arbList(arbInteger), list -> prop(Ord.listOrd(Ord.intOrd).equal().eq(list, list)));
}

public Property listOrdReverse() {
final Ord<List<Integer>> ord = Ord.listOrd(Ord.intOrd);
return property(arbList(arbInteger), arbList(arbInteger), (list1, list2) ->
prop(ord.compare(list1, list2) == ord.reverse().compare(list1, list2).reverse()));
}
}
Loading