Skip to content

Commit 5c2ef5b

Browse files
committed
Adding Cons
1 parent 187ac4e commit 5c2ef5b

4 files changed

Lines changed: 182 additions & 0 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.jnape.palatable.lambda.functions.builtin.fn2;
2+
3+
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.lambda.functions.Fn2;
5+
import com.jnape.palatable.lambda.iterators.ConsingIterator;
6+
7+
/**
8+
* Prepend an element to an <code>Iterable</code>.
9+
*
10+
* @param <A> the Iterable element type
11+
*/
12+
public final class Cons<A> implements Fn2<A, Iterable<A>, Iterable<A>> {
13+
14+
private static final Cons INSTANCE = new Cons();
15+
16+
private Cons() {
17+
}
18+
19+
@Override
20+
public Iterable<A> apply(A a, Iterable<A> as) {
21+
return () -> new ConsingIterator<>(a, as);
22+
}
23+
24+
@SuppressWarnings("unchecked")
25+
public static <A> Cons<A> cons() {
26+
return (Cons<A>) INSTANCE;
27+
}
28+
29+
public static <A> Fn1<Iterable<A>, Iterable<A>> cons(A a) {
30+
return Cons.<A>cons().apply(a);
31+
}
32+
33+
public static <A> Iterable<A> cons(A a, Iterable<A> as) {
34+
return cons(a).apply(as);
35+
}
36+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.jnape.palatable.lambda.iterators;
2+
3+
import java.util.Iterator;
4+
import java.util.NoSuchElementException;
5+
import java.util.function.Supplier;
6+
7+
public final class ConsingIterator<A> implements Iterator<A> {
8+
9+
private final A head;
10+
private final Supplier<Iterator<A>> asSupplier;
11+
private Iterator<A> asIterator;
12+
private boolean iteratedHead;
13+
14+
public ConsingIterator(A head, Iterable<A> as) {
15+
this.head = head;
16+
this.asSupplier = as::iterator;
17+
iteratedHead = false;
18+
}
19+
20+
@Override
21+
public boolean hasNext() {
22+
if (!iteratedHead)
23+
return true;
24+
25+
if (asIterator == null)
26+
asIterator = asSupplier.get();
27+
28+
return asIterator.hasNext();
29+
}
30+
31+
@Override
32+
public A next() {
33+
if (!hasNext())
34+
throw new NoSuchElementException();
35+
36+
if (!iteratedHead) {
37+
iteratedHead = true;
38+
return head;
39+
}
40+
41+
while (asIterator instanceof ConsingIterator && ((ConsingIterator) asIterator).iteratedHead) {
42+
ConsingIterator<A> cons = (ConsingIterator<A>) asIterator;
43+
if (cons.iteratedHead && cons.asIterator != null)
44+
asIterator = cons.asIterator;
45+
}
46+
47+
return asIterator.next();
48+
}
49+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.jnape.palatable.lambda.functions.builtin.fn2;
2+
3+
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.traitor.annotations.TestTraits;
5+
import com.jnape.palatable.traitor.runners.Traits;
6+
import org.junit.Test;
7+
import org.junit.runner.RunWith;
8+
import testsupport.traits.EmptyIterableSupport;
9+
import testsupport.traits.FiniteIteration;
10+
import testsupport.traits.ImmutableIteration;
11+
import testsupport.traits.Laziness;
12+
13+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons;
14+
import static java.util.Arrays.asList;
15+
import static java.util.Collections.emptyList;
16+
import static org.junit.Assert.assertThat;
17+
import static testsupport.matchers.IterableMatcher.iterates;
18+
19+
@RunWith(Traits.class)
20+
public class ConsTest {
21+
22+
@TestTraits({Laziness.class, ImmutableIteration.class, FiniteIteration.class, EmptyIterableSupport.class})
23+
public Fn1<Iterable<Object>, Iterable<Object>> createTestSubject() {
24+
return cons(0);
25+
}
26+
27+
@Test
28+
public void consingElementToHeadOfIterable() {
29+
assertThat(cons(0, asList(1, 2, 3)), iterates(0, 1, 2, 3));
30+
}
31+
32+
@Test
33+
public void consingToEmptyIterable() {
34+
assertThat(cons("foo", emptyList()), iterates("foo"));
35+
}
36+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.jnape.palatable.lambda.iterators;
2+
3+
import org.junit.Before;
4+
import org.junit.Test;
5+
6+
import java.util.Collections;
7+
8+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Drop.drop;
9+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Iterate.iterate;
10+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take;
11+
import static com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight.foldRight;
12+
import static java.util.Arrays.asList;
13+
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.assertFalse;
15+
import static org.junit.Assert.assertTrue;
16+
17+
public class ConsingIteratorTest {
18+
19+
private ConsingIterator<Integer> consingIterator;
20+
21+
@Before
22+
public void setUp() {
23+
consingIterator = new ConsingIterator<>(0, asList(1, 2, 3));
24+
}
25+
26+
@Test
27+
public void hasNextIfHeadNotYetIterated() {
28+
assertTrue(consingIterator.hasNext());
29+
}
30+
31+
@Test
32+
public void nextIsHeadIfNotYetIterated() {
33+
assertEquals((Integer) 0, consingIterator.next());
34+
}
35+
36+
@Test
37+
public void hasNextIfMoreElementsAfterHead() {
38+
consingIterator.next();
39+
assertTrue(consingIterator.hasNext());
40+
}
41+
42+
@Test
43+
public void doesNotHaveNextIfNoElementsLeft() {
44+
consingIterator.next();
45+
consingIterator.next();
46+
consingIterator.next();
47+
consingIterator.next();
48+
assertFalse(consingIterator.hasNext());
49+
}
50+
51+
@Test
52+
public void stackSafety() {
53+
Integer stackBlowingNumber = 1000000;
54+
Iterable<Integer> ints = foldRight((x, acc) -> () -> new ConsingIterator<>(x, acc),
55+
(Iterable<Integer>) Collections.<Integer>emptyList(),
56+
take(stackBlowingNumber, iterate(x -> x + 1, 1)));
57+
58+
assertEquals(stackBlowingNumber,
59+
take(1, drop(stackBlowingNumber - 1, ints)).iterator().next());
60+
}
61+
}

0 commit comments

Comments
 (0)