<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/language.generators.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'zh',
  ),
  'this' => 
  array (
    0 => 'language.generators.syntax.php',
    1 => '生成器语法',
    2 => '生成器语法',
  ),
  'up' => 
  array (
    0 => 'language.generators.php',
    1 => '生成器',
  ),
  'prev' => 
  array (
    0 => 'language.generators.overview.php',
    1 => '生成器总览',
  ),
  'next' => 
  array (
    0 => 'language.generators.comparison.php',
    1 => '生成器与 Iterator 对象的比较',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'zh',
    'path' => 'language/generators.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="language.generators.syntax" class="sect1">
  <h2 class="title">生成器语法</h2>

  <p class="para">
   生成器函数看起来像普通函数——不同的是普通函数返回一个值，而生成器可以 <a href="language.generators.syntax.php#control-structures.yield" class="link"><code class="literal">yield</code></a> 生成多个想要的值。
   任何包含 <a href="language.generators.syntax.php#control-structures.yield" class="link"><code class="literal">yield</code></a> 的函数都是一个生成器函数。
  </p>

  <p class="para">
   当一个生成器被调用的时候，它返回一个可以被遍历的对象.当你遍历这个对象的时候(例如通过一个<a href="control-structures.foreach.php" class="link"><code class="literal">foreach</code></a>循环)，PHP  将会在每次需要值的时候调用对象的遍历方法，并在产生一个值之后保存生成器的状态，这样它就可以在需要产生下一个值的时候恢复调用状态。
  </p>

  <p class="para">
   一旦不再需要产生更多的值，生成器可以简单返回，而调用生成器的代码还可以继续执行，就像一个数组已经被遍历完了。
  </p>

  <blockquote class="note"><p><strong class="note">注意</strong>: 
   <p class="para">
    生成器能够返回多个值，通过 <span class="methodname"><a href="generator.getreturn.php" class="methodname">Generator::getReturn()</a></span> 可以获取到。
   </p>
  </p></blockquote>

  <div class="sect2" id="control-structures.yield">
   <h3 class="title"><strong class="command">yield</strong> 关键字</h3>
 
   <p class="para">
    生成器函数的核心是<strong class="command">yield</strong>关键字。它最简单的调用形式看起来像一个return申明，不同之处在于普通return会返回值并终止函数的执行，而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
   </p>

   <div class="example" id="example-1">
    <p><strong>示例 #1 一个简单的生成值的例子</strong></p>
    <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">gen_one_to_three</span><span style="color: #007700">() {<br />    for (</span><span style="color: #0000BB">$i </span><span style="color: #007700">= </span><span style="color: #0000BB">1</span><span style="color: #007700">; </span><span style="color: #0000BB">$i </span><span style="color: #007700">&lt;= </span><span style="color: #0000BB">3</span><span style="color: #007700">; </span><span style="color: #0000BB">$i</span><span style="color: #007700">++) {<br />        </span><span style="color: #FF8000">//注意变量$i的值在不同的yield之间是保持传递的。<br />        </span><span style="color: #007700">yield </span><span style="color: #0000BB">$i</span><span style="color: #007700">;<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$generator </span><span style="color: #007700">= </span><span style="color: #0000BB">gen_one_to_three</span><span style="color: #007700">();<br />foreach (</span><span style="color: #0000BB">$generator </span><span style="color: #007700">as </span><span style="color: #0000BB">$value</span><span style="color: #007700">) {<br />    echo </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$value</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例会输出：</p></div>
    <div class="example-contents screen">
<div class="cdata"><pre>
1
2
3
</pre></div>
    </div>
   </div>

   <blockquote class="note"><p><strong class="note">注意</strong>: 
    <p class="para">
     在内部会为生成的值配对连续的整型索引，就像一个非关联的数组。
    </p>
   </p></blockquote>

   <div class="sect3" id="control-structures.yield.associative">
    <h4 class="title">指定键名来生成值</h4>
    
    <p class="para">
     PHP的数组支持关联键值对数组，生成器也一样支持。所以除了生成简单的值，你也可以在生成值的时候指定键名。
    </p>
    
    <p class="para">
     如下所示，生成一个键值对与定义一个关联数组十分相似。
    </p>
    
    <div class="example" id="example-2">
     <p><strong>示例 #2 生成一个键值对</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/* <br /> * 下面每一行是用分号分割的字段组合，第一个字段将被用作键名。<br /> */<br /><br /></span><span style="color: #0000BB">$input </span><span style="color: #007700">= &lt;&lt;&lt;'EOF'<br /></span><span style="color: #DD0000">1;PHP;Likes dollar signs<br />2;Python;Likes whitespace<br />3;Ruby;Likes blocks<br /></span><span style="color: #007700">EOF;<br /><br />function </span><span style="color: #0000BB">input_parser</span><span style="color: #007700">(</span><span style="color: #0000BB">$input</span><span style="color: #007700">) {<br />    foreach (</span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">"\n"</span><span style="color: #007700">, </span><span style="color: #0000BB">$input</span><span style="color: #007700">) as </span><span style="color: #0000BB">$line</span><span style="color: #007700">) {<br />        </span><span style="color: #0000BB">$fields </span><span style="color: #007700">= </span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">';'</span><span style="color: #007700">, </span><span style="color: #0000BB">$line</span><span style="color: #007700">);<br />        </span><span style="color: #0000BB">$id </span><span style="color: #007700">= </span><span style="color: #0000BB">array_shift</span><span style="color: #007700">(</span><span style="color: #0000BB">$fields</span><span style="color: #007700">);<br /><br />        yield </span><span style="color: #0000BB">$id </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">;<br />    }<br />}<br /><br />foreach (</span><span style="color: #0000BB">input_parser</span><span style="color: #007700">(</span><span style="color: #0000BB">$input</span><span style="color: #007700">) as </span><span style="color: #0000BB">$id </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">) {<br />    echo </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$id</span><span style="color: #DD0000">:\n"</span><span style="color: #007700">;<br />    echo </span><span style="color: #DD0000">"    </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #0000BB">0</span><span style="color: #007700">]</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br />    echo </span><span style="color: #DD0000">"    </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #0000BB">1</span><span style="color: #007700">]</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例会输出：</p></div>
     <div class="example-contents screen">
<div class="cdata"><pre>
1:
    PHP
    Likes dollar signs
2:
    Python
    Likes whitespace
3:
    Ruby
    Likes blocks
</pre></div>
     </div>
    </div>
   </div>

   <div class="sect3" id="control-structures.yield.null">
    <h4 class="title">生成 null 值</h4>
    
    <p class="para">
     Yield 可以在没有参数传入的情况下被调用来生成一个 <strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong> 值并配对一个自动的键名。
    </p>
    
    <div class="example" id="example-3">
     <p><strong>示例 #3 生成<strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong>s</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">gen_three_nulls</span><span style="color: #007700">() {<br />    foreach (</span><span style="color: #0000BB">range</span><span style="color: #007700">(</span><span style="color: #0000BB">1</span><span style="color: #007700">, </span><span style="color: #0000BB">3</span><span style="color: #007700">) as </span><span style="color: #0000BB">$i</span><span style="color: #007700">) {<br />        yield;<br />    }<br />}<br /><br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">iterator_to_array</span><span style="color: #007700">(</span><span style="color: #0000BB">gen_three_nulls</span><span style="color: #007700">()));<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例会输出：</p></div>
     <div class="example-contents screen">
<div class="cdata"><pre>
array(3) {
  [0]=&gt;
  NULL
  [1]=&gt;
  NULL
  [2]=&gt;
  NULL
}
</pre></div>
     </div>
    </div>
   </div>
   
   <div class="sect3" id="control-structures.yield.references">
    <h4 class="title">使用引用来生成值</h4>
    
    <p class="para">
     生成函数可以像使用值一样来使用引用生成。这个和<a href="functions.returning-values.php" class="link">从函数返回一个引用</a>一样：通过在函数名前面加一个引用符号。
    </p>
    
    <div class="example" id="example-4">
     <p><strong>示例 #4 使用引用来生成值</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">function &amp;</span><span style="color: #0000BB">gen_reference</span><span style="color: #007700">() {<br />    </span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">3</span><span style="color: #007700">;<br /><br />    while (</span><span style="color: #0000BB">$value </span><span style="color: #007700">&gt; </span><span style="color: #0000BB">0</span><span style="color: #007700">) {<br />        yield </span><span style="color: #0000BB">$value</span><span style="color: #007700">;<br />    }<br />}<br /><br /></span><span style="color: #FF8000">/* <br /> * 我们可以在循环中修改 $number 的值，而生成器是使用的引用值来生成，所以 gen_reference() 内部的 $value 值也会跟着变化。<br /> */<br /></span><span style="color: #007700">foreach (</span><span style="color: #0000BB">gen_reference</span><span style="color: #007700">() as &amp;</span><span style="color: #0000BB">$number</span><span style="color: #007700">) {<br />    echo (--</span><span style="color: #0000BB">$number</span><span style="color: #007700">).</span><span style="color: #DD0000">'... '</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例会输出：</p></div>
     <div class="example-contents screen">
<div class="cdata"><pre>
2... 1... 0...
</pre></div>
     </div>
    </div>
   </div>

   <div class="sect3" id="control-structures.yield.from">
    <h4 class="title">可用的 <strong class="command">yield from</strong> 生成器委托</h4>

    <p class="para">
     生成器委托允许使用 <strong class="command">yield from</strong> 关键字从另外一个生成器、
     <span class="classname"><a href="class.traversable.php" class="classname">Traversable</a></span> 对象、<span class="type"><a href="language.types.array.php" class="type array">array</a></span> 通过生成值。
     外部生成器将从内部生成器、object、array 中生成所有的值，直到它们不再有效，
     之后将在外部生成器中继续执行。
    </p>

    <p class="para">
     如果生成器与 <strong class="command">yield from</strong> 一起使用，那么
     <strong class="command">yield from</strong> 表达式将返回内部生成器返回的任何值。
    </p>

    <div class="caution"><strong class="caution">警告</strong>
      <h1 class="title">存储到 array （例如使用 <span class="function"><a href="function.iterator-to-array.php" class="function">iterator_to_array()</a></span>）</h1>

      <p class="para">
       <strong class="command">yield from</strong> 不能重置 key。它保留 <span class="classname"><a href="class.traversable.php" class="classname">Traversable</a></span>
       对象或者 <span class="type"><a href="language.types.array.php" class="type array">array</a></span> 返回的 key。因此，某些值可能会与其他的 <strong class="command">yield</strong> 或者 
       <strong class="command">yield from</strong> 共享公共的 key，因此，在插入数组时将会用这个 key 覆盖以前的值。
      </p>

      <p class="para">
       一个非常重要的常见情况是 <span class="function"><a href="function.iterator-to-array.php" class="function">iterator_to_array()</a></span> 默认返回带 key 的 array ，
       这可能会造成无法预料的结果。 <span class="function"><a href="function.iterator-to-array.php" class="function">iterator_to_array()</a></span> 还有第二个参数 
       <code class="parameter">preserve_keys</code> ，可以设置为 <strong><code><a href="reserved.constants.php#constant.false">false</a></code></strong> 来收集 <span class="classname"><a href="class.generator.php" class="classname">Generator</a></span>
       返回的不带 key 的所有值。
      </p>

      <div class="example" id="example-5">
       <p><strong>示例 #5 使用 <span class="function"><a href="function.iterator-to-array.php" class="function">iterator_to_array()</a></span> 的 <strong class="command">yield from</strong> </strong></p>
       <div class="example-contents">
 <div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /> </span><span style="color: #007700">function </span><span style="color: #0000BB">inner</span><span style="color: #007700">() {<br />     yield </span><span style="color: #0000BB">1</span><span style="color: #007700">; </span><span style="color: #FF8000">// key 0<br />     </span><span style="color: #007700">yield </span><span style="color: #0000BB">2</span><span style="color: #007700">; </span><span style="color: #FF8000">// key 1<br />     </span><span style="color: #007700">yield </span><span style="color: #0000BB">3</span><span style="color: #007700">; </span><span style="color: #FF8000">// key 2<br /> </span><span style="color: #007700">}<br /> function </span><span style="color: #0000BB">gen</span><span style="color: #007700">() {<br />     yield </span><span style="color: #0000BB">0</span><span style="color: #007700">; </span><span style="color: #FF8000">// key 0<br />     </span><span style="color: #007700">yield from </span><span style="color: #0000BB">inner</span><span style="color: #007700">(); </span><span style="color: #FF8000">// keys 0-2<br />     </span><span style="color: #007700">yield </span><span style="color: #0000BB">4</span><span style="color: #007700">; </span><span style="color: #FF8000">// key 1<br /> </span><span style="color: #007700">}<br /> </span><span style="color: #FF8000">// 传递 false 作为第二个参数获得数组 [0, 1, 2, 3, 4]<br /> </span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">iterator_to_array</span><span style="color: #007700">(</span><span style="color: #0000BB">gen</span><span style="color: #007700">()));<br /> </span><span style="color: #0000BB">?&gt;</span></span></code></div>
        </div>

        <div class="example-contents"><p>以上示例会输出：</p></div>
        <div class="example-contents screen">
 <div class="cdata"><pre>
 array(3) {
   [0]=&gt;
   int(1)
   [1]=&gt;
   int(4)
   [2]=&gt;
   int(3)
 }
 </pre></div>
        </div>
       </div>
     </div>

    <div class="example" id="example-6">
     <p><strong>示例 #6 <strong class="command">yield from</strong> 的基本用法</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">count_to_ten</span><span style="color: #007700">() {<br />    yield </span><span style="color: #0000BB">1</span><span style="color: #007700">;<br />    yield </span><span style="color: #0000BB">2</span><span style="color: #007700">;<br />    yield from [</span><span style="color: #0000BB">3</span><span style="color: #007700">, </span><span style="color: #0000BB">4</span><span style="color: #007700">];<br />    yield from new </span><span style="color: #0000BB">ArrayIterator</span><span style="color: #007700">([</span><span style="color: #0000BB">5</span><span style="color: #007700">, </span><span style="color: #0000BB">6</span><span style="color: #007700">]);<br />    yield from </span><span style="color: #0000BB">seven_eight</span><span style="color: #007700">();<br />    yield </span><span style="color: #0000BB">9</span><span style="color: #007700">;<br />    yield </span><span style="color: #0000BB">10</span><span style="color: #007700">;<br />}<br /><br />function </span><span style="color: #0000BB">seven_eight</span><span style="color: #007700">() {<br />    yield </span><span style="color: #0000BB">7</span><span style="color: #007700">;<br />    yield from </span><span style="color: #0000BB">eight</span><span style="color: #007700">();<br />}<br /><br />function </span><span style="color: #0000BB">eight</span><span style="color: #007700">() {<br />    yield </span><span style="color: #0000BB">8</span><span style="color: #007700">;<br />}<br /><br />foreach (</span><span style="color: #0000BB">count_to_ten</span><span style="color: #007700">() as </span><span style="color: #0000BB">$num</span><span style="color: #007700">) {<br />    echo </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$num</span><span style="color: #DD0000"> "</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例会输出：</p></div>
     <div class="example-contents screen">
<div class="cdata"><pre>
1 2 3 4 5 6 7 8 9 10
</pre></div>
     </div>
    </div>

    <div class="example" id="example-7">
     <p><strong>示例 #7 <strong class="command">yield from</strong> 并返回多个值</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">count_to_ten</span><span style="color: #007700">() {<br />    yield </span><span style="color: #0000BB">1</span><span style="color: #007700">;<br />    yield </span><span style="color: #0000BB">2</span><span style="color: #007700">;<br />    yield from [</span><span style="color: #0000BB">3</span><span style="color: #007700">, </span><span style="color: #0000BB">4</span><span style="color: #007700">];<br />    yield from new </span><span style="color: #0000BB">ArrayIterator</span><span style="color: #007700">([</span><span style="color: #0000BB">5</span><span style="color: #007700">, </span><span style="color: #0000BB">6</span><span style="color: #007700">]);<br />    yield from </span><span style="color: #0000BB">seven_eight</span><span style="color: #007700">();<br />    return yield from </span><span style="color: #0000BB">nine_ten</span><span style="color: #007700">();<br />}<br /><br />function </span><span style="color: #0000BB">seven_eight</span><span style="color: #007700">() {<br />    yield </span><span style="color: #0000BB">7</span><span style="color: #007700">;<br />    yield from </span><span style="color: #0000BB">eight</span><span style="color: #007700">();<br />}<br /><br />function </span><span style="color: #0000BB">eight</span><span style="color: #007700">() {<br />    yield </span><span style="color: #0000BB">8</span><span style="color: #007700">;<br />}<br /><br />function </span><span style="color: #0000BB">nine_ten</span><span style="color: #007700">() {<br />    yield </span><span style="color: #0000BB">9</span><span style="color: #007700">;<br />    return </span><span style="color: #0000BB">10</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">$gen </span><span style="color: #007700">= </span><span style="color: #0000BB">count_to_ten</span><span style="color: #007700">();<br />foreach (</span><span style="color: #0000BB">$gen </span><span style="color: #007700">as </span><span style="color: #0000BB">$num</span><span style="color: #007700">) {<br />    echo </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$num</span><span style="color: #DD0000"> "</span><span style="color: #007700">;<br />}<br />echo </span><span style="color: #0000BB">$gen</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getReturn</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例会输出：</p></div>
     <div class="example-contents screen">
<div class="cdata"><pre>
1 2 3 4 5 6 7 8 9 10
</pre></div>
     </div>
    </div>
   </div>
  </div>
 </div><?php manual_footer($setup); ?>