Skip to content

Commit d0dc327

Browse files
committed
Merge pull request functionaljava#104 from amarpotghan/pred-combs
Predicate Combinators closes functionaljava#98
2 parents b76019f + 6825358 commit d0dc327

File tree

2 files changed

+187
-18
lines changed

2 files changed

+187
-18
lines changed

core/src/main/java/fj/function/Booleans.java

Lines changed: 106 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package fj.function;
22

3-
import fj.F;
4-
import fj.F2;
5-
import fj.F3;
3+
64
import static fj.Function.*;
75

6+
import fj.F;
87
import fj.Monoid;
8+
import fj.Semigroup;
99
import fj.data.List;
1010
import fj.data.Stream;
1111

@@ -42,20 +42,12 @@ private Booleans() {
4242
/**
4343
* Logical negation.
4444
*/
45-
public static final F<Boolean, Boolean> not = new F<Boolean, Boolean>() {
46-
public Boolean f(final Boolean p) {
47-
return !p;
48-
}
49-
};
45+
public static final F<Boolean, Boolean> not = p -> !p;
5046

5147
/**
5248
* Curried form of logical "only if" (material implication).
5349
*/
54-
public static final F<Boolean, F<Boolean, Boolean>> implies = curry(new F2<Boolean, Boolean, Boolean>() {
55-
public Boolean f(final Boolean p, final Boolean q) {
56-
return !p || q;
57-
}
58-
});
50+
public static final F<Boolean, F<Boolean, Boolean>> implies = curry((p, q) -> !p || q);
5951

6052
/**
6153
* Curried form of logical "if" (reverse material implication).
@@ -92,6 +84,66 @@ public static boolean and(final List<Boolean> l) {
9284
return Monoid.conjunctionMonoid.sumLeft(l);
9385
}
9486

87+
/**
88+
* maps given function to the predicate function
89+
* @param p predicate to be mapped over
90+
* @param f function
91+
* @return predicate function
92+
*/
93+
public static <A, B> F<B, Boolean> contramap(F<B, A> f, F<A, Boolean> p){
94+
return compose(p, f);
95+
}
96+
97+
/**
98+
* alias for contramap
99+
* @param p predicate to be mapped over
100+
* @param f function
101+
* @return predicate function
102+
*/
103+
public static <A, B> F<B, Boolean> is(F<B, A> f, F<A, Boolean> p){
104+
return contramap(f, p);
105+
}
106+
107+
/**
108+
* returns inverse of contramap
109+
* @param p predicate to be mapped over
110+
* @param f function
111+
* @return predicate function
112+
*/
113+
public static <A, B> F<B, Boolean> isnot(F<B, A> f, F<A, Boolean> p){
114+
return compose(not, contramap(f, p));
115+
}
116+
117+
/**
118+
* composes the given predicate using conjunction
119+
* @param p1 first predicate
120+
* @param p2 second predicate
121+
* @return composed predicate function
122+
*/
123+
public static <A> F<A, Boolean> and(F<A, Boolean> p1, F<A, Boolean> p2){
124+
return Semigroup.<A, Boolean>functionSemigroup(conjunctionSemigroup).sum(p1, p2);
125+
}
126+
127+
/**
128+
* composes the given predicate using exclusive disjunction
129+
* @param p1 first predicate
130+
* @param p2 second predicate
131+
* @return composed predicate function
132+
*/
133+
public static <A> F<A, Boolean> xor(F<A, Boolean> p1, F<A, Boolean> p2){
134+
return Semigroup.<A, Boolean>functionSemigroup(exclusiveDisjunctionSemiGroup).sum(p1, p2);
135+
}
136+
137+
/**
138+
* returns composed predicate using disjunction
139+
* @param p1 first predicate
140+
* @param p2 second predicate
141+
* @return composed predicate
142+
*/
143+
public static <A> F<A, Boolean> or(F<A, Boolean> p1, F<A, Boolean> p2){
144+
return Semigroup.<A, Boolean>functionSemigroup(disjunctionSemigroup).sum(p1, p2);
145+
}
146+
95147
/**
96148
* Returns true if all the elements of the given stream are true.
97149
*
@@ -102,6 +154,46 @@ public static boolean and(final Stream<Boolean> l) {
102154
return Monoid.conjunctionMonoid.sumLeft(l);
103155
}
104156

157+
/**
158+
* Returns composed predicate
159+
*
160+
* @param l A stream of predicates
161+
* @return composed predicate
162+
*/
163+
public static <A> F<A, Boolean> andAll(final Stream<F<A, Boolean>> l) {
164+
return Monoid.<A, Boolean>functionMonoid(Monoid.conjunctionMonoid).sumLeft(l);
165+
}
166+
167+
/**
168+
* Returns a composed predicate of given List of predicates
169+
*
170+
* @param l A list of predicate functions
171+
* @return composed predicate function
172+
*/
173+
public static <A> F<A, Boolean> andAll(final List<F<A, Boolean>> l) {
174+
return Monoid.<A, Boolean>functionMonoid(Monoid.conjunctionMonoid).sumLeft(l);
175+
}
176+
177+
/**
178+
* Returns a composed predicate of given List of predicates
179+
*
180+
* @param l A list of predicate functions
181+
* @return composed predicate function
182+
*/
183+
public static <A> F<A, Boolean> orAll(final List<F<A, Boolean>> l) {
184+
return Monoid.<A, Boolean>functionMonoid(Monoid.disjunctionMonoid).sumLeft(l);
185+
}
186+
187+
/**
188+
* Returns a composed predicate of given Stream of predicates
189+
*
190+
* @param l A stream of predicate functions
191+
* @return composed predicate function
192+
*/
193+
public static <A> F<A, Boolean> orAll(final Stream<F<A, Boolean>> l) {
194+
return Monoid.<A, Boolean>functionMonoid(Monoid.disjunctionMonoid).sumLeft(l);
195+
}
196+
105197
/**
106198
* Returns true if any element of the given list is true.
107199
*
@@ -139,10 +231,6 @@ public static <A> F<A, Boolean> not(final F<A, Boolean> p) {
139231
* @return A function that returns its second argument if the first argument is true, otherwise the third argument.
140232
*/
141233
public static <A> F<Boolean, F<A, F<A, A>>> cond() {
142-
return curry(new F3<Boolean, A, A, A>() {
143-
public A f(final Boolean p, final A a1, final A a2) {
144-
return p ? a1 : a2;
145-
}
146-
});
234+
return curry((p, a1, a2) -> p ? a1 : a2);
147235
}
148236
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package fj.data;
2+
3+
import fj.F;
4+
import fj.function.Booleans;
5+
import fj.test.Property;
6+
import org.junit.Assert;
7+
import org.junit.Test;
8+
9+
import static fj.P1.curry;
10+
import static fj.data.List.list;
11+
import static fj.function.Booleans.isnot;
12+
import static org.hamcrest.core.Is.is;
13+
14+
/**
15+
* Created by amar on 28/01/15.
16+
*/
17+
public class BooleansTest {
18+
19+
@Test
20+
public void testAnd(){
21+
F<String, Boolean> f1 = a -> a.startsWith("fj");
22+
F<String, Boolean> f2 = a -> a.contains("data");
23+
24+
F<String, Boolean> f3 = Booleans.and(f1, f2);
25+
26+
Assert.assertTrue(f3.f("fj.data"));
27+
Assert.assertTrue(f3.f("fj.data.Function"));
28+
29+
}
30+
31+
@Test
32+
public void testOr(){
33+
F<String, Boolean> f1 = a -> a.startsWith("fj");
34+
F<String, Boolean> f2 = a -> a.startsWith("someOtherPackage");
35+
36+
F<String, Boolean> f3 = Booleans.or(f1, f2);
37+
38+
Assert.assertTrue(f3.f("fj.data"));
39+
Assert.assertTrue(f3.f("someOtherPackage.fj.data"));
40+
Assert.assertFalse(f3.f("something.fj.data.Function"));
41+
42+
}
43+
44+
@Test
45+
public void testComap(){
46+
F<String, Boolean> f1 = a -> a.length() > 3;
47+
F<Integer, String> f2 = a -> a.toString();
48+
49+
F<Integer, Boolean> f3 = Booleans.contramap(f2, f1);
50+
51+
Assert.assertTrue(f3.f(1000));
52+
Assert.assertFalse(f3.f(100));
53+
54+
}
55+
56+
@SuppressWarnings("unchecked")
57+
@Test
58+
public void testAndAll(){
59+
F<String, Boolean> f1 = a -> a.endsWith("fj");
60+
F<String, Boolean> f2 = a -> a.startsWith("someOtherPackage");
61+
F<String, Boolean> f3 = a -> a.length() < 20;
62+
63+
F<String, Boolean> f4 = Booleans.andAll(Stream.<F<String, Boolean>>stream(f1, f2, f3));
64+
65+
Assert.assertTrue(f4.f("someOtherPackage.fj"));
66+
Assert.assertFalse(f4.f("otther"));
67+
Assert.assertFalse(f4.f("someOtherPackage.fj.data.something.really.big"));
68+
69+
}
70+
71+
@SuppressWarnings("unchecked")
72+
@Test
73+
public void testIsNot(){
74+
F<Integer, Boolean> f1 = a -> a == 4;
75+
List<String> result = list("some", "come", "done!").filter(isnot(String::length, f1));
76+
77+
Assert.assertThat(result.length(), is(1));
78+
Assert.assertEquals(result, list("done!"));
79+
80+
}
81+
}

0 commit comments

Comments
 (0)