はじめに
Java8 Streamで書いてみたらどうなるかなーと思って、2種類作ってみました。
Stream APIのみで頑張ってみた
できるはできたけど、副作用前提のコードになってしまっているので微妙な例ですね...
進捗どうですか.java
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
public class 進捗どうですか {
@Test
public void test() {
List<String> src = Arrays.asList("進捗", "どう", "です", "か");
String regexp = ".*" + String.join("", src) + "$";
AtomicInteger counter = new AtomicInteger(0);
StringBuilder sb = new StringBuilder();
String actual = new Random().ints(0, src.size())
.mapToObj(src::get)
.map(s -> sb.append(s).toString())
.peek(s -> counter.incrementAndGet())
.filter(s -> s.matches(regexp))
.findFirst()
.get().toString();
System.out.println(actual + "???");
System.out.println(" _人人人人人人人_\n >進捗どうですか<\n  ̄Y^Y^Y^Y^Y^Y^Y ̄");
System.out.println(counter.get() + "文字で煽られました");
}
}
Streamと再帰処理を使ってみた
Streamだけの例が残念な感じなので、Javaによる関数型プログラミングの7章で取り上げられている再帰処理を使ってみると、こんな感じです。
進捗どうですか再帰.java
import org.junit.Test;
public class 進捗どうですか再帰 {
@Test
public void test() {
Progress progress = returning(new Progress()).invoke();
System.out.println(progress.getStr() + "???");
System.out.println(" _人人人人人人人_\n >進捗どうですか<\n  ̄Y^Y^Y^Y^Y^Y ̄");
System.out.println(progress.getCount() + "文字で煽られました");
}
public static TailCall<Progress> returning(Progress preProgress) {
Progress progress = Progress.proceed(preProgress);
return progress.unDone()
? TailCalls.call(() -> returning(progress))
: TailCalls.done(progress);
}
}
Progress.java
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class Progress {
private final static List<String> src = Arrays.asList("進捗", "どう", "です", "か");
private final static String regexp = ".*" + String.join("", src) + "$";
private final String str;
private final int count;
public Progress() {
this("", 0);
}
public Progress(String str, int count) {
this.str = str;
this.count = count;
}
public static Progress proceed(Progress progress) {
return new Progress(progress.getStr() + makeSuffix(), progress.getCount() + 1);
}
public static String makeSuffix() {
return new Random().ints(0, src.size())
.mapToObj(src::get)
.findFirst().orElseThrow(() -> new RuntimeException());
}
public boolean unDone() {
return !str.matches(regexp);
}
public String getStr() {
return str;
}
public int getCount() {
return count;
}
}
TailCall.java
import java.util.stream.Stream;
@FunctionalInterface
public interface TailCall<T> {
public TailCall<T> apply();
public default boolean isComplete() {
return false;
}
public default T result() {
throw new Error("Not implemented!");
}
public default T invoke() {
return Stream.iterate(this, TailCall::apply)
.filter(TailCall::isComplete)
.findFirst()
.get()
.result();
}
}
TailCalls.java
public class TailCalls {
public static <T> TailCall<T> call(final TailCall<T> nextCall) {
return nextCall;
}
public static <T> TailCall<T> done(final T value) {
return new TailCall<T>() {
@Override
public boolean isComplete() {
return true;
}
@Override
public T result() {
return value;
}
@Override
public TailCall<T> apply() {
throw new Error("Not implemented!");
}
}
}
}
実行例
ですかかどうですか進捗かかかかどうどうですどうです進捗進捗進捗どうかどうですですどう進捗進捗進捗どうどうですどうかどうどうです進捗進捗進捗かかですどうどうですですどうです進捗かどう進捗どうですか???
_人人人人人人人_
>進捗どうですか<
 ̄Y^Y^Y^Y^Y^Y^Y ̄
55文字で煽られました
おわりに
再帰処理の方がクラス数は多いですが、TailCall
とTailCalls
は一度作っておけば、ラムダ式を変えるだけで他の再帰処理にも使えるので便利ですね。