招聘

标签(空格分隔): 招聘 PHP 国贸


语言基础

foreach 语法结构提供了遍历数组的简单方式。

php5之前, foreach仅能用于数组
php5+, 利用foreach可以遍历对象

foreach仅能够应用于数据和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。

有两种语法:

/*遍历给定的 array_expression 数据。每次循环中, 当前单元的值被赋给$value并且数组内部的指针向前移一步(因此下次循环中将会得到下一个单元)
*/
foreach (array_expression as $value) {// statement
}foreach (array_expression as $value) :// statement
endforeach;
/*同上,只除了当前单元格的键名也会在每次循环中被赋给变量$key
*/
foreach (array_expression as $key => $value) {// statement
}foreach (array_expression as $key => $value) :// statement
endforeach;

还能够自定义遍历对象!

foreach开始执行时, 数组内部的指针会自动指向第一个单元. 这意味着不需要在foreach循环之前调用reset()
由于foreach依赖内部数组指针, 在循环中修改其值将可能导致意外的行为

可以很容易通过在 $value 之前加上 & 来修改数组元素. 此方法将以引用 赋值, 而不是拷贝一个值.

<?php$arr = [1, 2, 3, 4];
foreach($arr as &$value) {$value = $value * 2;
}// $arr is now [2, 4, 6, 8]
unset($value); // 最后取消掉引用

$value的引用仅在被遍历的数组可以被引用时才可用(例如是个变量)。

以下代码无法运行:


<?php
/*此段代码可以运行运行结果:1-22-43-64-8
*/
foreach (array(1, 2, 3, 4) as &$value) {echo $value, '-';$value = $value * 2;echo $value, PHP_EOL;
}

Warning: 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。

Note: foreach 不支持用 @ 来抑制错误信息的能力

foreach 虽然简单, 不过它可能出现一些意外行为, 特别是代码涉及到引用的时候。

问题研究

问题一: 如下代码运行结果为何不是 2/4/6 ?

<?php
$arr = [1, 2, 3];foreach ($arr as $k => &$v) {$v = $v * 2;
}foreach ($arr as $k => $v) {echo $v, PHP_EOL;
}/*
输出:244
*/

我们可以认为 foreach($arr as &$v) 结构隐含了如下操作, 分别将数组当前的 赋值给 $k$v. 具体展开形如:


<?php
foreach ($arr as $k => $v) {$k = currentKey();$v = currentVal();// 继续运行用户代码
} 

根据上述理论, 现在我们重新来分析下第一个foreach:

循环 备注 $arr值
循环 1-1 由于$v是一个引用, 因此 $v = &$arr[0], $v = $v * 2 相当于 $arr[0] * 2 [2, 2, 3]
循环 1-2 $v = &$arr[1] [2, 4, 3]
循环 1-3 $v = &$arr[2] [2, 4, 6]
循环 2-1 隐含操作 $v = $arr[0] 被触发, 由于此时 $v 仍是 $arr[2] 的引用, 相当于 $arr[2] = $arr[0] [2, 4, 2]
循环 2-2 $v = $arr[1], 即$arr[2] = $arr[1] [2, 4, 4]
循环 2-3 $v = $arr[2], 即$arr[2] = $arr[2] [2, 4, 4]

如何解决此类问题呢? PHP手册上有一段提醒:

Warning: 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。

<?php
$arr = [1, 2, 3];foreach ($arr as $k => &$v) {$v = $v * 2;
}
unset($v);
foreach ($arr as $k => $v) {echo $v, PHP_EOL;
}/*
输出:246
*/

从这个问题可以看出, 引用很可能会伴随副作用。如果不希望无意识的修改导致数据内容变更, 最好及时unset掉这些引用。

问题二: 如下代码运行结果为何不是 0=>a 1=>b 2=>c

<?php
$arr = ['a', 'b', 'c'];foreach ($arr as $k => $v) {echo key($arr), "=>", current($arr), PHP_EOL;
}foreach ($arr as $k => &$v) {echo key($arr), "=>", current($arr), PHP_EOL;
}
/*
#php5.6
1=>b 1=>b 1=>b
1=>b 2=>c =>#php7
0=>a 0=>a 0=>a
0=>a 0=>a 0=>a
*/

按照手册中的说法, key和current分别是获取数据中当前元素的键值。
那为何 key($arr) 一直是0,current($arr) 一直是'a'呢?

