Unexpected behaviour of current() in a foreach loop
这是一个简单的循环
输出(演示)
1 2 3 | BBBB // Output for 5.2.4 - 5.5.0alpha4 BCD // Output for 4.4.1 AAAA // Output for 4.3.0 - 4.4.0, 4.4.2 - 5.2.3 |
问题:
- 有人能解释一下发生了什么事吗?
- 为什么我不明白ABCD
- 即使阵列的副本是由
foreach 制作的,我也应该得到AAAA ,但在当前的PHP 稳定版本中不能得到。
注*我知道我可以简单地使用
current — Return the current element in an array
The current() function simply returns the value of the array element that's currently being pointed to by the internal pointer. It does not move the pointer in any way. If the internal pointer points beyond the end of the elements list or the array is empty, current() returns FALSE.
更新1-新观察
多亏了DanielFigueroa:只需在函数中包装
1 2 3 4 5 6 7 |
输出(演示)
1 | BCDA // What the hell |
问题:
- 为什么不买"BBBB"?
- 函数中的包装电流如何影响
foreach 输出? - 额外的"A"是从哪里来的?
更新2
1 2 3 4 5 6 7 |
输出(见演示)
1 | AAAA // No longer BBBB when using a function |
问题:
- 在函数中运行循环并在函数外运行循环有什么不同,因为在大多数PHP版本中,函数中都有
AAAA outside和BBBB outside
为什么从B开始?
由于5.2
输出:
1 | B |
这可能与
为什么
在循环开始之前,
这种行为也表现为更具破坏性的
1 2 3 4 5 6 7 8 |
输出:
1 2 3 4 5 |
将循环变量传递给函数
这是另一个有趣的场景:
1 2 3 4 5 6 7 8 9 10 |
输出:
1 2 | BCDA bool(false) |
这一次,数组是按值传递的,因此它的数组结构(而不是元素)被复制到函数的
最后一个
这是迄今为止
这可能与在
那么,为什么在函数中放置EDOCX1[0]会使其不同呢?
遵循以下代码:
1 2 3 4 5 6 7 8 9 |
输出:
1 2 | AAAA string(1)"A" |
在这种情况下,使用数组的副本初始化
会更糟吗?
当然!当您开始使用引用或在同一个变量上嵌套多个
那么我怎样才能得到一致的结果呢?
使用
来自PHP.NET
The current() function simply returns the value of the array element
that's currently being pointed to by the internal pointer. It does not
move the pointer in any way
然后:使用next()。
1 2 3 4 5 |
注意:第一个元素不会被打印,因为foreach将指针移动到数组的第二个元素:)
此示例将解释完整的行为:
1 2 3 4 5 6 |
输出是ABCD
请注意:
1
2 As foreach relies on the internal array pointer changing it within the loop
may lead to unexpected behavior.foreach
编辑:我还想分享我的新发现!!!!
实例1:
1 2 3 4 5 6 7 |
输出:AAAA
实例2:
1 2 3 4 5 6 7 |
输出:ABCD
在foreach内调用current()函数时,即使是对另一个数组也会影响foreach的行为…
例3:
1 2 3 4 5 6 7 8 9 |
输出:ACD(哇!B失踪了)
例子:4
1 2 3 4 5 6 7 8 |
输出:BCD
无法确定foreach循环中会发生什么!!!!
我不知道这是为什么,但我怀疑这可能与如何评估/处理分配有关。为了好玩,我尝试了这个方法,结果导致了另一个
1 2 3 4 5 6 7 8 |
结果:BCDA
所以我猜这里发生的是,函数调用强制以一种正确的方式对电流进行评估。另外,我之所以得到BCDA而不是ABCD,可能是因为内部指针首先是递增的(指向b),然后在en中,它被重置回指向a。
在php文档中应该注意这一行:
Note that the assignment copies the original variable to the new one (assignment by value), so changes to one will not affect the other. This may also have relevance if you need to copy something like a large array inside a tight loop.
我想这不算是一个答案,但我喜欢你的问题,并想贡献一点。
如果是谎言,你使用的代码。即使从字面上看,它可能看起来像相同的代码,但是变量不是(http://3v4l.org/jainj)。
要回答您的实际问题,为了获得一致的结果,请使用正确的工具。
如果需要具有数组值的变量,请将其赋值为:
1 |
如果需要获取该数组的当前值,请在
1 |
因为在
如果每次迭代都需要当前值,请使用它:
1 2 3 | foreach ($list as $current) { ... } |
见
哦,天哪,是的,那很容易。等等,我已经有了一致的结果。哦,那是很容易不愚弄自己。哎呀!;)
对于日志:将变量作为函数参数传递会使其成为新变量。即使参考文献(已解释)。
如果有疑问,不要使用PHP引用。甚至不包括变量:http://3v4l.org/6p5nz
即使foreach生成了数组的副本,我也应该得到aaaa,但在当前的php稳定版本中不会得到它。
既然我在这里找不到这个问题的答案,我就(试着)解释一下。
在
编辑:
我和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | [cc lang="php"] <?php $list = array("A","B","C","D"); echo 'Ref count before entering foreach:'; debug_zval_dump($list); echo ''; $i = 0; echo 'Ref count in foreach:'; foreach ($list as $var) { $i++; echo 'Iteration #'.$i.': '; debug_zval_dump($list); echo ''; } $list = array("A","B","C","D"); //re-assign array to avoid confusion echo 'Ref count before entering foreach that calls method"item" and passes array by value:'; debug_zval_dump($list); $i = 0; echo 'Ref count in foreach that calls method"item" and passes array by value:'; foreach ( $list as $var ) { $i++; item($list, $i); } function item($list, $i) { echo 'Iteration #'.$i.': '; debug_zval_dump($list); } $list = array("A","B","C","D"); //re-assign array to avoid confusion echo 'Ref count before entering foreach that calls method"item" and passes array by reference:'; debug_zval_dump($list); $i = 0; echo 'Ref count in foreach that calls method"item" and passes array by reference:'; foreach ( $list as $var ) { $i++; itemWithRef($list, $i); } function itemWithRef(&$list, $i) { echo 'Iteration #'.$i.': '; debug_zval_dump($list); } |
得到以下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | Ref count before entering foreach:array(4) refcount(2){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(1) } Ref count in foreach:Iteration #1: array(4) refcount(3){ [0]=> string(1)"A" refcount(2) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(1) } Iteration #2: array(4) refcount(3){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(2) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(1) } Iteration #3: array(4) refcount(3){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(2) [3]=> string(1)"D" refcount(1) } Iteration #4: array(4) refcount(3){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(2) } Ref count before entering foreach that calls method"item" and passes array by value:array(4) refcount(2){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(1) } Ref count in foreach that calls method"item" and passes array by value:Iteration #1: array(4) refcount(5){ [0]=> string(1)"A" refcount(2) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(1) } Iteration #2: array(4) refcount(5){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(2) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(1) } Iteration #3: array(4) refcount(5){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(2) [3]=> string(1)"D" refcount(1) } Iteration #4: array(4) refcount(5){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(2) } Ref count before entering foreach that calls method"item" and passes array by reference:array(4) refcount(2){ [0]=> string(1)"A" refcount(1) [1]=> string(1)"B" refcount(1) [2]=> string(1)"C" refcount(1) [3]=> string(1)"D" refcount(1) } Ref count in foreach that calls method"item" and passes array by reference:Iteration #1: array(4) refcount(1){ [0]=> string(1)"A" refcount(4) [1]=> string(1)"B" refcount(3) [2]=> string(1)"C" refcount(3) [3]=> string(1)"D" refcount(3) } Iteration #2: array(4) refcount(1){ [0]=> string(1)"A" refcount(3) [1]=> string(1)"B" refcount(4) [2]=> string(1)"C" refcount(3) [3]=> string(1)"D" refcount(3) } Iteration #3: array(4) refcount(1){ [0]=> string(1)"A" refcount(3) [1]=> string(1)"B" refcount(3) [2]=> string(1)"C" refcount(4) [3]=> string(1)"D" refcount(3) } Iteration #4: array(4) refcount(1){ [0]=> string(1)"A" refcount(3) [1]=> string(1)"B" refcount(3) [2]=> string(1)"C" refcount(3) [3]=> string(1)"D" refcount(4) } |
输出有点混乱。
在第一个示例中,
我只能说,当我们通过值
很好的指出。但不同版本的PHP似乎存在内存指向问题。同样,电流只给出当前位置,而您没有在任何地方增加(导航),因此无法获得正确的输出。由于不同版本的PHP以不同的方式解释数组的下一个和起始点,因此可以在循环内部使用某种条件重置此问题的解决方案。(通过循环,然后使用current,next prev不是一个好方法,因为var中已经有了object:)您可以选择什么)这是一种方法,你可以让它工作:
1 2 3 4 5 6 7 8 9 10 11 12 |
输出为ABCD。参见http://3v4l.org/5hm5y
用这个你已经知道发生了什么!
也许它能帮助你!
1 2 3 4 |
应该这样做。