Created
April 20, 2012 14:04
-
-
Save timyates/2428928 to your computer and use it in GitHub Desktop.
LazyGenerator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LazyGenerator.from 1..10 where { it < 3 } transform { it * idx++ } using idx:0 each { | |
println "Transformed Range $it" | |
} | |
LazyGenerator.from x:1..5, y:2..5 where { ( x + y ) % ( x + 2 ) == 0 } eachWithIndex { match, idx -> | |
println "$idx) $match" | |
} | |
LazyGenerator.from 1..10 each { | |
println "Range: $it" | |
} | |
LazyGenerator.from 1..10 where { it > 3 && it < 7 } each { | |
println "Limited Range $it" | |
} | |
def c = 0 | |
LazyGenerator.from { c++ } take 5 each { | |
println it | |
} | |
class LazyGenerator implements Iterator { | |
class HeadedIterator implements Iterator { | |
def next | |
Closure nextCall | |
boolean hasNext() { next != null } | |
Object next() { | |
def ret = next | |
next = nextCall.call() | |
ret | |
} | |
void remove() {} | |
} | |
private Closure definition | |
private Closure condition | |
private Closure transform | |
private Map using | |
private def initial | |
private def iterators | |
private def keys | |
private def current = null | |
private boolean exhausted | |
private Closure loadNext | |
public LazyGenerator( Closure definition, Closure condition={ true }, Closure transform={ it }, Map using=[:] ) { | |
this.definition = definition | |
initial = definition.call() | |
if( initial instanceof Map ) { | |
iterators = initial.collectEntries { [(it.key):it.value.iterator()] } | |
keys = initial.keySet() as List | |
this.loadNext = this.&mapNext | |
} | |
else { | |
iterators = initial instanceof Iterable ? | |
initial.iterator() : | |
new HeadedIterator( next:initial, nextCall:definition ) | |
this.loadNext = this.&iterableNext | |
} | |
this.condition = condition | |
this.transform = transform | |
this.transform.delegate = using | |
this.transform.resolveStrategy = Closure.DELEGATE_FIRST | |
this.using = using | |
exhausted = false | |
loadNext() | |
} | |
private void iterableNext() { | |
while( !exhausted ) { | |
if( current == null ) { | |
current = iterators.next() | |
} | |
else { | |
if( iterators.hasNext() ) { | |
current = iterators.next() | |
} | |
else { | |
exhausted = true | |
} | |
} | |
if( condition.call( current ) ) break | |
} | |
} | |
private void mapNext() { | |
while( !exhausted ) { | |
if( current == null ) { | |
current = keys.collectEntries { [ (it):iterators[ it ].next() ] } | |
} | |
else { | |
for( i in 0..<keys.size() ) { | |
if( iterators[ keys[ i ] ].hasNext() ) { | |
current[ keys[ i ] ] = iterators[ keys[ i ] ].next() | |
break | |
} | |
else if( i < keys.size() - 1 ) { | |
iterators[ keys[ i ] ] = initial[ keys[ i ] ].iterator() | |
current[ keys[ i ] ] = iterators[ keys[ i ] ].next() | |
} | |
else { | |
exhausted = true | |
} | |
} | |
} | |
condition.delegate = current | |
if( condition.call() ) break | |
} | |
} | |
public boolean hasNext() { | |
current != null && !exhausted | |
} | |
public Object next() { | |
def ret = current instanceof Map ? current.clone() : current | |
loadNext() | |
transform.call( ret ) | |
} | |
public void remove() {} | |
public boolean isExhausted() { exhausted } | |
static LazyGenerator from( Closure a ) { | |
new LazyGenerator( a ) | |
} | |
static LazyGenerator from( a ) { | |
new LazyGenerator( { a } ) | |
} | |
public LazyGenerator where( b ) { | |
b.resolveStrategy = Closure.DELEGATE_FIRST | |
new LazyGenerator( definition, b ) | |
} | |
public LazyGenerator transform( c ) { | |
new LazyGenerator( definition, condition, c ) | |
} | |
public LazyGenerator using( Map d ) { | |
new LazyGenerator( definition, condition, transform, d ) | |
} | |
} |
Cleaned it up a bit...
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Messing around with Groovy.
Very little (no) error checking or exception handling.
May contain nuts.