Skip to content
Skip to breadcrumbs
Skip to header menu
Skip to action menu
Skip to quick search
Quick Search
Browse
Pages
Blog
Labels
Attachments
Mail
Advanced
What’s New
Space Directory
Feed Builder
Keyboard Shortcuts
Confluence Gadgets
Log In
Sign Up
Dashboard
Groovy
Copy Page
You are not logged in. Any changes you make will be marked as
anonymous
. You may want to
Log In
if you already have an account. You can also
Sign Up
for a new account.
This page is being edited by
.
Paragraph
Paragraph
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Preformatted
Quote
Bold
Italic
Underline
More colours
Strikethrough
Subscript
Superscript
Monospace
Clear Formatting
Bullet list
Numbered list
Outdent
Indent
Align left
Align center
Align right
Link
Table
Insert
Insert Content
Image
Link
Attachment
Symbol
Emoticon
Wiki Markup
Horizontal rule
tinymce.confluence.insert_menu.macro_desc
Info
JIRA Issue
Status
Gallery
Tasklist
Table of Contents
Other Macros
Page Layout
No Layout
Two column (simple)
Two column (simple, left sidebar)
Two column (simple, right sidebar)
Three column (simple)
Two column
Two column (left sidebar)
Two column (right sidebar)
Three column
Three column (left and right sidebars)
Undo
Redo
Find/Replace
Keyboard Shortcuts Help
<p>One useful Groovy iterator is the SyncIterator, based on Ruby's SyncEnumerator which can iterate across several data structures at the same time. It makes use of DefaultTypeTransformation.asCollection() to perform the same coercion that Groovy uses by default to implement methods such as each(), collect(), and the like...</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; public class SyncIterator implements Iterator, Iterable{ private theobjects SyncIterator(Object[] objects){ theobjects=objects.collect{ if (it instanceof Iterator) return /*from closure*/ it else return /*from closure*/ DefaultTypeTransformation.asCollection(it).iterator() } } boolean hasNext(){ return theobjects.any{it.hasNext()} } Object next(){ if (!hasNext()) throw new java.util.NoSuchElementException() return theobjects.collect{ try{ return /*from closure*/ it.next() }catch(NoSuchElementException e){ return /*from closure*/ null } } } Iterator iterator(){ return this; } void remove(){ throw new UnsupportedOperationException("remove() not supported") } } </pre></td></tr></table> <p>Another useful iterator is the Generator, which takes a closure and offers up the stuff it yields as an iterator, without having to keep all of the generated data around in memory. (It's based loosely on Ruby 1.9's Generator, which was also the challenge in <a href="http://www.rubyquiz.com/quiz66.html">Ruby Quiz #66</a>)</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> //JAVA CODE import java.util.concurrent.*; import java.lang.ref.*; import groovy.lang.Closure; import java.util.*; public class Generator<T> implements Iterator<T>, Iterable<T>{ Semaphore availSemaphore=new Semaphore(0); Semaphore emptySemaphore=new Semaphore(1); //the thread can push one value at at time into pushedValue T pushedValue=null; //pull value moves it from pushedValue to pulledValue //until it is released by next() T pulledValue=null; boolean hasPulledValue=false; Thread internalThread; Generator(Closure closure){ internalThread=new GeneratorThread<T>(this,closure); internalThread.setDaemon(true); internalThread.start(); } private void pullValue(){ availSemaphore.acquireUninterruptibly(); pulledValue=pushedValue; pushedValue=null; hasPulledValue=true; emptySemaphore.release(); } public boolean hasNext(){ if (!hasPulledValue) pullValue(); return emptySemaphore.availablePermits() != 2; } public T next(){ if (!hasNext()) throw new NoSuchElementException("Closure has no more values"); T retval=pulledValue; hasPulledValue=false; return retval; } public void remove(){ throw new UnsupportedOperationException( "Remove is not supported on generators"); } public Iterator<T> iterator(){ return this; } public void finalize(){ internalThread.interrupt(); } static class GeneratorThread<T> extends Thread{ WeakReference<Generator<T>> generatorRef; Closure closure; public GeneratorThread(Generator<T> generator, Closure cl){ generatorRef=new WeakReference<Generator<T>>(generator); closure=cl; } public void run(){ closure.call(new SaveClosure<T>(this)); Generator generator=generatorRef.get(); //NOTE: when the closure completes, pullValue() will block forever //waiting for more available data. This release() allows it to //get in one last time, and read a variable indicating that the //thread has died and isn't producing any more data. one final //pullValue() run will have emptySemaphore==1 and //availSemaphore==1, and it will make emptySemaphore==2 thus //indicating that the thread has died if (generator!=null){ generator.availSemaphore.release(); } //NOTE: if the generator has been garbage collected, we don't care //about letting the generator pull a termination condition. } } static class SaveClosure<T> extends Closure{ WeakReference<Generator<T>> generatorRef; Semaphore emptySemaphore; Semaphore availSemaphore; public SaveClosure(GeneratorThread<T> gt){ super(gt,null); generatorRef=gt.generatorRef; Generator<T> generator=generatorRef.get(); if (generator!=null){ emptySemaphore=generator.emptySemaphore; availSemaphore=generator.availSemaphore; }else{ throw new GeneratorDisposedException(); } } public void doCall(T value){ try{ emptySemaphore.acquire(); }catch(InterruptedException e){ throw new GeneratorDisposedException(); } Generator<T> generator=generatorRef.get(); if (generator!=null){ generator.pushedValue=value; }else{ throw new GeneratorDisposedException(); } availSemaphore.release(); } } /** * A GeneratorDisposedException is used to terminate the thread * that was generating values, once the Generator has been garbage * collected. */ static public class GeneratorDisposedException extends RuntimeException{ } } </pre></td></tr></table> <p> Note that Groovy doesn't have this concept built in. Its MethodClosure to Iterator conversion loads everything into an array all at once, which isn't a particularly good idea you may be running find() on it, and only need to generate the first item to find what you're looking for.</p> <p> For example, consider the following use of a Generator:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> //generates an infinite sequence of Fibonacci numbers def fibonacci(Closure yield){ def a=0 def b=1 def temp while(true){ yield(b) temp=a a=b b=a+temp } } //find the first Fibonacci number that's evenly divisible by 20 println(new Generator(this.&fibonacci).find{ it % 20 == 0}) //BROKEN: the groovy runtime wants to run fibonacci to termination loading values into an array. //this generates an out of memory error. this.&fibonacci.find{it % 20 == 0} </pre></td></tr></table> <p>As the Groovy runtime implements this now, you would exhaust all available ram when converting the MethodClosure to an iterator, before find() was ever called. With a Generator, values are only generated on demand.</p> <p>Disadvantages:</p> <ul> <li>because of the use of threads, the generator may generate one more value than is actually needed before the garbage collector disposes of the generator.</li> <li>Exceptions thrown from the generator method are not propagated back to the caller (again, because the function is called on a different thread)</li> </ul>
Please type the word appearing in the picture.
Attachments
Labels
Location
Watch this page
< Edit
Preview >
Loading…
Save
Cancel
Next hint
search
attachments
weblink
advanced