commit 8783d82a0b23e924b0afe0b8034619a5babeef1c
Author: Oshgnacknak <osh@oshgnacknak.de>
Date: Thu, 14 Jan 2021 23:52:28 +0100
Java Cons
Diffstat:
6 files changed, 278 insertions(+), 0 deletions(-)
diff --git a/src/Cons.java b/src/Cons.java
@@ -0,0 +1,30 @@
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public interface Cons<V> {
+
+ V first();
+
+ Cons<V> rest();
+
+ <W> Cons<W> map(Function<V, W> mapper);
+
+ Cons<V> filter(Predicate<V> predicate);
+
+ <W> W foldl(BiFunction<V, W, W> f, W init);
+
+ <W> W foldr(BiFunction<V, W, W> f, W init);
+
+ @SafeVarargs
+ static <V> Cons<V> of(V... arr) {
+ return fromArray(0, arr);
+ }
+
+ private static <V> Cons<V> fromArray(int n, V[] arr) {
+ if (n >= arr.length) {
+ return new Empty<>();
+ }
+ return new NonEmpty<>(arr[n], fromArray(n+1, arr));
+ }
+}+
\ No newline at end of file
diff --git a/src/ConsTest.java b/src/ConsTest.java
@@ -0,0 +1,20 @@
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ConsTest {
+
+ @Test
+ void testOf() {
+ var cons = Cons.of(1, 2, 3, 4);
+ assertEquals(3, cons.rest().rest().first());
+ assertEquals("(cons 1 (cons 2 (cons 3 (cons 4 empty))))", cons.toString());
+ }
+
+ @Test
+ void testOfNone() {
+ var cons = Cons.of();
+ assertEquals(new Empty<>(), cons);
+ assertEquals("empty", cons.toString());
+ }
+}+
\ No newline at end of file
diff --git a/src/Empty.java b/src/Empty.java
@@ -0,0 +1,46 @@
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class Empty<V> implements Cons<V> {
+
+ @Override
+ public V first() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Cons<V> rest() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <W> Cons<W> map(Function<V, W> mapper) {
+ return new Empty<>();
+ }
+
+ @Override
+ public Cons<V> filter(Predicate<V> predicate) {
+ return new Empty<>();
+ }
+
+ @Override
+ public <W> W foldl(BiFunction<V, W, W> f, W init) {
+ return init;
+ }
+
+ @Override
+ public <W> W foldr(BiFunction<V, W, W> f, W init) {
+ return init;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Empty;
+ }
+
+ @Override
+ public String toString() {
+ return "empty";
+ }
+}+
\ No newline at end of file
diff --git a/src/EmptyTest.java b/src/EmptyTest.java
@@ -0,0 +1,43 @@
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class EmptyTest {
+
+ Cons<Integer> empty = new Empty<>();
+
+ @Test
+ void testFirst() {
+ assertThrows(UnsupportedOperationException.class, () -> empty.first());
+ }
+
+ @Test
+ void testRest() {
+ assertThrows(UnsupportedOperationException.class, () -> empty.rest());
+ }
+
+ @Test
+ void testMap() {
+ assertEquals(new Empty<>(), empty.map(n -> n+1));
+ }
+
+ @Test
+ void testFilter() {
+ assertEquals(new Empty<>(), empty.filter(n -> n>4));
+ }
+
+ @Test
+ void testFoldl() {
+ assertEquals(4, empty.foldl(Integer::sum, 4));
+ }
+
+ @Test
+ void testFoldr() {
+ assertEquals(4, empty.foldr(Integer::sum, 4));
+ }
+
+ @Test
+ void testToString() {
+ assertEquals("empty", empty.toString());
+ }
+}+
\ No newline at end of file
diff --git a/src/NonEmpty.java b/src/NonEmpty.java
@@ -0,0 +1,68 @@
+import java.util.Objects;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class NonEmpty<V> implements Cons<V> {
+
+ private final V first;
+ private final Cons<V> rest;
+
+ public NonEmpty(V first, Cons<V> rest) {
+ this.first = first;
+ this.rest = rest;
+ }
+
+ @Override
+ public V first() {
+ return first;
+ }
+
+ @Override
+ public Cons<V> rest() {
+ return rest;
+ }
+
+ @Override
+ public <W> Cons<W> map(Function<V, W> mapper) {
+ return new NonEmpty<>(mapper.apply(first), rest.map(mapper));
+ }
+
+ @Override
+ public Cons<V> filter(Predicate<V> predicate) {
+ var newRest = rest.filter(predicate);
+ return predicate.test(first) ?
+ new NonEmpty<>(first, newRest.filter(predicate)) :
+ newRest;
+ }
+
+ @Override
+ public <W> W foldl(BiFunction<V, W, W> f, W init) {
+ var newInit = f.apply(first, init);
+ return rest.foldl(f, newInit);
+ }
+
+ @Override
+ public <W> W foldr(BiFunction<V, W, W> f, W init) {
+ var value = rest.foldr(f, init);
+ return f.apply(first, value);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Cons)) return false;
+ Cons<?> cons = (Cons<?>) o;
+ return Objects.equals(first, cons.first()) && Objects.equals(rest, cons.rest());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(first, rest);
+ }
+
+ @Override
+ public String toString() {
+ return "(cons " + first + " " + rest + ")";
+ }
+}
diff --git a/src/NonEmptyTest.java b/src/NonEmptyTest.java
@@ -0,0 +1,66 @@
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class NonEmptyTest {
+
+ Cons<Integer> cons = new NonEmpty<>(1, new NonEmpty<>(2, new NonEmpty<>(3, new Empty<>())));
+ Cons<String> stringCons = new NonEmpty<>("Hallo", new NonEmpty<>("Welt", new Empty<>()));
+
+ @Test
+ void first() {
+ assertEquals(1, cons.first());
+ }
+
+ @Test
+ void rest() {
+ var expected = new NonEmpty<>(2, new NonEmpty<>(3, new Empty<>()));
+ assertEquals(expected, cons.rest());
+
+ var sExpected = new NonEmpty<>("Welt", new Empty<>());
+ assertEquals(sExpected, stringCons.rest());
+
+ assertThrows(UnsupportedOperationException.class, () ->
+ cons.rest().rest().rest().rest().rest());
+ }
+
+ @Test
+ void map() {
+ var expected = new NonEmpty<>(1, new NonEmpty<>(4, new NonEmpty<>(9, new Empty<>())));
+ assertEquals(expected, cons.map(n -> n*n));
+
+ expected = new NonEmpty<>(5, new NonEmpty<>(4, new Empty<>()));
+ assertEquals(expected, stringCons.map(String::length));
+ }
+
+ @Test
+ void filter() {
+ var expected = new NonEmpty<>(1, new NonEmpty<>(3, new Empty<>()));
+ assertEquals(expected, cons.filter(n -> n % 2 == 1));
+ assertEquals(new Empty<>(), cons.filter(n -> n > 10));
+
+ var sExpected = new NonEmpty<>("Hallo", new Empty<>());
+ assertEquals(sExpected, stringCons.filter(s -> s.startsWith("Ha")));
+ }
+
+ @Test
+ void foldl() {
+ assertEquals(6, cons.foldl(Integer::sum, 0));
+ assertEquals("WeltHallo", stringCons.foldl(String::concat, ""));
+ }
+
+ @Test
+ void foldr() {
+ assertEquals(6, cons.foldr(Integer::sum, 0));
+ assertEquals("HalloWelt", stringCons.foldr(String::concat, ""));
+ }
+
+ @Test
+ void testToString() {
+ var expected = "(cons 1 (cons 2 (cons 3 empty)))";
+ assertEquals(expected, cons.toString());
+
+ expected = "(cons Hallo (cons empty))";
+ assertEquals(expected, stringCons.toString());
+ }
+}+
\ No newline at end of file