Java流和狀態
使用Java 8流,似乎函式程式設計贏了,無狀態和遞迴萬歲!但是現實有點微妙:與軟體程式設計一樣,它取決於。我相信你的工具箱中的工具越多越好。
當你擁有的只是一把錘子時,一切看起來像釘子。
在函式程式設計中,每個函式都需要純粹:輸出僅取決於輸入,並且沒有副作用。
看看Stream.generate()有兩種用法:
Stream.generate(Math::random);
Stream.generate(() -> "Java");
第一行是隨機值產生,第二行是常量輸出。
Stream.iterate()的示例:
Stream.iterate(0, i -> i + 1);
當下一項的計算很簡單時,這種迭代很容易。當計算變得更復雜時,需要資料結構。
這是一個帶有函式的例子,它計算索引的平方,以便之後可以求和:
Stream.iterate(new double[]{1, 1}, pair -> new double[]{pair[0] + 1, Math.pow(pair[0] + 1, 2)});
我不確定這是最易讀的程式碼片段。為了使它更清潔,讓我們建立一個專用的結構:
public class Pair { public final int index; public final double value; public Pair(int index, double value) { this.index = index; this.value = value; } } Stream.iterate(new Pair(1, 1), pair -> new Pair(pair.index + 1, Math.pow(pair.index + 1, 2)));
這只是稍微好一點,因為計算邏輯仍然在lambda中“隱藏”。一種解決方案可能是讓Pair計算成為下一個值:
public class Pair { public static final Pair SEED = new Pair(1, 1); public final int index; public final double value; public Pair(int index, double value) { this.index = index; this.value = value; } public Pair next() { return new Pair(index + 1, Math.pow(index + 1, 2)); } } Stream.iterate(Pair.SEED, Pair::next);
我認為這是一個非常巧妙的解決方案。它也可以重複用於其他功能/系列/套件。
以下是階乘函式的示例:
public class Factorial { public static final Factorial SEED = new Factorial(1, 1); public final int index; public final int value; public Factorial(int index, int value) { this.index = index; this.value = value; } public Factorial next() { return new Factorial(index + 1, value * index); } } Stream.iterate(Pair.SEED, Pair::next);
還有另一個斐波那契套件:
public class Fibonacci { public static final Fibonacci SEED = new Fibonacci(1, 1); public final int previous; public final int value; public Fibonacci(int previous, int value) { this.previous = previous; this.value = value; } public Fibonacci next() { return new Fibonacci(value, value + previous); } } Stream.iterate(Fibonacci.SEED, Fibonacci::next);
注意狀態是如何引入的?狀態使程式碼更容易閱讀。
現在,讓我們進一步推動事情。還記得Stream.generate()上面函式嗎?使用正確的有狀態Supplier,它可以替換Stream.iterate():
public class IncrementSupplier implements Supplier<Integer> { private int value; public IncrementSupplier(int seed) { this.value = seed; } @Override public Integer get() { return ++value; } } Stream.iterate(0, i -> i + 1); Stream.generate(new IncrementSupplier(0));
上面兩行是同樣的。
開發者更多的是激情的生物,而不是理性的生物。這不是因為Java提供了函式程式設計,它現在不允許狀態。雖然狀態是併發程式設計上下文中的一個問題,但它對於更易讀的程式碼可能是一個巨大的幫助。