Skip to content

Instantly share code, notes, and snippets.

@timyates
Created April 20, 2012 14:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timyates/2428928 to your computer and use it in GitHub Desktop.
Save timyates/2428928 to your computer and use it in GitHub Desktop.
LazyGenerator
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 )
}
}
@timyates
Copy link
Author

Messing around with Groovy.

Very little (no) error checking or exception handling.

May contain nuts.

@timyates
Copy link
Author

Cleaned it up a bit...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment