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
13 changes: 13 additions & 0 deletions core/src/main/java/fj/Hash.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import fj.data.vector.V7;
import fj.data.vector.V8;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
* Produces a hash code for an object which should attempt uniqueness.
*
Expand Down Expand Up @@ -102,6 +105,16 @@ public static <A> Hash<A> anyHash() {
*/
public static final Hash<Short> shortHash = anyHash();

/**
* A hash instance for the <code>BigInteger</code> type.
*/
public static final Hash<BigInteger> bigintHash = anyHash();

/**
* A hash instance for the <code>BigDecimal</code> type.
*/
public static final Hash<BigDecimal> bigdecimalHash = anyHash();

/**
* A hash instance for the <code>String</code> type.
*/
Expand Down
167 changes: 153 additions & 14 deletions core/src/main/java/fj/data/NonEmptyList.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package fj.data;

import fj.F;
import fj.F1Functions;
import fj.*;
import fj.function.Effect1;

import static fj.data.Option.some;
import static fj.data.Option.somes;

import java.util.Collection;
import java.util.Iterator;

import static fj.Function.identity;
import static fj.data.Option.some;
import static fj.data.Option.somes;

/**
* Provides an in-memory, immutable, singly linked list with total <code>head</code> and <code>tail</code>.
*
Expand All @@ -26,17 +26,19 @@ public Iterator<A> iterator() {
return toCollection().iterator();
}

private final A head;

private final List<A> tail;

/**
* The first element of this linked list.
*/
@SuppressWarnings({"PublicField", "ClassEscapesDefinedScope"})
public final A head;
public A head() { return head; }

/**
* This list without the first element.
*/
@SuppressWarnings({"PublicField"})
public final List<A> tail;
public List<A> tail() { return tail; }

private NonEmptyList(final A head, final List<A> tail) {
this.head = head;
Expand All @@ -53,6 +55,23 @@ public NonEmptyList<A> cons(final A a) {
return nel(a, tail.cons(head));
}

/**
* Appends (snoc) the given element to this non empty list to produce a new non empty list. O(n).
*
* @param a The element to append to this non empty list.
* @return A new non empty list with the given element appended.
*/
public NonEmptyList<A> snoc(final A a) {
return nel(head, tail.snoc(a));
}

/**
* The length of this list.
*
* @return The length of this list.
*/
public int length() { return 1 + tail.length(); }

/**
* Appends the given list to this list.
*
Expand Down Expand Up @@ -135,6 +154,100 @@ public <B> NonEmptyList<B> mapTails(final F<NonEmptyList<A>, B> f) {
return tails().map(f);
}

/**
* Intersperses the given argument between each element of this non empty list.
*
* @param a The separator to intersperse in this non empty list.
* @return A non empty list with the given separator interspersed.
*/
public NonEmptyList<A> intersperse(final A a) {
final List<A> list = toList().intersperse(a);
return nel(list.head(), list.tail());
}

/**
* Reverse this non empty list in constant stack space.
*
* @return A new non empty list with the elements in reverse order.
*/
public NonEmptyList<A> reverse() {
final List<A> list = toList().reverse();
return nel(list.head(), list.tail());
}

/**
* Sorts this non empty list using the given order over elements using a <em>merge sort</em> algorithm.
*
* @param o The order over the elements of this non empty list.
* @return A sorted non empty list according to the given order.
*/
public NonEmptyList<A> sort(final Ord<A> o) {
final List<A> list = toList().sort(o);
return nel(list.head(), list.tail());
}

/**
* Zips this non empty list with the given non empty list to produce a list of pairs. If this list and the given list
* have different lengths, then the longer list is normalised so this function never fails.
*
* @param bs The non empty list to zip this non empty list with.
* @return A new non empty list with a length the same as the shortest of this list and the given list.
*/
public <B> NonEmptyList<P2<A, B>> zip(final NonEmptyList<B> bs) {
final List<P2<A, B>> list = toList().zip(bs.toList());
return nel(list.head(), list.tail());
}

/**
* Zips this non empty list with the index of its element as a pair.
*
* @return A new non empty list with the same length as this list.
*/
public NonEmptyList<P2<A, Integer>> zipIndex() {
final List<P2<A, Integer>> list = toList().zipIndex();
return nel(list.head(), list.tail());
}

/**
* Zips this non empty list with the given non empty list using the given function to produce a new list. If this list
* and the given list have different lengths, then the longer list is normalised so this function
* never fails.
*
* @param bs The non empty list to zip this non empty list with.
* @param f The function to zip this non empty list and the given non empty list with.
* @return A new non empty list with a length the same as the shortest of this list and the given list.
*/
public <B, C> NonEmptyList<C> zipWith(final List<B> bs, final F<A, F<B, C>> f) {
final List<C> list = toList().zipWith(bs, f);
return nel(list.head(), list.tail());
}

/**
* Zips this non empty list with the given non empty list using the given function to produce a new list. If this list
* and the given list have different lengths, then the longer list is normalised so this function
* never fails.
*
* @param bs The non empty list to zip this non empty list with.
* @param f The function to zip this non empty list and the given non empty list with.
* @return A new non empty list with a length the same as the shortest of this list and the given list.
*/
public <B, C> NonEmptyList<C> zipWith(final List<B> bs, final F2<A, B, C> f) {
final List<C> list = toList().zipWith(bs, f);
return nel(list.head(), list.tail());
}

/**
* Transforms a non empty list of pairs into a non empty list of first components and
* a non empty list of second components.
*
* @param xs The non empty list of pairs to transform.
* @return A non empty list of first components and a non empty list of second components.
*/
public static <A, B> P2<NonEmptyList<A>, NonEmptyList<B>> unzip(final NonEmptyList<P2<A, B>> xs) {
final P2<List<A>, List<B>> p = List.unzip(xs.toList());
return P.p(nel(p._1().head(), p._1().tail()), nel(p._2().head(), p._2().tail()));
}

/**
* Returns a <code>List</code> projection of this list.
*
Expand Down Expand Up @@ -174,13 +287,14 @@ public static <A> NonEmptyList<A> nel(final A head, final List<A> tail) {
}

/**
* Return a non-empty list with the given value.
* Constructs a non empty list from the given elements.
*
* @param head The value in the non-empty list.
* @return A non-empty list with the given value.
* @param head The first in the non-empty list.
* @param tail The elements to construct a list's tail with.
* @return A non-empty list with the given elements.
*/
public static <A> NonEmptyList<A> nel(final A head) {
return nel(head, List.<A>nil());
public static <A> NonEmptyList<A> nel(final A head, final A... tail) {
return nel(head, List.list(tail));
}

/**
Expand All @@ -203,4 +317,29 @@ public static <A> Option<NonEmptyList<A>> fromList(final List<A> as) {
Option.<NonEmptyList<A>>none() :
some(nel(as.head(), as.tail()));
}

/**
* Concatenate (join) a non empty list of non empty lists.
*
* @param o The non empty list of non empty lists to join.
* @return A new non empty list that is the concatenation of the given lists.
*/
public static <A> NonEmptyList<A> join(final NonEmptyList<NonEmptyList<A>> o) { return o.bind(identity()); }

/**
* Perform an equality test on this list which delegates to the .equals() method of the member instances.
* This is implemented with Equal.nonEmptyListEqual using the anyEqual rule.
*
* @param obj the other object to check for equality against.
* @return true if this list is equal to the provided argument
*/
@Override public boolean equals( final Object obj ) {
return Equal.equals0(NonEmptyList.class, this, obj, () -> Equal.nonEmptyListEqual(Equal.<A>anyEqual()));
}

@Override public int hashCode() {
return Hash.nonEmptyListHash(Hash.<A>anyHash()).hash(this);
}

@Override public String toString() { return Show.nonEmptyListShow(Show.<A>anyShow()).showS(this); }
}
2 changes: 1 addition & 1 deletion core/src/main/java/fj/data/fingertrees/Deep.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ private static final <V, A> FingerTree<V, A> deepR(final Measured<V, A> measured
@Override public FingerTree<V, A> append(final FingerTree<V, A> t) {
final Measured<V, A> m = measured();
return t.match(
constant(t),
constant(this),
single -> snoc(single.value()),
deep -> new Deep<>(m, m.sum(measure(), deep.measure()), prefix,
addDigits0(m, middle, suffix, deep.prefix, deep.middle), deep.suffix));
Expand Down
14 changes: 14 additions & 0 deletions core/src/main/java/fj/test/Arbitrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,10 @@ public static <A> Arbitrary<List<A>> arbList(final Arbitrary<A> aa) {
return arbitrary(listOf(aa.gen));
}

public static <A> Arbitrary<NonEmptyList<A>> arbNonEmptyList(final Arbitrary<A> aa) {
return arbitrary(Gen.listOf1(aa.gen).map(list -> NonEmptyList.fromList(list).some()));
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this always a list of size 1, rather than some arbitrary length?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No.

  • Gen.listOf - list of arbitrary length ([0, ∞)).
  • Gen.listOf1 - non empty list of arbitrary length ([1, ∞)).
  • Gen.sequenceN - list of specified length.

Copy link
Contributor

Choose a reason for hiding this comment

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

You're right, but I had to dig through the code to convince myself this is true. The existing javadoc should be more specific.

/**
* Returns an arbitrary implementation for streams.
*
Expand Down Expand Up @@ -761,6 +765,16 @@ public Array<A> f(final List<A> as) {
}));
}

/**
* Returns an arbitrary implementation for sequences.
*
* @param aa An arbitrary implementation for the type over which the sequence is defined.
* @return An arbitrary implementation for sequences.
*/
public static <A> Arbitrary<Seq<A>> arbSeq(final Arbitrary<A> aa) {
return arbitrary(arbArray(aa).gen.map(array -> Seq.seq((A[]) array.array())));
}

/**
* Returns an arbitrary implementation for throwables.
*
Expand Down
12 changes: 2 additions & 10 deletions core/src/main/java/fj/test/Gen.java
Original file line number Diff line number Diff line change
Expand Up @@ -554,15 +554,7 @@ public Gen<A> f(final Integer i) {
* @return A generator of lists whose values come from the given generator.
*/
public static <A> Gen<List<A>> listOf(final Gen<A> g, final int x) {
return sized(new F<Integer, Gen<List<A>>>() {
public Gen<List<A>> f(final Integer size) {
return choose(x, size).bind(new F<Integer, Gen<List<A>>>() {
public Gen<List<A>> f(final Integer n) {
return sequenceN(n, g);
}
});
}
});
return sized(size -> choose(x, max(x, size)).bind(n -> sequenceN(n, g)));
}

/**
Expand All @@ -576,7 +568,7 @@ public static <A> Gen<List<A>> listOf(final Gen<A> g) {
}

/**
* Returns a generator of lists whose values come from the given generator.
* Returns a generator of non empty lists whose values come from the given generator.
*
* @param g The generator to produce values from for the returned generator.
* @return A generator of lists whose values come from the given generator.
Expand Down
43 changes: 10 additions & 33 deletions core/src/main/java/fj/test/Rand.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,38 +124,15 @@ public Random f(final Long x) {
/**
* A standard random generator that uses {@link Random}.
*/
public static final Rand standard = new Rand(new F<Option<Long>, F<Integer, F<Integer, Integer>>>() {
public F<Integer, F<Integer, Integer>> f(final Option<Long> seed) {
return new F<Integer, F<Integer, Integer>>() {
public F<Integer, Integer> f(final Integer from) {
return new F<Integer, Integer>() {
public Integer f(final Integer to) {
if(from == to){
return from;
}else{
final int f = min(from, to);
final int t = max(from, to);
final int x = Math.abs(t - f);
return f + seed.map(fr).orSome(new Random()).nextInt(x == Integer.MIN_VALUE ? Integer.MAX_VALUE : x);
}
}
};
}
};
}
}, new F<Option<Long>, F<Double, F<Double, Double>>>() {
public F<Double, F<Double, Double>> f(final Option<Long> seed) {
return new F<Double, F<Double, Double>>() {
public F<Double, Double> f(final Double from) {
return new F<Double, Double>() {
public Double f(final Double to) {
final double f = min(from, to);
final double t = max(from, to);
return seed.map(fr).orSome(new Random()).nextDouble() * (t - f) + f;
}
};
}
};
}
public static final Rand standard = new Rand(seed -> from -> to -> {
final int min = min(from, to);
final int max = max(from, to);
final Random random = seed.map(fr).orSome(new Random());
return (int) ((random.nextLong() & Long.MAX_VALUE) % (1L + max - min)) + min;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let me explain why I changed these lines. There were two bugs here:

  • The upper bound was exclusive, but it should be inclusive.
  • It didn't work for ranges wider than Integer.MAX_VALUE (e.g. range [-10; Integer.MAX_VALUE]).

}, seed -> from -> to -> {
final double min = min(from, to);
final double max = max(from, to);
final Random random = seed.map(fr).orSome(new Random());
return random.nextDouble() * (max - min) + min;
});
}
Loading