PHP迭代器学习
yield关键字
$arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
$arr2 = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
function demo ($arr) {
foreach ($arr as $value) {
yield $value;
}
}
$obj1 = demo($arr1);
$obj2 = demo($arr2);
while (true) {
// $obj1->key(), $obj2->key() 为零
$value1 = $obj1->current();
$value2 = $obj2->current();
$value = $value1 * $value2;
echo $value.'<br>';
// $obj1->key(), $obj2->key() 加1
$obj1->next();
$obj2->next();
// 是否完成迭代(obj->next()后程序找不到yield,既为失效)
if (!$obj1->valid() || !$obj2->valid()) {
break;
}
}
输出结果:10 40 90 160 250 360 490 640 810 1000
生成器的方法
current()
返回当前产生的值,即是yield返回给生成器的值key()
返回当前产生的键,当yield返回的不是键值对的时候,默认使用索引键next()
生成器继续执行,通过调用生成器的next()
来继续执行被yield阻塞的后面的代码,没有返回值rewind()
重置迭代器,如果迭代已经开始,调用该方法会抛出一个异常,每次调用生成器函数生成生成器的时候会自动调用(生成迭代对象的时候已经隐含地执行了rewind操作)一次rewind()
,没有返回值valid()
检查迭代器是否被关闭,即迭代是否已经结束,返回true/falsesend()
向生成器中传入一个值,并将这个值传递给当前yield,替换整个yield表达式,然后继续执行生成器(无须显式调用next()
触发程序继续执行),返回下一个yield的返回值。
说明
如果你调用->rewind(), 代码就会运行到控制流第一次出现yield的地方. 而函数内传递给yield语句的返回值可以通过$range->current()获取.
为了继续执行生成器中yield后的代码, 你就需要调用next()方法. 这将再次启动生成器, 直到下一次yield语句出现. 因此,连续调用next()和current()方法, 你就能从生成器里获得所有的值, 直到再没有yield语句出现.
yield替换
- yield两边括号在send时参数替换
- yield表达式两边的括号在PHP7以前不是可选的, 也就是说在PHP5.5和PHP5.6中圆括号是必须的.
function gen() {
$ret = (yield 'yield1');
var_dump($ret);
$ret = (yield 'yield2');
var_dump($ret);
}
$gen = gen();
var_dump($gen->current()); // string(6) "yield1"
var_dump($gen->send('ret1')); // string(4) "ret1" (the first var_dump in gen)
// string(6) "yield2" (the var_dump of the ->send() return value)
var_dump($gen->send('ret2')); // string(4) "ret2" (again from within gen)
// NULL (the return value of ->send())
任务轻量级协程函数
<?php
class Task {
protected $taskId;
protected $coroutine;
protected $sendValue = null;
protected $beforeFirstYield = true;
public function __construct($taskId, Generator $coroutine) {
$this->taskId = $taskId;
$this->coroutine = $coroutine;
}
public function getTaskId() {
return $this->taskId;
}
public function setSendValue($sendValue) {
$this->sendValue = $sendValue;
}
public function run() {
if ($this->beforeFirstYield) {
$this->beforeFirstYield = false;
return $this->coroutine->current();
} else {
$retval = $this->coroutine->send($this->sendValue);
$this->sendValue = null;
return $retval;
}
}
public function isFinished() {
return !$this->coroutine->valid();
}
}
<?php
class Scheduler {
protected $maxTaskId = 0;
protected $taskMap = []; // taskId => task
protected $taskQueue;
public function __construct() {
// php 内置队列
$this->taskQueue = new SplQueue();
}
public function newTask(Generator $coroutine) {
$tid = ++$this->maxTaskId;
$task = new Task($tid, $coroutine);
$this->taskMap[$tid] = $task;
$this->schedule($task);
return $tid;
}
public function schedule(Task $task) {
// 加入队列
$this->taskQueue->enqueue($task);
}
public function run() {
while (!$this->taskQueue->isEmpty()) {
// 取出队列
$task = $this->taskQueue->dequeue();
$task->run();
// 任务是否valid ?
if ($task->isFinished()) {
unset($this->taskMap[$task->getTaskId()]);
} else {
// 加入队列继续
$this->schedule($task);
}
}
}
}
调用:
$scheduler = new Scheduler;
$scheduler->newTask(task1());
$scheduler->newTask(task2());
$scheduler->run();