先用vld查看编译后的 opcode:


➜  demo /usr/local/Cellar/php/7.2.7/bin/php -dvld.active=1 a.php
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 77) Position 1 = 2, Position 2 = 15
Branch analysis from position: 2
Jump found. (Code = 78) Position 1 = 3, Position 2 = 15
Branch analysis from position: 3
Jump found. (Code = 42) Position 1 = 2
Branch analysis from position: 2
Branch analysis from position: 15
Jump found. (Code = 62) Position 1 = -2
Branch analysis from position: 15
filename:       /Users/jianyong/demo/a.php
function name:  (null)
number of ops:  17
compiled vars:  !0 = $arr, !1 = $v, !2 = $k
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------2     0  E >   ASSIGN                                                   !0, <array>4     1      > FE_RESET_R                                       $4      !0, ->152    > > FE_FETCH_R                                       ~5      $4, !1, ->153    >   ASSIGN                                                   !2, ~55     4        INIT_FCALL                                               'key'5        SEND_VAR                                                 !06        DO_ICALL                                         $77        ECHO                                                     $78        ECHO                                                     '%3D%3E'9        INIT_FCALL                                               'current'10        SEND_VAR                                                 !011        DO_ICALL                                         $812        ECHO                                                     $813        ECHO                                                     '%0A'14      > JMP                                                      ->215    >   FE_FREE                                                  $47    16      > RETURN                                                   1branch: #  0; line:     2-    4; sop:     0; eop:     1; out1:   2; out2:  15
branch: #  2; line:     4-    4; sop:     2; eop:     2; out1:   3; out2:  15
branch: #  3; line:     4-    5; sop:     3; eop:    14; out1:   2
branch: # 15; line:     5-    7; sop:    15; eop:    16; out1:  -2
path #1: 0, 2, 3, 2, 15,
path #2: 0, 2, 15,
path #3: 0, 15,
0=>a
0=>a
0=>a

PHP7新特性之foreach

  • [x] foreach 循环对数组内部指针不再起作用, 在PHP7之前, 当数据通过foreach迭代时, 数组指针会移动。
<?php
$array = [0, 1, 2];
foreach ($array as &$val) {var_dump(current($array));
}
版本 结果 说明
PHP5 int(1) int(2) bool(false) 数组指针会移动
PHP7 int(0) int(0) int(0) 数据指针不再移动
  • [x] 按照值进行循环时, 对数组的修改是不会影响循环。

foreach按照值进行循环的时候(by-value), foreach是对该数组的一个拷贝进行操作. 所以在循环过程中修改不影响循环结果

<?php
$arr = [0, 1, 2];
$ref = &$arr;foreach ($arr as $val) {var_dump($val);unset($arr[1]);
}
版本 结果 说明
PHP5 int(0) int(2) 会将unset的数据跳过
PHP7 int(0) int(1) int(2) 对数组的改动不影响循环
  • [x] 按照引用进行循环的时候, 对数组的修改会影响循环
<?php
$arr = [0, 1, 2];
$ref = &$arr;foreach ($arr as &$val) {var_dump($val);unset($arr[1]);
}
版本 结果
PHP5 int(0) int(2)
PHP7 int(0) int(2)
  • [x] 对简单对象plain(non-Traversable)的循环

在简单对象的循环, 不管是按照值循环还是引用循环, 和按照引用对数组循环的行为是一样的, 不过对位置的管理会更加精确

  • [x] 对迭代对象(Traversable objects)对象行为和之前一致

stackoverflow 上面的解释, Traversable objects is one that implements Iterator or IteratorAggregate interface

如果一个对象实现了 Iterator 或者 IteratorAggregate 接口, 即可称之为迭代对象

参考

  • https://wiki.php.net/rfc/php7...
  • 97fe15db4356f8fa1b3b8eb9bb1baa8141376077

原文地址:https://segmentfault.com/a/1190000015753252

转载于:https://www.cnblogs.com/lalalagq/p/9979719.html

