sobota, stycznia 03, 2015

Potfór ;-) czyli generator z yield w Java'ie

Poczytałem sobie wczoraj o generatorach, które mają się pojawić w ECMAScript6 i mi się spodobały.
Co prawda można zrobić generatory na wiele sposobów i wcale użycie yield nie jest wymagane, ale chciałem zobaczyć czy uda mi się takie coś popełnić w Java'ie ;-)

Idea jest taka by pisać tylko generator (który będzie w klasie niestety). Np. generator liczb od 0 do ilości wywołań (zakładając, że jest ich mniej niż maksymalna wartość int'a) może wyglądać tak:

public class Counter extends Generator<Integer> {
@Override public void generate() {
int i=0;
while (true) {
yield(i++);
}
}
}

znów generator, który będzie generował ciąg 1,2,3,1,2,3,1,2,3 i tak dalej z powtarzaniem się 1,2,3 będzie wyglądał tak:


public class Counter123 extends Generator<Integer> {
@Override public void generate() {
while (true) {
yield(1);
yield(2);
yield(3);
}
}
}
A generator, który będzie generował poszczególne znaki ze Stringa tak:

public class LettersFromString extends Generator<String> {

private String str;

public LettersFromString(String str) {
this.str = str;
}

@Override public void generate() {
int idx = 0;
while (idx<str.length()) yield(""+str.charAt(idx++));
}
}
Użycie generatora zaś będzie wyglądało tak, że najpierw go stworzymy, a później będziemy dostawać wartości przezeń generowane po zawołaniu next(). Jeśli wartości nie będzie to dostaniemy null.
Czyli przy generatorze:
public class OnceGenerator extends Generator<String> {
@Override public void generate() {
yield("Test");
}
}
Przy pierwszym zawołaniu next() dostaniemy "Test", a przy drugim null.
Ogólna idea jest taka by pisząc generator myśleć tylko o generowaniu kolejnej wartości, a nie o całym cięciu naszego algorytmu generowania na jakieś kawałki. 
Ludzie myślą jednowątkowo i generator powinien być pisany tak jakby to był jeden wątek.
No i stworzyłem portfora ;-) który pozwala na tworzenie takich generatorów ;-)

public abstract class Generator<T> {

private T value;
private Object lock = new Object();
boolean available = false;

public Generator() {
this(null);
}

public Generator(T val) {
this.value = val;
new Thread(new Runnable() {
@Override public void run() {
synchronized (lock) {
generate();
available = true;
}
}
}).start();
}

public abstract void generate();

public void yield(T val) {
this.value = val;
this.available = true;
try {
lock.notify();
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public T next() {
synchronized (lock) {
while (!available) {
try {
lock.wait();
} catch (InterruptedException ie) {

}
}
T val = this.value;
this.value = null;
this.available = false;
lock.notify();
return val;
}
}
}

Który to potfór pozwala na tworzenie generatorów jak w ECMAScript6 ;-)
Teraz się zastanawiam czy można by było stworzyć takie generatory bez wątków i na razie nie wiem.



Podobne postybeta
Przepływ sterowany danymi - A takie Java'owe coś ;-)
Sztuczki tropiciela błędów ;-)
Modale dobre - confirm dla Androida :-)
wait() i notify()/notifyAll() - najbardziej nierozumiane metody klasy Object ;-)
Generator programów