diff --git a/Function/.gitignore b/Function/.gitignore new file mode 100644 index 0000000..345e61a --- /dev/null +++ b/Function/.gitignore @@ -0,0 +1,49 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/Function/.idea/.name b/Function/.idea/.name new file mode 100644 index 0000000..1ff17c5 --- /dev/null +++ b/Function/.idea/.name @@ -0,0 +1 @@ +Function \ No newline at end of file diff --git a/Function/.idea/compiler.xml b/Function/.idea/compiler.xml new file mode 100644 index 0000000..8f81c05 --- /dev/null +++ b/Function/.idea/compiler.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Function/.idea/misc.xml b/Function/.idea/misc.xml new file mode 100644 index 0000000..c092288 --- /dev/null +++ b/Function/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/Function/.idea/modules.xml b/Function/.idea/modules.xml new file mode 100644 index 0000000..8962661 --- /dev/null +++ b/Function/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Function/Function.iml b/Function/Function.iml new file mode 100644 index 0000000..e6ccb8a --- /dev/null +++ b/Function/Function.iml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Function/pom.xml b/Function/pom.xml new file mode 100644 index 0000000..b49ba25 --- /dev/null +++ b/Function/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + ru.spbau.mit.kazakov + Function + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.8 + 1.8 + + + + + + + + junit + junit + 4.8 + + + org.jetbrains + annotations + 13.0 + + + com.google.guava + guava + 23.2-jre + + + + + UTF-8 + UTF-8 + + + \ No newline at end of file diff --git a/Function/src/main/java/ru/spbau/mit/kazakov/Function/Collections.java b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Collections.java new file mode 100644 index 0000000..f4ecfc1 --- /dev/null +++ b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Collections.java @@ -0,0 +1,126 @@ +package ru.spbau.mit.kazakov.Function; + + +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * Class providing methods for working with collections using functions. + */ +public class Collections { + /** + * Applies specified function to specified container's elements and adds mapped elements in new list. + * + * @param function specified function + * @param elements specified container + * @param type of elements in specified container + * @param type of elements in result list + * @return result list + */ + static public List map(@NotNull final Function1 function, @NotNull final Iterable elements) { + List resultList = new ArrayList<>(); + + for (T element : elements) { + resultList.add(function.apply(element)); + } + + return resultList; + } + + /** + * Creates new list composed of elements from specified container that satisfy specified predicate. + * + * @param predicate specified predicate + * @param elements specified container + * @param type of elements in specified container + * @return result list + */ + static public List filter(@NotNull final Predicate predicate, @NotNull final Iterable elements) { + List resultList = new ArrayList<>(); + + for (T element : elements) { + if (predicate.apply(element)) { + resultList.add(element); + } + } + + return resultList; + } + + /** + * Creates new list by adding elements from specified container while they satisfy specified predicate. + * + * @param predicate specified predicate + * @param elements specified container + * @param type of elements in specified container + * @return result list + */ + static public List takeWhile(@NotNull final Predicate predicate, @NotNull final Iterable elements) { + List resultList = new ArrayList<>(); + + for (T element : elements) { + if (!predicate.apply(element)) { + break; + } + resultList.add(element); + } + + return resultList; + } + + /** + * Creates new list by adding elements from specified container unless they satisfy specified predicate. + * + * @param predicate specified predicate + * @param elements specified container + * @param type of elements in specified container + * @return result list + */ + static public List takeUnless(@NotNull final Predicate predicate, @NotNull final Iterable elements) { + return takeWhile(predicate.not(), elements); + } + + /** + * Left-associative fold of specified collection. Reduces the list using specified function from left to right. + * + * @param function specified function + * @param startValue first value for function's first element + * @param elements specified collection + * @param type of elements in specified collection + * @param specified function's codomain + * @return result value + */ + static public U foldl(@NotNull final Function2 function, + U startValue, @NotNull final Collection elements) { + U resultValue = startValue; + + for (T element : elements) { + resultValue = function.apply(resultValue, element); + } + + return resultValue; + } + + /** + * Right-associative fold of specified collection. Reduces the list using specified function from right to left. + * + * @param function specified function + * @param startValue first value for function's second element + * @param elements specified collection + * @param type of elements in specified collection + * @param specified function's codomain + * @return result value + */ + static public U foldr(@NotNull final Function2 function, + U startValue, @NotNull final Collection elements) { + U resultValue = startValue; + + for (T element : Lists.reverse(new ArrayList<>(elements))) { + resultValue = function.apply(element, resultValue); + } + + return resultValue; + } +} diff --git a/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function1.java b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function1.java new file mode 100644 index 0000000..9bbfe30 --- /dev/null +++ b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function1.java @@ -0,0 +1,32 @@ +package ru.spbau.mit.kazakov.Function; + + +import org.jetbrains.annotations.NotNull; + +/** + * The interface for representing a function with one argument. + * + * @param domain's type + * @param codomain's type + */ +@FunctionalInterface +public interface Function1 { + /** + * Applies function to specified argument. + * + * @param argument for applying to + * @return mapped value + */ + U apply(T argument); + + /** + * Creates composition of functions. + * + * @param function for composition + * @param specified function's codomain's type + * @return result of function composition + */ + default Function1 compose(@NotNull final Function1 function) { + return argument -> function.apply(apply(argument)); + } +} diff --git a/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function2.java b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function2.java new file mode 100644 index 0000000..3532a7b --- /dev/null +++ b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function2.java @@ -0,0 +1,62 @@ +package ru.spbau.mit.kazakov.Function; + + +import org.jetbrains.annotations.NotNull; + +/** + * The interface for representing a function with two arguments. + * + * @param first argument's type + * @param second argument's type + * @param codomain's type + */ +@FunctionalInterface +public interface Function2 { + /** + * Applies function to specified argument. + * + * @param firstArgument for applying to + * @param secondArgument for applying to + * @return mapped value + */ + K apply(T firstArgument, U secondArgument); + + /** + * Creates composition of functions. + * + * @param specified function's codomain's type + * @return result of function composition + */ + default Function2 compose(@NotNull final Function1 function) { + return (firstArgument, secondArgument) -> function.apply(apply(firstArgument, secondArgument)); + } + + /** + * Binds first argument to specified value. + * + * @param firstArgument specified value + * @return function of one argument + */ + default Function1 bind1(T firstArgument) { + return secondArgument -> apply(firstArgument, secondArgument); + } + + /** + * Binds second argument to specified value. + * + * @param secondArgument specified value + * @return function of one argument + */ + default Function1 bind2(U secondArgument) { + return firstArgument -> apply(firstArgument, secondArgument); + } + + /** + * Converts current function with two arguments into two functions with one argument. + * + * @return new function with one argument + */ + default Function1> curry() { + return this::bind1; + } +} \ No newline at end of file diff --git a/Function/src/main/java/ru/spbau/mit/kazakov/Function/Predicate.java b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Predicate.java new file mode 100644 index 0000000..2a4c177 --- /dev/null +++ b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Predicate.java @@ -0,0 +1,53 @@ +package ru.spbau.mit.kazakov.Function; + + +import org.jetbrains.annotations.NotNull; + +/** + * The interface for representing a predicate with one argument. + * + * @param domain's type + */ +@FunctionalInterface +public interface Predicate extends Function1 { + /** + * Predicate always returning true. + */ + Predicate ALWAYS_TRUE = argument -> true; + + /** + * Predicate always returning false. + */ + Predicate ALWAYS_FALSE = ALWAYS_TRUE.not(); + + /** + * Makes new predicate with two argument composed of logical OR of current and specified predicates. + * + * @param predicate a specified predicate + * @param specified predicate's argument's type + * @return new predicate with two variables + */ + default Function2 or(@NotNull final Predicate predicate) { + return (firstArgument, secondArgument) -> Predicate.this.apply(firstArgument) || predicate.apply(secondArgument); + } + + /** + * Makes new predicate with two argument composed of logical AND of current and specified predicates. + * + * @param predicate a specified predicate + * @param specified predicate's argument's type + * @return new predicate with two variables + */ + default Function2 and(@NotNull final Predicate predicate) { + return (firstArgument, secondArgument) -> Predicate.this.apply(firstArgument) && predicate.apply(secondArgument); + } + + /** + * Makes new predicate composed of logical NOT of current predicate. + * + * @return new predicate + */ + default Predicate not() { + return argument -> !apply(argument); + } +} diff --git a/Function/src/test/java/ru/spbau/mit/kazakov/Function/CollectionsTest.java b/Function/src/test/java/ru/spbau/mit/kazakov/Function/CollectionsTest.java new file mode 100644 index 0000000..8a9a311 --- /dev/null +++ b/Function/src/test/java/ru/spbau/mit/kazakov/Function/CollectionsTest.java @@ -0,0 +1,167 @@ +package ru.spbau.mit.kazakov.Function; + + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + + +import static java.util.Arrays.asList; +import static org.junit.Assert.*; + +public class CollectionsTest { + @Test + public void testMapEmpty() { + Function1 function = argument -> argument * argument; + List list = new ArrayList<>(); + List expected = new ArrayList<>(); + + assertEquals(expected, Collections.map(function, list)); + } + + @Test + public void testMap() { + Function1 function = argument -> argument * argument; + List list = asList(-2, -1, 0, 4, 10); + List expected = asList(4, 1, 0, 16, 100); + + assertEquals(expected, Collections.map(function, list)); + } + + @Test + public void testFilterEmptyInput() { + Predicate function = argument -> Math.abs(argument) > 10; + List list = new ArrayList<>(); + List expected = new ArrayList<>(); + + assertEquals(expected, Collections.filter(function, list)); + } + + @Test + public void testFilterEmptyOutput() { + Predicate function = argument -> Math.abs(argument) == 1; + List list = asList(10, 3, -10, -99, 0, -23, 1001); + List expected = new ArrayList<>(); + + assertEquals(expected, Collections.filter(function, list)); + } + + @Test + public void testFilterAllListOutput() { + Predicate function = argument -> Math.abs(argument) != 1010; + List list = asList(10, 3, -10, -99, 0, -23, 1001); + List expected = asList(10, 3, -10, -99, 0, -23, 1001); + + assertEquals(expected, Collections.filter(function, list)); + } + + @Test + public void testFilter() { + Predicate function = argument -> Math.abs(argument) > 10; + List list = asList(10, 3, -10, -99, 0, -23, 1001); + List expected = asList(-99, -23, 1001); + + assertEquals(expected, Collections.filter(function, list)); + } + + @Test + public void testTakeWhileEmptyInput() { + Predicate function = argument -> Math.abs(argument) == 77; + List list = new ArrayList<>(); + List expected = new ArrayList<>(); + + assertEquals(expected, Collections.takeWhile(function, list)); + } + + @Test + public void testTakeWhileEmptyOutput() { + Predicate function = argument -> Math.abs(argument) == 77; + List list = asList(1, -77, 77, 77, 0, 77, -77, 198); + List expected = new ArrayList<>(); + + assertEquals(expected, Collections.takeWhile(function, list)); + } + + @Test + public void testTakeWhileAllListOutput() { + Predicate function = argument -> Math.abs(argument) == 77; + List list = asList(-77, 77, 77, 77, -77); + List expected = asList(-77, 77, 77, 77, -77); + + assertEquals(expected, Collections.takeWhile(function, list)); + } + + @Test + public void testTakeWhile() { + Predicate function = argument -> Math.abs(argument) == 77; + List list = asList(-77, 77, 77, 0, 77, -77, 198); + List expected = asList(-77, 77, 77); + + assertEquals(expected, Collections.takeWhile(function, list)); + } + + @Test + public void testTakeUnlessEmptyInput() { + Predicate function = argument -> Math.abs(argument) == 77; + List list = new ArrayList<>(); + List expected = new ArrayList<>(); + + assertEquals(expected, Collections.takeUnless(function, list)); + } + + @Test + public void testTakeUnlessEmptyOutput() { + Predicate function = argument -> Math.abs(argument) == 77; + List list = asList(-77, 77, 77, 0, 77, -77, 198); + List expected = new ArrayList<>(); + + assertEquals(expected, Collections.takeUnless(function, list)); + } + + @Test + public void testTakeUnlessAllListOutput() { + Predicate function = argument -> Math.abs(argument) == 77; + List list = asList(0, 1, -99, 121, 89898, 32423); + List expected = asList(0, 1, -99, 121, 89898, 32423); + + assertEquals(expected, Collections.takeUnless(function, list)); + } + + @Test + public void testTakeUnless() { + Predicate function = argument -> Math.abs(argument) == 77; + List list = asList(0, 1, -99, 121, -77, 89898, 77, 32423); + List expected = asList(0, 1, -99, 121); + + assertEquals(expected, Collections.takeUnless(function, list)); + } + + @Test + public void testFoldlEmpty() { + Function2 function = (firstArgument, secondArgument) -> secondArgument / firstArgument; + List list = new ArrayList<>(); + assertEquals(1, (int) Collections.foldl(function, 1, list)); + } + + @Test + public void testFoldl() { + Function2 function = (firstArgument, secondArgument) -> secondArgument / firstArgument; + List list = asList(10, 20, 100); + assertEquals(50, (int) Collections.foldl(function, 1, list)); + } + + @Test + public void testFoldrEmpty() { + Function2 function = (firstArgument, secondArgument) -> firstArgument / secondArgument; + List list = new ArrayList<>(); + assertEquals(1, (int) Collections.foldr(function, 1, list)); + } + + @Test + public void testFoldr() { + Function2 function = (firstArgument, secondArgument) -> firstArgument / secondArgument; + List list = asList(100, 20, 10); + assertEquals(50, (int) Collections.foldr(function, 1, list)); + } +} \ No newline at end of file diff --git a/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function1Test.java b/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function1Test.java new file mode 100644 index 0000000..3862672 --- /dev/null +++ b/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function1Test.java @@ -0,0 +1,32 @@ +package ru.spbau.mit.kazakov.Function; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Function1Test { + @Test + public void testConstructor() { + new Function1() { + @Override + public Double apply(Integer argument) { + return argument / 2.0; + } + }; + } + + @Test + public void testApply() { + Function1 function = argument -> argument * 2; + assertEquals(new Integer(10), function.apply(5)); + } + + @Test + public void compose() { + Function1 someFunction = argument -> (long) (argument / 5); + Function1 anotherFunction = Object::toString; + Function1 composition = someFunction.compose(anotherFunction); + assertEquals("6", composition.apply(30)); + } + +} \ No newline at end of file diff --git a/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function2Test.java b/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function2Test.java new file mode 100644 index 0000000..a3d9e30 --- /dev/null +++ b/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function2Test.java @@ -0,0 +1,63 @@ +package ru.spbau.mit.kazakov.Function; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Function2Test { + @Test + public void testConstructor() { + new Function2() { + @Override + public Double apply(Long firstArgument, Integer secondArgument) { + return (firstArgument * secondArgument) / 5.0; + } + }; + } + + @Test + public void testApply() { + Function2 function = + (firstArgument, secondArgument) -> firstArgument.toString() + secondArgument.toString(); + assertEquals("8j", function.apply((byte) 8, 'j')); + } + + @Test + public void testCompose() { + Function2 someFunction = Integer::equals; + Function1 anotherFunction = argument -> { + if (argument) { + return (byte) 1; + } else { + return (byte) 0; + } + }; + Function2 composition = someFunction.compose(anotherFunction); + assertEquals(1, (byte) composition.apply(979, 979)); + } + + @Test + public void bind1() { + Function2 someFunction = + (firstArgument, secondArgument) -> (int) Math.pow(firstArgument, secondArgument); + Function1 anotherFunction = someFunction.bind1(2); + assertEquals(32, (int) anotherFunction.apply(5)); + } + + @Test + public void bind2() { + Function2 someFunction = + (firstArgument, secondArgument) -> (int) Math.pow(firstArgument, secondArgument); + Function1 anotherFunction = someFunction.bind2(3); + assertEquals(64, (int) anotherFunction.apply(4)); + } + + @Test + public void curry() { + Function2 function = + (firstArgument, secondArgument) -> Math.abs(firstArgument) + Math.abs(secondArgument); + Function1> curried = function.curry(); + assertEquals(46, (int) curried.apply(-44).apply(2)); + } + +} \ No newline at end of file diff --git a/Function/src/test/java/ru/spbau/mit/kazakov/Function/PredicateTest.java b/Function/src/test/java/ru/spbau/mit/kazakov/Function/PredicateTest.java new file mode 100644 index 0000000..e354a30 --- /dev/null +++ b/Function/src/test/java/ru/spbau/mit/kazakov/Function/PredicateTest.java @@ -0,0 +1,61 @@ +package ru.spbau.mit.kazakov.Function; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class PredicateTest { + @Test + public void testConstructor() { + new Predicate(){ + @Override + public Boolean apply(Double argument) { + return argument > 7; + } + }; + } + + @Test + public void testALWAYS_TRUE() { + assertTrue((boolean) Predicate.ALWAYS_TRUE.apply(new Object())); + } + + @Test + public void testALWAYS_FALSE() { + assertFalse((boolean) Predicate.ALWAYS_FALSE.apply(new Object())); + } + + @Test + public void testOr() { + Predicate somePredicate = argument -> argument; + Predicate anotherPredicate = argument -> argument >= 111; + Function2 or = somePredicate.or(anotherPredicate); + + assertFalse(or.apply(false, 1)); + assertTrue(or.apply(true, 110)); + assertTrue(or.apply(false, 111)); + assertTrue(or.apply(true, 1000)); + } + + @Test + public void testAnd() { + Predicate somePredicate = argument -> argument; + Predicate anotherPredicate = argument -> argument >= 111; + Function2 and = somePredicate.and(anotherPredicate); + + assertFalse(and.apply(false, 1)); + assertFalse(and.apply(true, 110)); + assertFalse(and.apply(false, 111)); + assertTrue(and.apply(true, 1000)); + } + + @Test + public void testNot() { + Predicate predicate = argument -> argument.equals("Math is painful"); + Predicate not = predicate.not(); + + assertTrue(not.apply("Math is wonderful")); + assertFalse(not.apply("Math is painful")); + } + +} \ No newline at end of file