Skip to content

Commit 7f14232

Browse files
committed
[#3] External functional loops
1 parent f1c2ba8 commit 7f14232

File tree

2 files changed

+422
-0
lines changed

2 files changed

+422
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package org.jooq.lambda;
2+
3+
import org.jooq.lambda.tuple.Tuple4;
4+
5+
import java.util.Arrays;
6+
import java.util.LongSummaryStatistics;
7+
import java.util.function.IntConsumer;
8+
import java.util.function.IntUnaryOperator;
9+
import java.util.function.LongConsumer;
10+
import java.util.function.LongPredicate;
11+
import java.util.function.Predicate;
12+
13+
/** External lambda "for"-loops. */
14+
public class Loops {
15+
private Loops () {}//static utils
16+
17+
18+
/**
19+
Simple loop (numberOfRepetitions .. 0].
20+
<pre>{@code
21+
while (numberOfRepetitions-- > 0)
22+
body.run();
23+
}</pre>
24+
*/
25+
public static void loop (long numberOfRepetitions, Runnable body) {
26+
while (numberOfRepetitions-- > 0) {
27+
body.run();
28+
}
29+
}
30+
31+
/**
32+
Simple while-loop (numberOfRepetitions .. 0].
33+
<pre>{@code}
34+
while (numberOfRepetitions-- > 0)
35+
body.accept(numberOfRepetitions);
36+
}</pre>
37+
*/
38+
public static void loop (long numberOfRepetitions, LongConsumer body) {
39+
while (numberOfRepetitions-- > 0) {
40+
body.accept(numberOfRepetitions);
41+
}
42+
}
43+
44+
/**
45+
Simple for-loop [0 .. numberOfRepetitions).
46+
<pre>{@code}
47+
for (int i=0; i&lt;max; i++)
48+
body.accept(i);
49+
}</pre>
50+
*/
51+
public static void forLoop (int maxExclusive, IntConsumer body) {
52+
for (int i=0; i<maxExclusive; i++) {
53+
body.accept(i);
54+
}
55+
}
56+
57+
/**
58+
Simple loop (numberOfRepetitions .. 0] with extra condition.
59+
<pre>{@code
60+
while (numberOfRepetitions-- > 0 && body.test(numberOfRepetitions)) {}
61+
}</pre>
62+
@param body do-while Predicate: loops while it is true and numberOfRepetitions &gt; 0
63+
*/
64+
public static void loopWhile (long numberOfRepetitions, LongPredicate body) {
65+
//noinspection StatementWithEmptyBody
66+
while (numberOfRepetitions-- > 0 && body.test(numberOfRepetitions)) {
67+
// do while
68+
}
69+
}
70+
71+
// Ad-Hoc JMH (not for serious benchmarks)
72+
73+
static final long NANO = 1000_000_000L;
74+
75+
/** Similar to {@link #loop(long, Runnable)}, but with metrics */
76+
public static Tuple4<Long, LongSummaryStatistics, Exception, String> loopMeasured (long numberOfRepetitions, Runnable body) {
77+
LongSummaryStatistics st = new LongSummaryStatistics();
78+
Exception ex = null;
79+
long n = System.nanoTime();
80+
81+
while (numberOfRepetitions-- > 0) {
82+
long t = System.nanoTime();
83+
try {
84+
body.run();
85+
} catch (Exception showStopper) {
86+
ex = showStopper;
87+
break;
88+
} finally {
89+
st.accept(System.nanoTime() - t);
90+
}
91+
}
92+
n = System.nanoTime() - n;
93+
94+
StringBuilder sb = new StringBuilder(200);// ≤ ≥
95+
sb.append("LOOP: ").append(st.getCount())
96+
.append(" nanos= ").append(st.getSum()).append(" ≤ ").append(n)
97+
.append(" ~ op/sec= ");
98+
if (st.getSum() > 0) {
99+
sb.append((st.getCount() * NANO) / st.getSum()).append(" ≥ ");
100+
}
101+
sb.append((st.getCount() * NANO) / Math.max(n, 1));
102+
if (st.getCount()>0) {
103+
sb.append(" ~ nano/op= ").append(st.getSum() / st.getCount()).append(" ≤ ").append(n / st.getCount());
104+
}
105+
sb.append(" min<avg<max= ").append(st.getMin())
106+
.append(" < ").append(st.getAverage())
107+
.append(" < ").append(st.getMax());
108+
return new Tuple4<>(n, st, ex, sb.toString());
109+
}
110+
111+
/** Similar to {@link #loop(long, Runnable)}, but with metrics and warm-up */
112+
public static Tuple4<Long, LongSummaryStatistics, Exception, String> loopMeasuredWarm (long numberOfRepetitions, Runnable body) {
113+
long warmCnt = Math.min(50_000, numberOfRepetitions/10+10);
114+
115+
loop(warmCnt, body);
116+
117+
return loopMeasured(numberOfRepetitions, body);
118+
}
119+
120+
121+
public static class Incrementer {
122+
protected final int[] indexes;
123+
private final IntUnaryOperator maxForIndex;
124+
125+
/** All indexes have the same max and rotate in range [0..maxExclusive)
126+
@param degree size of index vector = how many nested for-loops
127+
@param maxExclusive number of repetitions: every index variable rotates in range [0..maxExclusive)
128+
*/
129+
public Incrementer (int degree, final int maxExclusive) {
130+
assert degree > 0 : "degree must be > 0, but "+degree;
131+
assert maxExclusive > 0 : "maxExclusive must be > 0, but "+maxExclusive;
132+
133+
indexes = new int[degree];
134+
maxForIndex = (index) -> maxExclusive;
135+
init();
136+
}//new
137+
138+
/**
139+
* Permutation with repetition. Every index has its own max.
140+
* @param maxExclusive all indexes have their own max and rotate in range [0..maxExclusive[index])
141+
*/
142+
public Incrementer (final int[] maxExclusive) {
143+
assert maxExclusive.length > 0 : "degree must be > 0, but "+maxExclusive.length;
144+
145+
indexes = new int[maxExclusive.length];
146+
maxForIndex = (index) -> maxExclusive[index];
147+
init();
148+
}//new
149+
150+
protected void init () {}
151+
152+
public int degree () {
153+
return indexes.length;
154+
}
155+
156+
public int indexAt (int indexIndex) {
157+
return indexes[indexIndex];
158+
}
159+
160+
public int maxAt (int indexIndex) {
161+
return maxForIndex.applyAsInt(indexIndex);
162+
}
163+
164+
@Override public String toString () {
165+
return "Incrementer"+ Arrays.toString(indexes);
166+
}
167+
168+
169+
/**
170+
Increments index vector (array with indexes) by 1.
171+
E.g. for base 2: 0,0,0→0,0,1→0,1,0
172+
Emulates one step in multi-nested-for loop.
173+
174+
@return we "overflowed" the array size = all indexes have reached their max-1
175+
@see #forLoop(Predicate)
176+
*/
177+
public boolean incrementIndexVector () {
178+
final int len = indexes.length;
179+
180+
for (int i = 0; i < len; ) {
181+
if (++indexes[i] < maxForIndex.applyAsInt(i)) {
182+
return false;
183+
184+
} else {// overflow
185+
indexes[i] = 0;// e.g. [10]: 09 → 10
186+
i++;
187+
}
188+
}
189+
return true;
190+
}
191+
192+
/**
193+
* Permutation with repetition.
194+
* @param loopBody receive this Incrementer with indexes and maxExclusive, must return boolean searchIsOver/found
195+
* @return searchIsOver/found
196+
*/
197+
public boolean forLoop (Predicate<Incrementer> loopBody) {
198+
while (true) {
199+
boolean found = loopBody.test(this);
200+
if (found) {
201+
return true;
202+
}
203+
boolean overflow = incrementIndexVector();
204+
if (overflow) {
205+
return false;
206+
}
207+
}
208+
}
209+
}//Incrementer
210+
211+
}

0 commit comments

Comments
 (0)