Skip to content

Commit 8cfcc2d

Browse files
committed
Difference
1 parent 8ae9df8 commit 8cfcc2d

File tree

3 files changed

+100
-2
lines changed

3 files changed

+100
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
1010
- `CmpEqBy`, `CmpEq`, `GTBy`, `GT`, `LTBy`, `LT`, `GTEBy`, `GTE`, `LTEBy`, and `LTE` inequality checks
1111
- `MinBy`, `MaxBy`, `Min`, and `Max` semigroups
1212
- `Product2-8` interfaces, representing general product types
13-
- `Union`, a semigroup that behaves like a lazy set union on `Iterable`s
13+
- `Union`, a semigroup that behaves like a lazy set union on `Iterable`s
14+
- `Difference`, a semigroup that behaves like a partially lazy set difference on `Iterable`s
1415

1516
### Changed
1617
- `Tuple2-8` now implement `Product2-8`
17-
- `Into2-8` now accepts a product of the same cardinality, instead of requiring a tuple
18+
- `Into2-8` now accept a product of the same cardinality, instead of requiring a tuple
1819
- `CoProduct2-8#project` now return generalized products
1920
- `Choice2-8#project` return tuples
2021

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.jnape.palatable.lambda.monoid;
2+
3+
import com.jnape.palatable.lambda.functions.Fn1;
4+
5+
import java.util.Collections;
6+
import java.util.HashSet;
7+
8+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Distinct.distinct;
9+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Empty.empty;
10+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Filter.filter;
11+
import static com.jnape.palatable.lambda.functions.builtin.fn2.ToCollection.toCollection;
12+
13+
public final class Difference<A> implements Monoid<Iterable<A>> {
14+
15+
private static final Difference INSTANCE = new Difference();
16+
17+
private Difference() {
18+
}
19+
20+
@Override
21+
public Iterable<A> identity() {
22+
return Collections::emptyIterator;
23+
}
24+
25+
26+
@Override
27+
public Iterable<A> apply(Iterable<A> xs, Iterable<A> ys) {
28+
return () -> {
29+
if (empty(xs))
30+
return xs.iterator();
31+
32+
if (empty(ys))
33+
return distinct(xs).iterator();
34+
35+
//todo: pre-order dfs fold the expression tree to make stack-safe
36+
HashSet<A> uniqueYs = toCollection(HashSet::new, ys);
37+
return distinct(filter(a -> !uniqueYs.contains(a), xs)).iterator();
38+
};
39+
}
40+
41+
@SuppressWarnings("unchecked")
42+
public static <A> Difference<A> difference() {
43+
return INSTANCE;
44+
}
45+
46+
public static <A> Fn1<Iterable<A>, Iterable<A>> difference(Iterable<A> xs) {
47+
return Difference.<A>difference().apply(xs);
48+
}
49+
50+
public static <A> Iterable<A> difference(Iterable<A> xs, Iterable<A> ys) {
51+
return difference(xs).apply(ys);
52+
}
53+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.jnape.palatable.lambda.monoid;
2+
3+
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.traitor.annotations.TestTraits;
5+
import com.jnape.palatable.traitor.runners.Traits;
6+
import org.junit.Test;
7+
import org.junit.runner.RunWith;
8+
import testsupport.traits.EmptyIterableSupport;
9+
import testsupport.traits.FiniteIteration;
10+
import testsupport.traits.ImmutableIteration;
11+
import testsupport.traits.InfiniteIterableSupport;
12+
import testsupport.traits.Laziness;
13+
14+
import static com.jnape.palatable.lambda.monoid.Difference.difference;
15+
import static java.util.Arrays.asList;
16+
import static java.util.Collections.emptyList;
17+
import static java.util.Collections.singletonList;
18+
import static org.junit.Assert.assertThat;
19+
import static testsupport.matchers.IterableMatcher.isEmpty;
20+
import static testsupport.matchers.IterableMatcher.iterates;
21+
22+
@RunWith(Traits.class)
23+
public class DifferenceTest {
24+
25+
@TestTraits({Laziness.class, InfiniteIterableSupport.class, EmptyIterableSupport.class, FiniteIteration.class, ImmutableIteration.class})
26+
public Fn1<Iterable<Integer>, Iterable<Integer>> testSubject() {
27+
return Difference.<Integer>difference().flip().apply(asList(1, 2, 3));
28+
}
29+
30+
@Test
31+
public void identity() {
32+
assertThat(difference().identity(), isEmpty());
33+
}
34+
35+
@Test
36+
public void semigroup() {
37+
assertThat(difference(emptyList(), emptyList()), isEmpty());
38+
assertThat(difference(asList(1, 2, 3), emptyList()), iterates(1, 2, 3));
39+
assertThat(difference(asList(1, 2, 2, 3), emptyList()), iterates(1, 2, 3));
40+
assertThat(difference(emptyList(), asList(1, 2, 3)), isEmpty());
41+
assertThat(difference(asList(1, 2, 3), singletonList(4)), iterates(1, 2, 3));
42+
assertThat(difference(asList(1, 2, 3), asList(2, 4)), iterates(1, 3));
43+
}
44+
}

0 commit comments

Comments
 (0)