Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
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")
   }

}

...

Code Block
//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> super(gt,nullgenerator=generatorRef.get();
	 if (generator!=null){
	   generatorRef emptySemaphore=gtgenerator.generatorRefemptySemaphore;
	    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.emptySemaphore.acquireUninterruptibly(); 	  generator.pushedValue=value;
	 generator.availSemaphore.release();
}else{
	    throw  }else{new GeneratorDisposedException();
	 throw}
new	 GeneratorDisposedExceptionavailSemaphore.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{
   }
}

 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.

...

Code Block
//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 this.&fibonacci.find{it % 20 == 0}

...