生成器与迭代器
本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 62 课,共 91 课。
生成器提供了一种简单的方法,无需实现完整的 Iterator 接口即可创建迭代器。生成器函数使用 yield 而不是 return 来逐个生成值,并在每个值之间暂停执行。
这是一个生成数字序列的基本生成器:
<?php
function countTo(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(3) as $number) {
echo "$number\n";
}
输出:
1
2
3主要优势是节省内存。生成器不会在内存中创建整个数组,而是按需生成值。处理大型数据集时,这一点至关重要。处理 100 万条记录的生成器,使用的内存与处理 10 条记录的生成器相同。
生成器也可以生成键值对:
<?php
function userGenerator(): Generator {
yield 'admin' => 'Alice';
yield 'editor' => 'Bob';
}
foreach (userGenerator() as $role => $name) {
echo "$role: $name\n";
}
输出:
admin: Alice
editor: Bob要让你自己的类可以使用 foreach 进行迭代,请实现 Iterator 接口。不过,在大多数情况下,生成器能提供更简洁的解决方案,所需的样板代码也更少。当一个类需要提供一组项目,又不想一次性将所有内容加载到内存中时,生成器在面向对象编程中尤其有用。
挑战
简单让我们构建一个产品库存系统,使用生成器高效地遍历大型集合,而无需一次性将所有内容加载到内存中。你将创建一个存储产品的类,并提供生成器方法,以不同方式遍历这些产品。
你将把代码分为 3 个文件:
Product.php:创建一个表示库存中某个商品的Product类。使用构造函数属性提升来定义公共$sku(字符串)、公共$name(字符串)和公共$price(浮点数)。Inventory.php:创建一个管理产品集合并提供生成器方法来遍历产品的Inventory类。引入 Product 文件。这个类应该:- 具有一个用于存储产品的私有数组属性
- 具有一个
addProduct(Product $product)方法,用于将产品添加到集合中 - 具有一个
allProducts(): Generator生成器方法,每次生成一个产品 - 具有一个
expensiveProducts(float $minPrice): Generator生成器方法,只生成价格大于或等于给定最低价格的产品,并生成键值对,其中键是 SKU,值是产品
main.php:引入 Inventory 文件。你会收到一个输入:最低价格阈值。创建一个
Inventory,并添加以下 3 个产品:- SKU:
"A001",名称:"Laptop",价格:999.99 - SKU:
"A002",名称:"Mouse",价格:29.99 - SKU:
"A003",名称:"Monitor",价格:349.99
首先,遍历
allProducts(),并将每个产品的名称单独打印在一行上。然后打印一个空行,接着使用输入的阈值遍历
expensiveProducts()。对于每个价格较高的产品,在单独一行上打印"[sku]: [name] - $[price]"。价格格式必须恰好保留 2 位小数。- SKU:
这个挑战展示了生成器如何让你延迟遍历集合:只有在需要时才会生成每个产品,因此这种方法在处理大型库存时能节省内存。expensiveProducts() 中的键值生成展示了生成器如何提供结构化的遍历数据。
亲手试试
<?php
require_once 'Inventory.php';
// 读取输入
$minPrice = floatval(trim(fgets(STDIN)));
// TODO:创建一个 Inventory 实例
// TODO:添加 3 个商品:
// SKU:"A001",名称:"Laptop",价格:999.99
// SKU:"A002",名称:"Mouse",价格:29.99
// SKU:"A003",名称:"Monitor",价格:349.99
// TODO:遍历 allProducts(),打印每个商品的名称
// TODO:打印一个空行
// TODO:使用 $minPrice 遍历 expensiveProducts()
// 对每个高价商品,打印 "[sku]: [name] - $[price]"
// 将价格格式化为恰好 2 位小数
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器