ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## 延迟求值(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 接口实例的查询都是热切求值的。