php有很多功能强大的接口,其中ArrayAccess 与 Iterator 的配合使用可以让对象与数组一样有着灵活的访问性。
当然,用ArrayAccess 与 Iterator 配合可以用来对付数组,但还有一个更好的办法同则SPL 提供的ArrayIterator
原因就是 :
ArrayIterator implement ArrayAccess, SeekableIterator, Countable, Searializable {}
而接下来要介绍的则是Iterator的更高一层用法。
与Iterator有关的函数先记录下
iterator_to_array() 把迭代器中的元素转换成数组
IteratorAggregate::getIterator() 调用一个外部迭代器
ArrayIterator
Iteartor_count() 等等
而php在使用Iterator接口则是迭代器模式的一种实现。在这里,其中的概念客户端(实现迭代过程)、迭代器、具体迭代器则会别对应于,foreach() , 继承于iterator接口的具体类和需要遍历的数组或集合。
而生成器,则是建立在理解迭代器的基础之上。
php中的生成器,可以叫做迭代生成器,因为它就是一个不可new的类,同时继承Iterator,且多了一个send() 与 生成器通信
它的实现则是通过yield关键字,或语句,或表达式,其工作方式则是,使用yield的结构体就是一个生成生成器类,当执行到yield
时,则中断该生成器,并存储其状态,当再次执行(foreach 或 while等循环结构) , 则会恢复其状态,并直到再次遇到yield
<?php function gen() { $ret = (yield 'yield1'); var_dump($ret); $ret = (yield 'yield2'); var_dump($ret); } $gen = gen(); var_dump($gen->current()); // output:string(6) "yield1" 当该生成器形成的时候 //rewind()就已经隐式的执行,即生成就已经到第一个yield中断了 var_dump($gen->send('ret1')); // output:string(4) "ret1" (the first var_dump in gen) // 这时,send()则做了它该做的,恢复中断,把值(ret1)传入yield,并返回yield(ret1), 直到再遇到yield ,无则返回null // output:string(6) "yield2" (the var_dump of the ->send() return value) // 这时执行到$ret = (yields 'yield2'); 时则中磁芯,并把yield表达式的值返回,
//此时为 yield2 ,若没有 后面的 ‘yield2’, 则会返回null var_dump($gen->send('ret2')); //output:string(6) "yield2" (the var_dump of the ->send() return value)
// output:null
// 这时send()执行的时候 ,并没有下一个yield则返回的是null,而其恢复执行后在函数体内有一个var_dump(),所以会有output
其实,由上面的实例可总结出两点 ,
一是,初始化生成器时则已经到了一个yield,形成了中断,
二是,send 的执行实际上是,先next() , 再vaild() , 不能过则return null, 通过则current() ,返回,若是yield 后没有“默认”($ret = (yield 'default');),
则返回的是null,再进行中断,直至再次恢复。
理解了它是如何工作的,则出现了一个实际的问题,它有什么用呢?
生成器的高级使用出现在“在php中使用协程实现多任务调度”这一主题中,该主题偏难,而我对它的理解也只是到了简单的任务调度这一块
而更高级的内容,再慢慢了解。
为什么它能完成任务的调度呢?关于这一点可类比操作系统中的程序中断,在那里,中断的作用则就是为了任务调度。
如何使用yield来完成任务调度,这一