深入理解PHP之foreach相关推荐

  1. Scala中的foreach方法和map方法

    两个方法的共同点在于(foreach和map):都是用于遍历集合对象,并对每一项执行指定的方法. 而两者的差异在于:foreach无返回值(准确说返回void),map返回集合对象. 结论就是:for ...

  2. php 跳出当前foreach,forEach方法跳出循环

    前文 没怎么理解JavaScript的forEach方法的童鞋使用forEach时很容易遇到一个问题就是,当你想要跳出这个循环时,发现return.break是不起作用的.下面我们来分析一下原因以及解 ...

  3. [C#基础知识系列]专题十二:迭代器

    引言: 在C# 1.0中我们经常使用foreach来遍历一个集合中的元素,然而一个类型要能够使用foreach关键字来对其进行遍历必须实现IEnumerable或IEnumerable<T> ...

  4. php中处理xml文件的类 simpleXML

    1 <?php 2 header("Content-Type:text/html;charset=utf-8");//此处必须输出html编码格式,虽然后面会输出xml的编码 ...

  5. 初学Java Web(9)——学生管理系统(简易版)总结

    技术准备 这个项目是自己用于巩固 J2EE 相关知识的练手项目,非常简单,但是相关的功能却非常实用,所以在这里分享一下 为了完成这个项目,需要掌握如下技术: Java 基础知识 前端: HTML, C ...

  6. 学生管理系统(SSM简易版)总结

    2019独角兽企业重金招聘Python工程师标准>>> 技术准备 为了完成这个项目,需要掌握如下技术: Java 基础知识 前端: HTML, CSS, JAVASCRIPT, JQ ...

  7. Lambda 表达式详解~Lambda与集合

    我们先从最熟悉的*Java集合框架(Java Collections Framework, JCF)*开始说起. 为引入Lambda表达式,Java8新增了java.util.funcion包,里面包 ...

  8. 四万字 Lambda 表达式完整教程(强烈建议收藏)

    点击关注公众号,利用碎片时间学习 Java Lambda表达式的一个重要用法是简化某些匿名内部类(Anonymous Classes)的写法.实际上Lambda表达式并不仅仅是匿名内部类的语法糖,JV ...

  9. 韦东山 嵌入式Linux应用开发基础知识 上【gcc makefile 输入设备

    1 main的输入参数,并且在命令行运行文件的时候输入 我的练习 先写了个单纯输出的hello 按照教程里那样写hello 所以gcc编译过程应该是 先创建一个.c文件 gcc -c -o hello ...

最新文章

  1. 2017-9-13:学习笔记
  2. java泛型反映调用方法体内类型引用问题
  3. picf509c语言程序,樊媛媛c语言程序设计09编译预处理.pptx
  4. ajax连接云数据库密码,ajax和数据库连接
  5. 数字效率Evernote超效率数字笔记术
  6. 技术情报局(笛卡尔树)
  7. java abstractrequest,Java AbstractJackson2HttpMessageConverter類代碼示例
  8. [vue] 怎么在vue中使用插件?
  9. 帆软独家:数字化转型打造企业数据战斗力
  10. python-pymysql模块的使用入门
  11. 7-2 合并两个有序数组为新的有序数组 (15 分)
  12. 附加:中半部分sql语句 区/县(数据表)
  13. 计算机辅助初中英语教学,利用多媒体优化初中英语课堂教学课题研究
  14. b站React禹哥版视频笔记-React面向组件编程(上)
  15. ubuntu 下载 wps 及 字体缺失问题
  16. antd的联级选择器异步调用编辑回显_react-uplod-img 是一个基于 React antd组件的图片上传组件...
  17. java定义数组变量初始化为0_java中怎么数组初始化?
  18. 微信营销系统(第三方微信平台)之微分销模块拓展
  19. 普通的视觉工程师的待遇是怎样的?
  20. 三星i908、iPhone3G对比评测

热门文章

  1. 【王道计组笔记】总线(4):总线操作和定时
  2. 【二分法】计蒜客:对数方程
  3. XBMC源代码分析 4:视频播放器(dvdplayer)-解码器(以ffmpeg为例)
  4. 开源数字媒体资产管理系统:Razuna
  5. 安卓手机上跑_直接在电脑上浏览操作安卓手机 #效率App #scrcpy
  6. SpringBoot接收数组参数
  7. jstl错误:According to TLD or attribute directive in tag file, attribute value does not accept any expr
  8. Navicat 解决方案之ORA-28547
  9. NYOJ105 - 九的余数
  10. mysql 判断日志时间早_MySQL5.7慢查询日志时间与系统时间差8小时原因详解