Skip to content

Predicate Combinators closes #98 #104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 9, 2015
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
124 changes: 106 additions & 18 deletions core/src/main/java/fj/function/Booleans.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package fj.function;

import fj.F;
import fj.F2;
import fj.F3;

import static fj.Function.*;

import fj.F;
import fj.Monoid;
import fj.Semigroup;
import fj.data.List;
import fj.data.Stream;

Expand Down Expand Up @@ -42,20 +42,12 @@ private Booleans() {
/**
* Logical negation.
*/
public static final F<Boolean, Boolean> not = new F<Boolean, Boolean>() {
public Boolean f(final Boolean p) {
return !p;
}
};
public static final F<Boolean, Boolean> not = p -> !p;

/**
* Curried form of logical "only if" (material implication).
*/
public static final F<Boolean, F<Boolean, Boolean>> implies = curry(new F2<Boolean, Boolean, Boolean>() {
public Boolean f(final Boolean p, final Boolean q) {
return !p || q;
}
});
public static final F<Boolean, F<Boolean, Boolean>> implies = curry((p, q) -> !p || q);

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

/**
* maps given function to the predicate function
* @param p predicate to be mapped over
* @param f function
* @return predicate function
*/
public static <A, B> F<B, Boolean> contramap(F<B, A> f, F<A, Boolean> p){
return compose(p, f);
}

/**
* alias for contramap
* @param p predicate to be mapped over
* @param f function
* @return predicate function
*/
public static <A, B> F<B, Boolean> is(F<B, A> f, F<A, Boolean> p){
return contramap(f, p);
}

/**
* returns inverse of contramap
* @param p predicate to be mapped over
* @param f function
* @return predicate function
*/
public static <A, B> F<B, Boolean> isnot(F<B, A> f, F<A, Boolean> p){
return compose(not, contramap(f, p));
}

/**
* composes the given predicate using conjunction
* @param p1 first predicate
* @param p2 second predicate
* @return composed predicate function
*/
public static <A> F<A, Boolean> and(F<A, Boolean> p1, F<A, Boolean> p2){
return Semigroup.<A, Boolean>functionSemigroup(conjunctionSemigroup).sum(p1, p2);
}

/**
* composes the given predicate using exclusive disjunction
* @param p1 first predicate
* @param p2 second predicate
* @return composed predicate function
*/
public static <A> F<A, Boolean> xor(F<A, Boolean> p1, F<A, Boolean> p2){
return Semigroup.<A, Boolean>functionSemigroup(exclusiveDisjunctionSemiGroup).sum(p1, p2);
}

/**
* returns composed predicate using disjunction
* @param p1 first predicate
* @param p2 second predicate
* @return composed predicate
*/
public static <A> F<A, Boolean> or(F<A, Boolean> p1, F<A, Boolean> p2){
return Semigroup.<A, Boolean>functionSemigroup(disjunctionSemigroup).sum(p1, p2);
}

/**
* Returns true if all the elements of the given stream are true.
*
Expand All @@ -102,6 +154,46 @@ public static boolean and(final Stream<Boolean> l) {
return Monoid.conjunctionMonoid.sumLeft(l);
}

/**
* Returns composed predicate
*
* @param l A stream of predicates
* @return composed predicate
*/
public static <A> F<A, Boolean> andAll(final Stream<F<A, Boolean>> l) {
return Monoid.<A, Boolean>functionMonoid(Monoid.conjunctionMonoid).sumLeft(l);
}

/**
* Returns a composed predicate of given List of predicates
*
* @param l A list of predicate functions
* @return composed predicate function
*/
public static <A> F<A, Boolean> andAll(final List<F<A, Boolean>> l) {
return Monoid.<A, Boolean>functionMonoid(Monoid.conjunctionMonoid).sumLeft(l);
}

/**
* Returns a composed predicate of given List of predicates
*
* @param l A list of predicate functions
* @return composed predicate function
*/
public static <A> F<A, Boolean> orAll(final List<F<A, Boolean>> l) {
return Monoid.<A, Boolean>functionMonoid(Monoid.disjunctionMonoid).sumLeft(l);
}

/**
* Returns a composed predicate of given Stream of predicates
*
* @param l A stream of predicate functions
* @return composed predicate function
*/
public static <A> F<A, Boolean> orAll(final Stream<F<A, Boolean>> l) {
return Monoid.<A, Boolean>functionMonoid(Monoid.disjunctionMonoid).sumLeft(l);
}

/**
* Returns true if any element of the given list is true.
*
Expand Down Expand Up @@ -139,10 +231,6 @@ public static <A> F<A, Boolean> not(final F<A, Boolean> p) {
* @return A function that returns its second argument if the first argument is true, otherwise the third argument.
*/
public static <A> F<Boolean, F<A, F<A, A>>> cond() {
return curry(new F3<Boolean, A, A, A>() {
public A f(final Boolean p, final A a1, final A a2) {
return p ? a1 : a2;
}
});
return curry((p, a1, a2) -> p ? a1 : a2);
}
}
81 changes: 81 additions & 0 deletions core/src/test/java/fj/data/BooleansTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package fj.data;

import fj.F;
import fj.function.Booleans;
import fj.test.Property;
import org.junit.Assert;
import org.junit.Test;

import static fj.P1.curry;
import static fj.data.List.list;
import static fj.function.Booleans.isnot;
import static org.hamcrest.core.Is.is;

/**
* Created by amar on 28/01/15.
*/
public class BooleansTest {

@Test
public void testAnd(){
F<String, Boolean> f1 = a -> a.startsWith("fj");
F<String, Boolean> f2 = a -> a.contains("data");

F<String, Boolean> f3 = Booleans.and(f1, f2);

Assert.assertTrue(f3.f("fj.data"));
Assert.assertTrue(f3.f("fj.data.Function"));

}

@Test
public void testOr(){
F<String, Boolean> f1 = a -> a.startsWith("fj");
F<String, Boolean> f2 = a -> a.startsWith("someOtherPackage");

F<String, Boolean> f3 = Booleans.or(f1, f2);

Assert.assertTrue(f3.f("fj.data"));
Assert.assertTrue(f3.f("someOtherPackage.fj.data"));
Assert.assertFalse(f3.f("something.fj.data.Function"));

}

@Test
public void testComap(){
F<String, Boolean> f1 = a -> a.length() > 3;
F<Integer, String> f2 = a -> a.toString();

F<Integer, Boolean> f3 = Booleans.contramap(f2, f1);

Assert.assertTrue(f3.f(1000));
Assert.assertFalse(f3.f(100));

}

@SuppressWarnings("unchecked")
@Test
public void testAndAll(){
F<String, Boolean> f1 = a -> a.endsWith("fj");
F<String, Boolean> f2 = a -> a.startsWith("someOtherPackage");
F<String, Boolean> f3 = a -> a.length() < 20;

F<String, Boolean> f4 = Booleans.andAll(Stream.<F<String, Boolean>>stream(f1, f2, f3));

Assert.assertTrue(f4.f("someOtherPackage.fj"));
Assert.assertFalse(f4.f("otther"));
Assert.assertFalse(f4.f("someOtherPackage.fj.data.something.really.big"));

}

@SuppressWarnings("unchecked")
@Test
public void testIsNot(){
F<Integer, Boolean> f1 = a -> a == 4;
List<String> result = list("some", "come", "done!").filter(isnot(String::length, f1));

Assert.assertThat(result.length(), is(1));
Assert.assertEquals(result, list("done!"));

}
}