## 延迟求值(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 接口实例的查询都是热切求值的。