Skip to content

Commit 418cfa1

Browse files
authored
Fix DropWhile fusion bug (palatable#120)
1 parent 5e73e1f commit 418cfa1

File tree

6 files changed

+38
-29
lines changed

6 files changed

+38
-29
lines changed
Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,27 @@
11
package com.jnape.palatable.lambda.internal.iteration;
22

33
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.lambda.internal.ImmutableQueue;
45

5-
import java.util.ArrayList;
66
import java.util.Iterator;
7-
import java.util.List;
8-
9-
import static com.jnape.palatable.lambda.functions.builtin.fn2.Any.any;
10-
import static java.util.Collections.singletonList;
117

128
public final class PredicatedDroppingIterable<A> implements Iterable<A> {
13-
private final List<Fn1<? super A, ? extends Boolean>> predicates;
14-
private final Iterable<A> as;
9+
private final ImmutableQueue<Fn1<? super A, ? extends Boolean>> predicates;
10+
private final Iterable<A> as;
1511

1612
public PredicatedDroppingIterable(Fn1<? super A, ? extends Boolean> predicate, Iterable<A> as) {
17-
List<Fn1<? super A, ? extends Boolean>> predicates = new ArrayList<>(singletonList(predicate));
13+
ImmutableQueue<Fn1<? super A, ? extends Boolean>> predicates = ImmutableQueue.singleton(predicate);
1814
while (as instanceof PredicatedDroppingIterable) {
1915
PredicatedDroppingIterable<A> nested = (PredicatedDroppingIterable<A>) as;
2016
as = nested.as;
21-
predicates.addAll(0, nested.predicates);
17+
predicates = nested.predicates.concat(predicates);
2218
}
2319
this.predicates = predicates;
2420
this.as = as;
2521
}
2622

2723
@Override
2824
public Iterator<A> iterator() {
29-
Fn1<? super A, ? extends Boolean> metaPredicate = a -> any(p -> p.apply(a), predicates);
30-
return new PredicatedDroppingIterator<>(metaPredicate, as.iterator());
25+
return new PredicatedDroppingIterator<>(predicates, as.iterator());
3126
}
3227
}
Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package com.jnape.palatable.lambda.internal.iteration;
22

33
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.lambda.internal.ImmutableQueue;
45

56
import java.util.Iterator;
67
import java.util.NoSuchElementException;
78

89
public final class PredicatedDroppingIterator<A> extends ImmutableIterator<A> {
9-
private final Fn1<? super A, ? extends Boolean> predicate;
10-
private final RewindableIterator<A> rewindableIterator;
11-
private boolean finishedDropping;
10+
private final Iterator<Fn1<? super A, ? extends Boolean>> predicates;
11+
private final RewindableIterator<A> rewindableIterator;
1212

13-
public PredicatedDroppingIterator(Fn1<? super A, ? extends Boolean> predicate, Iterator<A> asIterator) {
14-
this.predicate = predicate;
13+
public PredicatedDroppingIterator(ImmutableQueue<Fn1<? super A, ? extends Boolean>> predicates, Iterator<A> asIterator) {
14+
this.predicates = predicates.iterator();
1515
rewindableIterator = new RewindableIterator<>(asIterator);
16-
finishedDropping = false;
1716
}
1817

1918
@Override
@@ -31,11 +30,17 @@ public A next() {
3130
}
3231

3332
private void dropElementsIfNecessary() {
34-
while (rewindableIterator.hasNext() && !finishedDropping) {
35-
if (!predicate.apply(rewindableIterator.next())) {
36-
rewindableIterator.rewind();
37-
finishedDropping = true;
33+
while (predicates.hasNext() && rewindableIterator.hasNext()) {
34+
Fn1<? super A, ? extends Boolean> predicate = predicates.next();
35+
boolean predicateDone = false;
36+
37+
while (rewindableIterator.hasNext() && !predicateDone) {
38+
if (!predicate.apply(rewindableIterator.next())) {
39+
rewindableIterator.rewind();
40+
predicateDone = true;
41+
}
3842
}
43+
3944
}
4045
}
4146
}

src/main/java/com/jnape/palatable/lambda/internal/iteration/RewindableIterator.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ public A retrieve() {
5454
if (cache == null)
5555
throw new NoSuchElementException("Cache is empty.");
5656

57-
A cache = this.cache;
58-
this.cache = null;
59-
return cache;
57+
return this.cache;
6058
}
6159

6260
public boolean isEmpty() {

src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhileTest.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ public void deforestingExecutesPredicatesInOrder() {
5656
innerInvocations.add(x);
5757
return x > 2;
5858
}, asList(1, 2, 3))).forEach(__ -> {});
59-
assertThat(innerInvocations, iterates(1, 2, 3));
60-
assertThat(outerInvocations, iterates(1, 2));
59+
assertThat(innerInvocations, iterates(1));
60+
assertThat(outerInvocations, iterates(1, 2, 3));
61+
}
62+
63+
@Test
64+
public void eachLayerIsAppliedOnce() {
65+
assertThat(dropWhile(i -> i % 2 == 0, dropWhile(i -> i % 2 == 1, asList(1, 2, 3))),
66+
iterates(3));
6167
}
6268
}

src/test/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIteratorTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package com.jnape.palatable.lambda.internal.iteration;
22

33
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.lambda.internal.ImmutableQueue;
45
import org.junit.Before;
56
import org.junit.Test;
67
import org.junit.runner.RunWith;
78
import org.mockito.Mock;
89
import org.mockito.junit.MockitoJUnitRunner;
910

11+
import java.util.Collections;
1012
import java.util.Iterator;
1113
import java.util.NoSuchElementException;
1214

15+
import static java.util.Collections.singletonList;
1316
import static org.hamcrest.core.Is.is;
1417
import static org.junit.Assert.assertThat;
1518
import static testsupport.Mocking.mockIteratorToHaveValues;
@@ -25,7 +28,7 @@ public class PredicatedDroppingIteratorTest {
2528

2629
@Before
2730
public void setUp() {
28-
predicatedDroppingIterator = new PredicatedDroppingIterator<>(EVEN, iterator);
31+
predicatedDroppingIterator = new PredicatedDroppingIterator<>(ImmutableQueue.singleton(EVEN), iterator);
2932
}
3033

3134
@Test

src/test/java/com/jnape/palatable/lambda/internal/iteration/RewindableIteratorTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.NoSuchElementException;
1111

1212
import static org.hamcrest.core.Is.is;
13+
import static org.junit.Assert.assertEquals;
1314
import static org.junit.Assert.assertThat;
1415
import static testsupport.Mocking.mockIteratorToHaveValues;
1516

@@ -51,13 +52,14 @@ public void cannotRewindIfNoValuesIterated() {
5152
rewindableIterator.rewind();
5253
}
5354

54-
@Test(expected = NoSuchElementException.class)
55-
public void cannotRewindTheSameElementTwice() {
55+
@Test
56+
public void canRewindTheSameElementTwice() {
5657
mockIteratorToHaveValues(iterator, 1, 2, 3);
5758
rewindableIterator.next();
5859
rewindableIterator.rewind();
5960
rewindableIterator.next();
6061
rewindableIterator.rewind();
62+
assertEquals(1, rewindableIterator.next());
6163
}
6264

6365
@Test

0 commit comments

Comments
 (0)