## 延遲求值(Lazy Evaluation)
像其表哥LINQ一樣,PINQ也廣泛使用延遲求值:
~~~
$filteredValues = $values->where(function ($i) { return strlen($i) < 50; });
~~~
在上述示例中,提供的函數將不會執行,直到實際需要值為止:
~~~
foreach($filteredValues as $value) {
//在值被迭代的時候,查詢函數將開始執行
}
~~~
## 不可變性(Immutability)
PINQ查詢也是不可變的,也就是:
~~~
$values = \Pinq\Traversable::from(range(1, 10));
$values->where(function ($i) { return $i >= 5; });
foreach($values as $value) {
//1, 2, 3, 4, 5, 6....
}
~~~
你可能會問為什么會這樣,原因如下:
~~~
$values = \Pinq\Traversable::from(range(1, 10));
foreach($values->where(function ($i) { return $i >= 5; }) as $value) {
//5, 6, 7, 8....
}
foreach($values->where(function ($i) { return $i < 5; }) as $value) {
// 如果查詢改變了原始對象,那么在此處迭代將沒有任何值,這是一個非常直觀的錯誤。
}
~~~
編寫原始查詢的正確方法如下:
~~~
$values = \Pinq\Traversable::from(range(1, 10));
//將過濾后的值覆蓋掉原始數據源
$values = $values->where(function ($i) { return $i >= 5; });
foreach($values as $value) {
//5, 6, 7, 8....
}
~~~
## 另一方面
這可能會有點混亂,但是請容忍我, 特殊接口ICollection/ IRepository的實例,所有查詢都被熱切地求值計算,并使原始對象改變。
* 首先,改變:
~~~
//注意這里用的是Collection
$values = \Pinq\Collection::from(range(1, 10));
$values->removeRange(range(1, 4));
foreach($values as $value) {
//5, 6, 7, 8...
}
~~~
ICollection接口是為了給ITraversable接口提供額外的可變性,人們可能很容易地用相同的方式寫下ITraversable實例的代碼:
~~~
$values = \Pinq\Traversable::from(range(1, 10));
//去除掉某部分
$values = $values->except(range(1, 4));
foreach($values as $value) {
//5, 6, 7, 8...
}
~~~
這可能看起來更好,你甚至得到延遲求值的性能收獲,但是在使用外部數據源時,差異變得明顯。如果您正在查詢底層數據庫,并且您真的想要刪除這些值,該怎么辦?這就是為什么需要一個額外的ICollection/ IRepository來實現它們。
可變性是需要關注的東西。
* 其次,熱切的求值計算:
~~~
$values = \Pinq\Collection::from(range(1, 10));
$values->apply(function (&$number) { $number *= 10; });
~~~
為什么要這樣熱切地求值?如果不需要立即執行這些值的計算,那么顯然會更好一些,也可以在迭代中進行求值。嗯,不,回到與外部數據源相同的點,如果你開始調用:
~~~
$values->apply(function (&$number) { $number *= 10; });
~~~
而$values不是一個ICollection底層為數組的實例,而是IRepository查詢一個flatfile(無格式文件)。但是由于這些值從未被迭代,所以查詢將永遠不會執行,并且您的文件數據將保持不變。這顯然是走下坡路的錯誤道路。如果這是您正試圖的行為,您應該使用select。
這就是為什么特殊的ICollection/IRepository 接口實例的查詢都是熱切求值的。