惰性計算指「盡可能延遲表達式求值」。對比嚴格求值(如 Java 默認):
print length([2+1, 3*2, 1/0, 5-4])
// 嚴格語言:編譯/運行拋出 DivByZero 異常(第三個元素 1/0)
// 惰性語言:輸出 4(僅計算列表長度,不求值元素)
優勢:
map/filter 等鏈式操作避免中間集合創建,效率更高。Java 無原生惰性支持,但可通過 Iterator、第三方庫(Commons Collections、Totally Lazy)實現。
基於經典素數判斷算法(見下),實現 Iterator<Integer> 按需產生下一個素數。
public class Prime {
public static boolean isFactor(int p, int n) { return n % p == 0; }
public static Set<Integer> getFactors(int n) { ... }
public static int sumFactors(int n) { ... }
public static boolean isPrime(int n) { return n==2 || sumFactors(n)==n+1; }
public static int nextPrimeFrom(int last) { do last++; while(!isPrime(last)); return last; }
}
public class PrimeIterator implements Iterator<Integer> {
private int lastPrime = 1;
public boolean hasNext() { return true; } // 素數無限
public Integer next() {
return lastPrime = Prime.nextPrimeFrom(lastPrime); // 更新並返回
}
public void remove() { throw new UnsupportedOperationException(); }
}
@Test public void prime_iterator() {
Iterator<Integer> it = new PrimeIterator();
for (int expected : PRIMES_BELOW_50) assertEquals(expected, it.next());
}
雖非標準迭代器用法,但完美模擬惰性集合「按需生成、無限長度」特性。
LazyList.decorate(list, factory) 包裝現有列表,索引訪問時若無值則調用工廠生成。
List<Integer> primes = new ArrayList<>();
List<Integer> lazyPrimes = LazyList.decorate(primes, new PrimeFactory());
for (int i=0; i
public class PrimeFactory implements Factory {
private int index = 0;
public Object create() { return Prime.indexedPrime(index++); }
}
缺陷:工廠無感知請求索引,需自維護狀態(如 index),且列表必須預初始化為空以保持索引同步,耦合度高。
Totally Lazy 將惰性貫穿框架設計,大量使用靜態導入構建可讀性極強的 DSL 風格代碼。
import static com.googlecode.totallylazy.Predicates.is;
import static com.googlecode.totallylazy.numbers.Numbers.*;
public class Prime {
public static Predicate<Number> isFactor(Number n) {
return where(remainder(n), is(zero)); // n % factor == 0
}
public static Sequence<Number> factors(Number n) {
return range(1, n).filter(isFactor(n)); // 1..n 過濾因子
}
public static Number sumFactors(Number n) {
return factors(n).reduce(sum); // 求和
}
public static boolean isPrime(Number n) {
return equalTo(increment(n), sumFactors(n)); // n+1 == sum(factors)
}
}
代碼如規格文檔般清晰,where、remainder、range、filter、reduce 均為惰性操作。
public class PrimeFast {
public static Sequence<Number> getFactors(final Number n) {
Sequence<Number> lower = range(1, squareRoot(n)).filter(isFactor(n));
return lower.join(lower.map(divide(n))); // 對稱生成大因子
}
public static Sequence<Number> factors(final Number n) {
return getFactors(n).memorise(); // 記憶化緩存
}
// sumFactors、isPrime 同前
}
memorise() 自動緩存相同參數調用結果,無需手工 Map 存儲。
public static Function1<Number, Number> nextPrime = n -> nextPrime(n);
public static Computation<Number> primes = computation(2, computation(3, nextPrime));
public static Sequence<Number> primes() { return primes; }
public static Number nextPrime(Number n) {
return iterate(add(2), n).filter(prime).second(); // 從 n+2 起步,過濾素數,取第二個
}
典型惰性模式:種子值 + 生成下一元素的函數 組合成 Computation,轉為 Sequence 即得無限惰性序列。
下一期將探討 Groovy、Scala、Clojure 中的原生惰性支持與對比。