進捗どうですか with Java8 Streams

## はじめに

Java8 Streamで書いてみたらどうなるかなーと思って、2種類作ってみました。

## Stream APIのみで頑張ってみた

できるはできたけど、副作用前提のコードになってしまっているので微妙な例ですね...

```java:進捗どうですか.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() + "文字で煽られました");
  }
}
```

- [paiza.ioで実行](https://paiza.io/projects/F_4Y6j8VpMpZksQnsUwFag)


## Streamと再帰処理を使ってみた

Streamだけの例が残念な感じなので、[Javaによる関数型プログラミング](http://www.amazon.co.jp/dp/4873117046/)の7章で取り上げられている再帰処理を使ってみると、こんな感じです。


```java:進捗どうですか再帰.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);
  }

}
```

```java: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;
  }

}
```

```java: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();
  }

}
```

```java: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!");
      }
    }
  }
}
```

- [paiza.ioで実行](https://paiza.io/projects/tc4IEwENd2G67K6NW4ZLSQ)

## 実行例
```
ですかかどうですか進捗かかかかどうどうですどうです進捗進捗進捗どうかどうですですどう進捗進捗進捗どうどうですどうかどうどうです進捗進捗進捗かかですどうどうですですどうです進捗かどう進捗どうですか???
 ＿人人人人人人人＿
 ＞進捗どうですか＜
 ￣Y^Y^Y^Y^Y^Y^Y￣
55文字で煽られました
```

## おわりに

再帰処理の方がクラス数は多いですが、`TailCall`と`TailCalls`は一度作っておけば、ラムダ式を変えるだけで他の再帰処理にも使えるので便利ですね。

## 参考文献
1. [Javaによる関数型プログラミング](http://www.amazon.co.jp/dp/4873117046/)
2. [site:qiita.com AND "進捗・どう・です・か"](https://www.google.co.jp/webhp?ie=UTF-8#q=site%3Aqiita.com+AND+%22%E9%80%B2%E6%8D%97%E3%83%BB%E3%81%A9%E3%81%86%E3%83%BB%E3%81%A7%E3%81%99%E3%83%BB%E3%81%8B%22+)