Created
July 31, 2016 19:28
-
-
Save cogmission/1bfa2f82889b2d5bd615e72c15b4584d to your computer and use it in GitHub Desktop.
Java Version of Python Generator
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
/* --------------------------------------------------------------------- | |
* Numenta Platform for Intelligent Computing (NuPIC) | |
* Copyright (C) 2016, Numenta, Inc. Unless you have an agreement | |
* with Numenta, Inc., for a separate license for this software code, the | |
* following terms and conditions apply: | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU Affero General Public License version 3 as | |
* published by the Free Software Foundation. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
* See the GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see http://www.gnu.org/licenses. | |
* | |
* http://numenta.org/licenses/ | |
* --------------------------------------------------------------------- | |
*/ | |
package org.numenta.nupic.util; | |
import java.util.Iterator; | |
import java.util.concurrent.CyclicBarrier; | |
import java.util.concurrent.LinkedBlockingQueue; | |
/** | |
* Java version of a Python Generator. This generator has no "send" | |
* method but does have yield. Values are consumed by either a forEach | |
* loop or by calling {@link #next()} directly. | |
* <p> | |
* Typical Usage: | |
* </p> | |
* <p> | |
* Define a class which overrides {@code AbstractGenerator} as such: | |
* </p> | |
* <b>For terminable generator...</b> | |
* <pre> | |
* class MyGenerator extends AbstractGenerator<Integer> { | |
* public void exec() { | |
* int i = 0; | |
* while(i < 50) { | |
* // Do some work then call yield | |
* | |
* yield(new Integer(i++)); // Execution "pauses" here after yield() is executed... | |
* | |
* // Do some more work... // Execution continues here after call to next()... | |
* } | |
* } | |
* | |
* public boolean isConsumed() { | |
* return false; // "false" makes this an "infinite" generator. | |
* } | |
* } | |
* | |
* MyGenerator generator = new MyGenerator(); | |
* while(generator.hasNext()) { | |
* System.out.println(generator.next()); | |
* } | |
* | |
* // Using foreach... | |
* MyGenerator generator2 = new MyGenerator(); | |
* for(Integer result : generator2) { | |
* System.out.println(result); | |
* } | |
* </pre> | |
* | |
* <hr><br> | |
* | |
* <b>For infinite generator...</b> | |
* <pre> | |
* class MyGenerator extends AbstractGenerator<Integer> { | |
* public void exec() { | |
* int i = 0; | |
* while(true) { | |
* if(isHalted()) { // Sane implementation must do this first... | |
break; | |
} | |
* // Do some work then call yield | |
* | |
* yield(new Integer(i++)); // Execution "pauses" here after yield() is executed... | |
* | |
* // Do some more work... // Execution continues here after call to next()... | |
* } | |
* } | |
* | |
* public boolean isConsumed() { | |
* return false; // "false" makes this an "infinite" generator. | |
* } | |
* } | |
* | |
* MyGenerator generator = new MyGenerator(); | |
* while(generator.hasNext()) { | |
* System.out.println(generator.next()); | |
* } | |
* | |
* // Using foreach... | |
* MyGenerator generator2 = new MyGenerator(); | |
* for(Integer result : generator2) { | |
* System.out.println(result); | |
* } | |
* </pre> | |
* | |
* @author cogmission | |
* | |
* @param <T> the return type of the generator | |
*/ | |
public abstract class AbstractGenerator<T> implements Generator<T> { | |
/** Default serial id*/ | |
protected static final long serialVersionUID = 1L; | |
/** The object of type <T> returned from {@link #yield(Object)}*/ | |
private T returnVal; | |
/** The {@link Runnable} code body containing the call to {@link #exec} */ | |
private Runnable body; | |
/** Signals the {@link #yield} point of execution halting */ | |
private CyclicBarrier barrier = new CyclicBarrier(2); | |
/** Set to true during generator initialization */ | |
private volatile boolean running; | |
/** Transfers the execution result from the inner thread to the caller */ | |
private LinkedBlockingQueue<T> queue = new LinkedBlockingQueue<>(); | |
/** The inner execution thread */ | |
private Thread mainThread; | |
/** | |
* Constructs a new {@code AbstractGenerator} | |
*/ | |
public AbstractGenerator() { | |
body = () -> { | |
while(running) { | |
try { | |
exec(); | |
}catch(Exception e) { | |
//e.printStackTrace(); | |
System.out.println("Was interrupted"); | |
} | |
} | |
}; | |
} | |
/** | |
* Override this method in a subclass to execute the | |
* body of code which will do the processing during | |
* every iteration. This method contains the call to | |
* {@link #yield(Object)} which stores the processed | |
* state for retrieval on the next call to {@link #next()}. | |
*/ | |
public abstract void exec(); | |
/** | |
* Called during the execution of the {@link #exec()} | |
* method to signal the availability of the result from | |
* one iteration of processing. | |
* | |
* @param t the object of type <T> to return | |
*/ | |
@Override | |
public void yield(T t) { | |
returnVal = t; | |
try { | |
queue.offer(t); | |
barrier.await(); | |
}catch(Exception e) { | |
// Halted execution ends here | |
} | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public boolean hasNext() { | |
return !isConsumed(); | |
} | |
/** | |
* Halts the main thread | |
*/ | |
@Override | |
public void halt() { | |
running = false; | |
mainThread.interrupt(); | |
} | |
/** | |
* Used by the main {@link #exec} loop to query if | |
* the execution is to be stopped. | |
* @return true if halt requested, false if not | |
*/ | |
@Override | |
public boolean haltRequested() { | |
return !running; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public T next() { | |
if(!running) { | |
running = true; | |
mainThread = new Thread(body); | |
mainThread.setDaemon(true); | |
mainThread.start(); | |
} else { | |
try { barrier.await(); }catch(Exception e) { e.printStackTrace(); } | |
} | |
try { returnVal = queue.take(); }catch(Exception e) { e.printStackTrace(); } | |
return returnVal; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public Iterator<T> iterator() { | |
return this; | |
} | |
} |
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
/* --------------------------------------------------------------------- | |
* Numenta Platform for Intelligent Computing (NuPIC) | |
* Copyright (C) 2016, Numenta, Inc. Unless you have an agreement | |
* with Numenta, Inc., for a separate license for this software code, the | |
* following terms and conditions apply: | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU Affero General Public License version 3 as | |
* published by the Free Software Foundation. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
* See the GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see http://www.gnu.org/licenses. | |
* | |
* http://numenta.org/licenses/ | |
* --------------------------------------------------------------------- | |
*/ | |
package org.numenta.nupic.util; | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertFalse; | |
import static org.junit.Assert.assertNotEquals; | |
import static org.junit.Assert.assertTrue; | |
import org.junit.Test; | |
public class AbstractGeneratorTest { | |
///////////////////////////////// | |
// Utility Methods // | |
///////////////////////////////// | |
/** | |
* Returns an {@link AbstractGenerator} that runs for 30 iterations | |
* | |
* @return an {@link AbstractGenerator} that runs for 30 iterations | |
*/ | |
private Generator<Integer> getTerminableGenerator() { | |
@SuppressWarnings("serial") | |
class TerminableGenerator extends AbstractGenerator<Integer> { | |
int i = 0; | |
public void exec() { | |
while(i < 31) { | |
yield(new Integer(i++)); | |
} | |
} | |
public boolean isConsumed() { | |
return i > 30; | |
} | |
} | |
return new TerminableGenerator(); | |
} | |
/** | |
* Returns an {@link AbstractGenerator} that runs infinitely | |
* until halted. | |
* | |
* @return an {@link AbstractGenerator} that runs infinitely | |
* until halted. | |
*/ | |
private Generator<Integer> getInfiniteGenerator() { | |
@SuppressWarnings("serial") | |
class InfiniteGenerator extends AbstractGenerator<Integer> { | |
int i = 0; | |
public void exec() { | |
while(true) { | |
if(haltRequested()) { | |
break; | |
} | |
yield(new Integer(i++)); | |
} | |
} | |
public boolean isConsumed() { | |
return false; | |
} | |
} | |
return new InfiniteGenerator(); | |
} | |
///////////////////////////////// | |
// Test Methods // | |
///////////////////////////////// | |
/** | |
* Test that iteration control is managed by the {@link AbstractGenerator#exec()} | |
* method, and that the execution can be precisely terminated. | |
*/ | |
@Test | |
public void testTerminableGenerator() { | |
int i = 0; | |
Generator<Integer> generator = getTerminableGenerator(); | |
for(Integer result : generator) { | |
assertNotEquals(result, Integer.valueOf(i - 1)); | |
assertNotEquals(result, Integer.valueOf(i + 1)); | |
assertEquals(result, Integer.valueOf(i)); | |
i++; | |
} | |
assertTrue(i == 31); | |
assertFalse(i == 32); | |
} | |
/** | |
* Test that we can create an generator that can run infinitely, but | |
* still have its execution time managed by the methods on {@link AbstractGenerator} | |
*/ | |
@Test | |
public void testInfiniteGenerator() { | |
int i = 0; | |
Generator<Integer> generator = getInfiniteGenerator(); | |
for(Integer result : generator) { | |
if(result == 30) { | |
generator.halt(); | |
break; | |
} | |
assertNotEquals(result, Integer.valueOf(i - 1)); | |
assertNotEquals(result, Integer.valueOf(i + 1)); | |
assertEquals(result, Integer.valueOf(i)); | |
i++; | |
} | |
assertTrue(i == 30); | |
assertFalse(i == 31); | |
} | |
} |
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
/* --------------------------------------------------------------------- | |
* Numenta Platform for Intelligent Computing (NuPIC) | |
* Copyright (C) 2016, Numenta, Inc. Unless you have an agreement | |
* with Numenta, Inc., for a separate license for this software code, the | |
* following terms and conditions apply: | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU Affero General Public License version 3 as | |
* published by the Free Software Foundation. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
* See the GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see http://www.gnu.org/licenses. | |
* | |
* http://numenta.org/licenses/ | |
* --------------------------------------------------------------------- | |
*/ | |
package org.numenta.nupic.util; | |
import java.io.Serializable; | |
import java.util.Iterator; | |
public interface Generator<T> extends Iterable<T>, Iterator<T>, Serializable { | |
/** | |
* Called during the execution of the {@link #exec()} | |
* method to signal the availability of the result from | |
* one iteration of processing. | |
* | |
* @param t the object of type <T> to return | |
*/ | |
default void yield(T t) {} | |
/** | |
* Halts the main thread | |
*/ | |
default void halt() {} | |
/** | |
* Used by the main {@link #exec} loop to query if | |
* the execution is to be stopped. | |
* @return true if halt requested, false if not | |
*/ | |
default boolean haltRequested() { return false; } | |
/** | |
* Overridden to identify when this generator has | |
* concluded its processing. For infinite generators | |
* simply return "false" here. | |
* | |
* @return a flag indicating whether the last iteration | |
* was the last processing cycle. | |
*/ | |
boolean isConsumed(); | |
/** | |
* Returns a flag indicating whether another iteration | |
* of processing may occur. | |
* | |
* @return true if so, false if not | |
*/ | |
boolean hasNext(); | |
/** | |
* Returns the object of type <T> which is the | |
* result of one iteration of processing. | |
* | |
* @return the object of type <T> to return | |
*/ | |
T next(); | |
/** | |
* {@inheritDoc} | |
*/ | |
Iterator<T> iterator(); | |
} |
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
package org.numenta.nupic.util; | |
/** | |
* Generates a range of integers while exhibiting the specific behaviors | |
* of a Python generator. | |
* | |
* @author cogmission | |
* @see AbstractGenerator | |
*/ | |
public interface IntGenerator { | |
/** | |
* Returns an {@link AbstractGenerator} capable of returning a range of integers | |
* specified by the lower and upper bound arguments. | |
* | |
* @param lower the lower bound <b><em>(inclusive)</em></b> | |
* @param upper the upper bound <b><em>(exclusive)</em></b> | |
* @return an {@link AbstractGenerator} capable of returning a range of integers | |
*/ | |
public static Generator<Integer> of(int lower, int upper) { | |
/** | |
* Inner implementation of an {@code AbstractGenerator} for {@code Integer}s | |
*/ | |
class TerminableGenerator extends AbstractGenerator<Integer> { | |
private static final long serialVersionUID = 1L; | |
int i = lower; | |
@Override | |
public void exec() { | |
while(i < upper) { | |
yield(Integer.valueOf(i++)); | |
} | |
} | |
@Override | |
public boolean isConsumed() { | |
return i > upper - 1; | |
} | |
} | |
return new TerminableGenerator(); | |
} | |
} |
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
/* --------------------------------------------------------------------- | |
* Numenta Platform for Intelligent Computing (NuPIC) | |
* Copyright (C) 2016, Numenta, Inc. Unless you have an agreement | |
* with Numenta, Inc., for a separate license for this software code, the | |
* following terms and conditions apply: | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU Affero General Public License version 3 as | |
* published by the Free Software Foundation. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
* See the GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see http://www.gnu.org/licenses. | |
* | |
* http://numenta.org/licenses/ | |
* --------------------------------------------------------------------- | |
*/ | |
package org.numenta.nupic.util; | |
import static org.junit.Assert.*; | |
import org.junit.Test; | |
public class IntGeneratorTest { | |
/** | |
* Test that iteration control is managed by the {@link AbstractGenerator#exec()} | |
* method, and that the execution can be precisely terminated. | |
*/ | |
@Test | |
public void testIntegerGenerator() { | |
int i = 0; | |
Generator<Integer> generator = IntGenerator.of(0, 31); | |
for(Integer result : generator) { | |
assertNotEquals(result, Integer.valueOf(i - 1)); | |
assertNotEquals(result, Integer.valueOf(i + 1)); | |
assertEquals(result, Integer.valueOf(i)); | |
i++; | |
} | |
assertTrue(i == 31); | |
assertFalse(i == 32); | |
} | |
/** | |
* Test that iteration control is managed by the {@link AbstractGenerator#exec()} | |
* method, and that the execution can be precisely terminated. | |
*/ | |
@Test | |
public void testIntegerGenerator_SpecifyNext() { | |
int i = 28; | |
Generator<Integer> generator = IntGenerator.of(i, 31); | |
assertFalse(generator.next() == 29); | |
assertTrue(generator.next() == 29); | |
assertTrue(generator.next() == 30); | |
assertFalse(generator.hasNext()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment