diff --git a/src/main/java/ru/spbau/mit/Collections.java b/src/main/java/ru/spbau/mit/Collections.java new file mode 100644 index 0000000..c2e5e0c --- /dev/null +++ b/src/main/java/ru/spbau/mit/Collections.java @@ -0,0 +1,64 @@ +package ru.spbau.mit; + +import java.util.ArrayList; +import java.util.List; + +public final class Collections { + private Collections() {} + + public static List map(Function1 f, Iterable c) { + List newcol = new ArrayList<>(); + for (T el : c) { + newcol.add(f.apply(el)); + } + return newcol; + } + + public static List filter(Predicate pred, Iterable c) { + List newcol = new ArrayList<>(); + for (T el : c) { + if (pred.apply(el)) { + newcol.add(el); + } + } + return newcol; + } + + public static List takeWhile(Predicate pred, Iterable c) { + List newcol = new ArrayList<>(); + for (T el : c) { + if (!pred.apply(el)) { + break; + } + newcol.add(el); + } + return newcol; + } + + public static List takeUnless(Predicate pred, Iterable c) { + return takeWhile(pred.not(), c); + } + + + public static I foldl(Function2 f, Iterable c, I init) { + I res = init; + for (T el : c) { + res = f.apply2(res, el); + } + return res; + } + + public static I foldr(Function2 f, Iterable c, I init) { + Function1 resfunc = new Function1() { + @Override + public I apply(I arg) { + return arg; + } + }; + + for (T el : c) { + resfunc = f.bind1(el).compose(resfunc); + } + return resfunc.apply(init); + } +} diff --git a/src/main/java/ru/spbau/mit/Function1.java b/src/main/java/ru/spbau/mit/Function1.java new file mode 100644 index 0000000..16783fe --- /dev/null +++ b/src/main/java/ru/spbau/mit/Function1.java @@ -0,0 +1,14 @@ +package ru.spbau.mit; + +public abstract class Function1 { + public Function1 compose(final Function1 g) { + return new Function1() { + @Override + public G apply(T arg) { + return g.apply(Function1.this.apply(arg)); + } + }; + } + + public abstract S apply(T arg); +} diff --git a/src/main/java/ru/spbau/mit/Function2.java b/src/main/java/ru/spbau/mit/Function2.java new file mode 100644 index 0000000..92179bb --- /dev/null +++ b/src/main/java/ru/spbau/mit/Function2.java @@ -0,0 +1,41 @@ +package ru.spbau.mit; + +public abstract class Function2 { + public Function1> curry() { + return new Function1>() { + @Override + public Function1 apply(T1 arg1) { + return Function2.this.bind1(arg1); + } + }; + } + + // because of compose can't extend from Function1 + public Function2 compose(final Function1 g) { + return new Function2() { + public S2 apply2(T1 arg1, T2 arg2) { + return g.apply(Function2.this.apply2(arg1, arg2)); + } + }; + } + + public Function1 bind1(final T1 arg1) { + return new Function1() { + @Override + public S apply(T2 arg2) { + return apply2(arg1, arg2); + } + }; + } + + public Function1 bind2(final T2 arg2) { + return new Function1() { + @Override + public S apply(T1 arg1) { + return apply2(arg1, arg2); + } + }; + } + + public abstract S apply2(T1 arg1, T2 arg2); +} diff --git a/src/main/java/ru/spbau/mit/Predicate.java b/src/main/java/ru/spbau/mit/Predicate.java new file mode 100644 index 0000000..c2b9fd2 --- /dev/null +++ b/src/main/java/ru/spbau/mit/Predicate.java @@ -0,0 +1,44 @@ +package ru.spbau.mit; + +public abstract class Predicate extends Function1 { + public static final Predicate ALWAYS_TRUE = new Predicate() { + @Override + public Boolean apply(Object arg) { + return true; + } + }; + + public static final Predicate ALWAYS_FALSE = new Predicate() { + @Override + public Boolean apply(Object arg) { + return false; + } + }; + + public Predicate and(final Predicate rhs) { + return new Predicate() { + @Override + public Boolean apply(T arg) { + return Predicate.this.apply(arg) && rhs.apply(arg); + } + }; + } + + public Predicate or(final Predicate rhs) { + return new Predicate() { + @Override + public Boolean apply(T arg) { + return Predicate.this.apply(arg) || rhs.apply(arg); + } + }; + } + + public Predicate not() { + return new Predicate() { + @Override + public Boolean apply(T arg) { + return !Predicate.this.apply(arg); + } + }; + } +} diff --git a/src/test/java/ru/spbau/mit/CollectionsTest.java b/src/test/java/ru/spbau/mit/CollectionsTest.java new file mode 100644 index 0000000..fb31488 --- /dev/null +++ b/src/test/java/ru/spbau/mit/CollectionsTest.java @@ -0,0 +1,179 @@ +package ru.spbau.mit; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.junit.Assert.*; + +public class CollectionsTest { + private static final Integer MAGIC_1 = 1; + private static final Integer MAGIC_2 = -4; + private static final Integer MAGIC_3 = 3; + private static final Integer MAGIC_4 = 10; + private static final Integer MAGIC_5 = -20; + + private static final List INT_ARR = Arrays.asList( + MAGIC_1, MAGIC_2, MAGIC_3, MAGIC_4, MAGIC_5); + + private static final List INT_ARR_NEGATED = Arrays.asList( + -MAGIC_1, -MAGIC_2, -MAGIC_3, -MAGIC_4, -MAGIC_5); + + private static final List ARR_WITH_NULL = Arrays.asList( + null, 1, null, 4, null); + + private static final List INT_ARR_STRINGED = Arrays.asList( + MAGIC_1.toString(), + MAGIC_2.toString(), + MAGIC_3.toString(), + MAGIC_4.toString(), + MAGIC_5.toString() + ); + + private static final Predicate IS_POSITIVE_INTEGER = new Predicate() { + @Override + public Boolean apply(Integer arg) { + return arg > 0; + } + }; + + private static final Predicate IS_NULL = new Predicate() { + @Override + public Boolean apply(Object arg) { + return arg == null; + } + }; + + private static final Predicate NOT_NULL = IS_NULL.not(); + + private static final List EMPTY_ARR = emptyList(); + + private static final Function2 MINUS = new Function2() { + @Override + public Integer apply2(Integer arg1, Integer arg2) { + return arg1 - arg2; + } + }; + + private static final Function1 ID = new Function1() { + @Override + public Object apply(Object arg) { + return arg; + } + }; + + private static final Function1 TO_STRING = new Function1() { + @Override + public String apply(Object arg) { + return arg.toString(); + } + }; + + private static final Function2 FAIL_FUNCTION = + new Function2() { + @Override + public Object apply2(Object arg1, Object arg2) { + assert false; + return null; + } + }; + + @Test + public void map() throws Exception { + assertEquals(INT_ARR_NEGATED, Collections.map(MINUS.bind1(0), INT_ARR)); + assertEquals(EMPTY_ARR, Collections.map(ID, EMPTY_ARR)); + assertEquals(INT_ARR_STRINGED, Collections.map(TO_STRING, INT_ARR)); + assertEquals(INT_ARR, Collections.map(ID, INT_ARR)); + } + + @Test + public void filter() throws Exception { + final List positiveOnly = Arrays.asList(-MAGIC_2, -MAGIC_5); + assertEquals(positiveOnly, Collections.filter(IS_POSITIVE_INTEGER, INT_ARR_NEGATED)); + assertEquals(INT_ARR, Collections.filter(Predicate.ALWAYS_TRUE, INT_ARR)); + assertEquals(EMPTY_ARR, Collections.filter(Predicate.ALWAYS_FALSE, INT_ARR)); + assertEquals(EMPTY_ARR, Collections.filter(IS_NULL, INT_ARR)); + assertEquals(Arrays.asList(null, null, null), Collections.filter(IS_NULL, ARR_WITH_NULL)); + Predicate isTen = new Predicate() { + @Override + public Boolean apply(Object arg) { + return (arg).toString().equals("10"); + } + }; + final int ten = 10; + assertEquals(singletonList(ten), Collections.filter(isTen, INT_ARR)); + } + + @Test + public void takeWhile() throws Exception { + Predicate u2 = new Predicate() { + @Override + public Boolean apply(CharSequence charSequence) { + return charSequence.length() < 2; + } + }; + assertEquals(singletonList("1"), Collections.takeWhile(u2, INT_ARR_STRINGED)); + assertEquals(EMPTY_ARR, Collections.takeWhile(IS_POSITIVE_INTEGER, INT_ARR_NEGATED)); + assertEquals(INT_ARR_STRINGED, Collections.takeWhile(Predicate.ALWAYS_TRUE, INT_ARR_STRINGED)); + ArrayList nullArray = new ArrayList<>(); + nullArray.add(null); + assertEquals(nullArray, Collections.takeWhile(IS_NULL, ARR_WITH_NULL)); + assertEquals(EMPTY_ARR, Collections.takeWhile(IS_NULL, INT_ARR)); + } + + @Test + public void takeUnless() throws Exception { + assertEquals(EMPTY_ARR, Collections.takeUnless(IS_POSITIVE_INTEGER.not(), INT_ARR_NEGATED)); + assertEquals(singletonList(-1), Collections.takeUnless(IS_POSITIVE_INTEGER, INT_ARR_NEGATED)); + assertEquals(INT_ARR, Collections.takeUnless(Predicate.ALWAYS_FALSE, INT_ARR)); + ArrayList nullArr = new ArrayList<>(); + nullArr.add(null); + assertEquals(nullArr, Collections.takeUnless(NOT_NULL, ARR_WITH_NULL)); + assertEquals(EMPTY_ARR, Collections.takeUnless(IS_NULL, ARR_WITH_NULL)); + } + + @Test + public void foldl() throws Exception { + final Integer result = 10; + assertEquals(result, Collections.foldl(MINUS, INT_ARR, 0)); + + final Function2 accumulateString = new Function2() { + @Override + public String apply2(String arg1, Object arg2) { + return arg1 + Objects.toString(arg2); + } + }; + + final String initString = "Hello, "; + final String resultString = "Hello, 1-4310-20"; + assertEquals(resultString, Collections.foldl(accumulateString, INT_ARR, initString)); + assertEquals(0, Collections.foldl(FAIL_FUNCTION, EMPTY_ARR, 0)); + String expected = "null1null4null"; + assertEquals(expected, Collections.foldl(accumulateString, ARR_WITH_NULL, "")); + } + + @Test + public void foldr() throws Exception { + + final Function2 accumulateString = new Function2() { + @Override + public String apply2(Object arg1, String arg2) { + return Objects.toString(arg1) + arg2; + } + }; + + final String initString = "123"; + final String resultString = "-14-3-1020123"; + assertEquals(resultString, Collections.foldr(accumulateString, INT_ARR_NEGATED, initString)); + + final Integer init = -33; + final Integer result = 11; + assertEquals(result, Collections.foldr(MINUS, INT_ARR, init)); + assertEquals(0, Collections.foldr(FAIL_FUNCTION, EMPTY_ARR, 0)); + } +} diff --git a/src/test/java/ru/spbau/mit/Function1Test.java b/src/test/java/ru/spbau/mit/Function1Test.java new file mode 100644 index 0000000..ba3aa33 --- /dev/null +++ b/src/test/java/ru/spbau/mit/Function1Test.java @@ -0,0 +1,90 @@ +package ru.spbau.mit; + +import org.junit.Test; +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.*; + +public class Function1Test { + private static final Function1> FROM_ARRAY_TO_COLLECTION = + new Function1>() { + @Override + public Collection apply(Integer[] arg) { + return Arrays.asList(arg); + } + }; + + private final Function1 sqr = new Function1() { + @Override + public Integer apply(Integer arg) { + return arg * arg; + } + }; + + private final Function1 id = new Function1() { + @Override + public Object apply(Object arg) { + return arg; + } + }; + + @Test + public void compose() throws Exception { + Function1 g = new Function1() { + @Override + public Double apply(Integer integer) { + return integer.doubleValue(); + } + }; + final Double fiveDouble = 5.0; + final Integer fiveInteger = 5; + assertEquals(fiveDouble, g.apply(fiveInteger)); + + Function1 f = new Function1() { + @Override + public String apply(Number number) { + return number.toString(); + } + }; + + Function1 fg = g.compose(f); + String fiveDoubleString = "5.0"; + assertEquals(fiveDoubleString, fg.apply(fiveInteger)); + + Function1 hash = new Function1() { + @Override + public Integer apply(Object arg) { + return arg.hashCode(); + } + }; + + Integer i = 1; + // must compile. + final Integer someInteger = 123; + assertEquals(0, hash.apply(someInteger) % 1); + + + final int arraySize = 10; + Integer[] integers = new Integer[arraySize]; + FROM_ARRAY_TO_COLLECTION.apply(integers); // compilation OK. + +// Double []doubles = new Double[arraySize]; +// FROM_ARRAY_TO_COLLECTION.apply(doubles); // compilation Failed. + +// Function1 +// Function1 u1 = new Function1() { +// @Override +// Boolean apply(Object o) { +// return o.hashCode() % 2 == 0; +// } +// }; +// + final Integer twentyFiveInteger = 25; + assertEquals(twentyFiveInteger, sqr.compose(id).apply(fiveInteger)); + assertEquals(fiveInteger, id.compose(id).apply(fiveInteger)); + String someString = "5"; + assertEquals(someString, id.compose(id).apply(someString)); + } + +} diff --git a/src/test/java/ru/spbau/mit/Function2Test.java b/src/test/java/ru/spbau/mit/Function2Test.java new file mode 100644 index 0000000..2064b7e --- /dev/null +++ b/src/test/java/ru/spbau/mit/Function2Test.java @@ -0,0 +1,72 @@ +package ru.spbau.mit; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Function2Test { + + private static final Integer FIVE_INTEGER = 5; + private static final Integer FOUR_INTEGER = 4; + private static final Integer THREE_INTEGER = 3; + private static final Integer ONE_INTEGER = 1; + private static final Integer ZERO_INTEGER = 0; + + private final Function1 sqr = new Function1() { + @Override + public Integer apply(Integer arg) { + return arg * arg; + } + }; + + private final Function2 minus = new Function2() { + @Override + public Integer apply2(Integer arg1, Integer arg2) { + return arg1 - arg2; + } + }; + + private final Function1 id = new Function1() { + @Override + public Object apply(Object arg) { + return arg; + } + }; + + @Test + public void curry() throws Exception { + Function1 fiveMinus = new Function1() { + @Override + public Integer apply(Integer arg) { + return FIVE_INTEGER - arg; + } + }; + + assertEquals(ONE_INTEGER, minus.curry().apply(FIVE_INTEGER).apply(FOUR_INTEGER)); + assertEquals(ONE_INTEGER, fiveMinus.apply(FOUR_INTEGER)); +// assertEquals(ONE_INTEGER, minus.curry(fiveDouble).apply(FOUR_INTEGER)); // Compilation error. +// assertEquals(ONE_INTEGER, minus.curry(ONE_INTEGER).apply(fiveDouble)); // Compilation error. + } + + @Test + public void apply2() throws Exception { + assertEquals(FOUR_INTEGER, minus.apply2(FIVE_INTEGER, ONE_INTEGER)); +// assertEquals(ONE_INTEGER, minus.apply2(FIVE_INTEGER, (Double) FOUR_INTEGER)); // Compilation error. + } + + @Test + public void compose() throws Exception { + assertEquals(ONE_INTEGER, minus.compose(id).apply2(FIVE_INTEGER, FOUR_INTEGER)); + assertEquals(ONE_INTEGER, minus.compose(sqr).apply2(FOUR_INTEGER, THREE_INTEGER)); + } + + @Test + public void bind1() throws Exception { + assertEquals(FOUR_INTEGER, minus.bind1(FIVE_INTEGER).apply(ONE_INTEGER)); + } + + @Test + public void bind2() throws Exception { + assertEquals(ZERO_INTEGER, minus.bind2(ONE_INTEGER).apply(ONE_INTEGER)); + } +} diff --git a/src/test/java/ru/spbau/mit/PredicateTest.java b/src/test/java/ru/spbau/mit/PredicateTest.java new file mode 100644 index 0000000..eac7e92 --- /dev/null +++ b/src/test/java/ru/spbau/mit/PredicateTest.java @@ -0,0 +1,101 @@ +package ru.spbau.mit; + +import org.junit.Test; + +import java.util.Objects; + +import static org.junit.Assert.*; + +public class PredicateTest { + private static final Integer FIVE_INTEGER = 5; + private static final Double ZERO_DOUBLE = 0.0; + private static final Double ONE_DOUBLE = 1.0; + private static final Integer ONE_INTEGER = 1; + private static final Integer BIG_INTEGER = 123123124; + private static final Integer FOUR_INTEGER = 4; + + // Does not work. +// private static final Function2 less = new Function2() { +// @Override +// public Boolean apply2(Integer arg1, Integer arg2) { +// return arg1 < arg2; +// } +// }; + + private static final Predicate LESS_THAN_FIVE = new Predicate() { + @Override + public Boolean apply(Integer arg) { + return arg < FIVE_INTEGER; + } + }; + + private static final Predicate IS_POSITIVE_INTEGER = new Predicate() { + @Override + public Boolean apply(Integer arg) { + return arg > 0; + } + }; + + private static final Predicate IS_POSITIVE_DOUBLE = new Predicate() { + @Override + public Boolean apply(Double arg) { + return arg > 0; + } + }; + + private static final Predicate FAIL_INTEGER = new Predicate() { + @Override + public Boolean apply(Integer arg) { + assert false; + return null; + } + }; + + private static final Predicate FAIL_DOUBLE = new Predicate() { + @Override + public Boolean apply(Double arg) { + assert false; + return null; + } + }; + + private static final Predicate EQUAL_TO_ONE = new Predicate() { + @Override + public Boolean apply(Double arg) { + return Objects.equals(ONE_DOUBLE, arg); + } + }; + + @Test + public void and() throws Exception { + assertFalse(EQUAL_TO_ONE.and(FAIL_DOUBLE).apply(ZERO_DOUBLE)); + assertFalse(IS_POSITIVE_INTEGER.and(LESS_THAN_FIVE).apply(BIG_INTEGER)); + assertTrue(LESS_THAN_FIVE.and(IS_POSITIVE_INTEGER).apply(FOUR_INTEGER)); + assertTrue(IS_POSITIVE_DOUBLE.and(EQUAL_TO_ONE).apply(ONE_DOUBLE)); +// assertFalse(Predicate.ALWAYS_FALSE.and(FAIL_DOUBLE).apply(null)); + } + + @Test + public void or() throws Exception { +// assertTrue(EQUAL_TO_ONE.apply(ONE_INTEGER)); // Compilation error. + assertFalse(EQUAL_TO_ONE.or(IS_POSITIVE_DOUBLE).apply(ZERO_DOUBLE)); + assertTrue(LESS_THAN_FIVE.or(IS_POSITIVE_INTEGER).apply(FIVE_INTEGER)); + assertTrue(LESS_THAN_FIVE.or(FAIL_INTEGER).apply(ONE_INTEGER)); + assertTrue(IS_POSITIVE_DOUBLE.or(EQUAL_TO_ONE).apply(ONE_DOUBLE)); +// assertTrue(Predicate.ALWAYS_TRUE.or(FAIL_INTEGER).apply(null)); + } + + @Test + public void not() throws Exception { + assertFalse(LESS_THAN_FIVE.not().apply(FOUR_INTEGER)); + assertTrue(LESS_THAN_FIVE.not().apply(FIVE_INTEGER)); + } + + @Test + public void apply() throws Exception { + assertTrue(LESS_THAN_FIVE.apply(FOUR_INTEGER)); + assertFalse(LESS_THAN_FIVE.apply(FIVE_INTEGER)); + assertFalse(EQUAL_TO_ONE.apply(ZERO_DOUBLE)); + } + +}