この質問にはすでに答えがあります。
これは単純なループです
$list = array("A", "B", "C","D");
foreach ($list as $var) {
print(current($list));
}
出力(デモ)
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
質問 :
foreach
私はなっているはずですAAAA
しかし現在ではそれを得ていないPHP
安定版注*私は私が単に使用できることを知っていますprint $var
しかしPHP DOCから
current - 配列内の現在の要素を返す の現在()functionは単に、内部ポインタが現在指している配列要素の値を返すだけです。ポインタを動かすことはありません。内部ポインタが要素リストの末尾を超えているか、配列が空の場合、current()はFALSEを返します。
アップデート1 - 新しい所見
ありがとうダニエル・フィゲロア:ラッピングだけでcurrent
関数内では異なる結果が得られます
foreach ( $list as $var ) {
print(item($list));
}
function item($list) {
return current($list);
}
出力(デモ)
BCDA // What the hell
質問 :
foreach
出力?更新2
$list = array("A","B","C","D");
item2($list);
function item2($list) {
foreach ( $list as $var ) {
print(current($list));
}
}
出力(デモを見る)
AAAA // No longer BBBB when using a function
質問 :
AAAA
外とBBBB
ほとんどのPHPバージョンの関数で
なぜBで始まるのですか?
5.2以降foreach
(確実に)配列ポインタを進める前ループ本体が始まります。また見なさいFE_RESET
オペコード。
$list = array("A", "B", "C","D");
foreach ($list as $var) {
break;
}
var_dump(current($list));
出力:
B
これは、ZEND_OP_DATA
疑似オペコードは機能します(これは実際には文書化されていません)。
どしてcurrent()
同じ値を与え続ける?
ループが始まる前に、foreach
ループしている配列への内部参照を作成します。ループ内に入ると、配列変数が変更または参照渡しされるたびに、配列参照のコピーを作成することによって内部参照と変数の関連付けが解除されます(要素は不可)。このコピーされた値は配列ポインタ(ループ初期化によって以前に変更されたもの)を保持します。
この振る舞いはより破壊的にも現れます。unset()
操作:
$list = array('A', 'B', 'C', 'D');
foreach ($list as $key => $val) {
echo $val;
unset($list[1], $list[2], $list[3]);
}
echo "\n", print_r($list, true), "\n";
出力:
ABCD
Array
(
[0] => A
)
関数にループ変数を渡す
これは別の興味深いシナリオです。
$list = array('A', 'B', 'C', 'D');
function itm($arr)
{
return current($arr);
}
foreach ($list as $item) {
print itm($list);
}
var_dump(current($list));
出力:
BCDA
bool(false)
今回は、配列は値渡しされるので、その配列構造は(要素ではなく)関数の関数にコピーされます。$arr
パラメータ前の例とは異なり、ループの内部参照と次のコードの関連付けは解除されていません。$list
コピーは関数の有効範囲内で行われるためです。
最後はどうですか"A"
?
これは、はるかに最も謎めいた振る舞いです。foreach
そしてこれらの状況下でのみ目撃することができます。最後のループ反復では、配列ポインタは一見最初の項目に巻き戻します。ループの終わりでそれが明らかに要素の終わりを越えて指しているので(出力の最後の行からわかるように)。
これは何かと関係があるかもしれませんSWITCH_FREE
の最後に実行されるオペコードforeach
。
それでなぜ配置するのですかforeach
関数でそれは違うの?
次のコードを見てください。
function item2($arr)
{
foreach ($arr as $var) {
print(current($arr));
}
var_dump(current($arr));
}
$list = array("A","B","C","D");
item2($list);
出力:
AAAA
string(1) "A"
この場合、foreach
(refcount> 1であるため)配列のコピーで初期化され、その結果配列からの関連付けが解除されます。$arr
シンボル。
さらに悪化する可能性がありますか?
もちろん!参照を使い始めたり、複数の入れ子にしたりすると、さらに驚くべき結果が得られます。foreach
同じ変数をループします。
それでは、どうすれば一貫した結果が得られるでしょうか。
つかいますIterators
または、実行中に配列変数を参照して一貫した値を取得することに頼らないでください。foreach
操作。
rewind
から来た ?それは関数が配列を巻き戻すことを意味するのでしょうか???またはreturn文が配列に影響を与える - Baba
からPHP.net
current()関数は単純に配列要素の値を返します それは現在内部ポインタによって指されています。ありません 何らかの方法でポインタを動かす
それから:use次()
$list = array("A", "B", "C","D");
foreach ($list as $var) {
print(current($list));
next($list);
}
注:foreachがポインタを配列の2番目の要素に移動したため、最初の要素は印刷されません:)
この例は完全な振る舞いを説明します:
$list = array("A", "B", "C","D");
foreach ($list as $var) {
if(!isset($a)) reset($list); $a = 'isset';
print(current($list));
next($list);
}
出力はABCDです
次の点にも注意してください。
As foreach relies on the internal array pointer changing it within the loop may lead to unexpected behavior.
編集:私も私の新しい紛らわしい発見を共有したいです!
例1
$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
current($list_copy);
echo current($list);
//next($list);
}
出力:AAAA
例2
$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
current($list_copy);
echo current($list);
next($list);
}
出力:ABCD
他の配列であってもforeach内でcurrent()関数を呼び出すと、foreachの動作に影響します。
例3
$list = array("A", "B", "C","D");
$refcopy = &$list;
foreach ($list as $key => $val) {
if(!isset($a)) { $a = 'isset'; reset($list); }
echo current($list);
next($list);
}
出力:ACD(うわー!B不足している)
例:4
$list = array("A", "B", "C","D");
$refcopy = &$list;
foreach ($list as $key => $val) {
echo current($list);
next($list);
}
出力:BCD
foreachループの中で何が起こるのか正確には決定できません!!!
これがなぜなのかについて私には本当の手がかりがありませんが、私はそれが割り当てが評価/処理される方法と関係があるかもしれないと思います。楽しみのために私はこれを試してみました、そしてそれはもう一つをもたらしましたincorrect
動作:
$arr = array('A', 'B', 'C', 'D');
function itm($val) {
return current($val);
}
foreach ($arr as $item) {
print itm($arr);
}
結果:BCDA
それで、ここで起こっていることは、関数呼び出しが電流の評価を正しい方法で行わせることであると私は思います。また、私がABCDの代わりにBCDAを取得した理由は、おそらく最初は内部ポインタがインクリメントされ(Bを指す)、その後enではそれがAを指すようにリセットされるためです。
この行に注目する価値があります。PHPドキュメント:
代入は元の変数を新しいものにコピーする(値による代入)ので、一方への変更はもう一方に影響しません。あなたがタイトなループの中に大きな配列のようなものをコピーする必要があるならば、これはまた関連性があるかもしれません。
私はこれが本当に答えとして数えないと思いますが、私はあなたの質問が好きで、少し貢献したかったです。
$arr
参考までに出力BBBB
。codepad.viper-7.com/6MxdFr - Leri
たとえ配列のコピーがforeachによって作られたとしても、私はAAAAを取得すべきですが、現在のPHP安定版では取得してはいけません。
私はここでこの質問に対する答えを見つけられなかったので、私は説明しようとします。
の最初の繰り返しの前にforeach
$list
実際にはコピーされません。のみ参照カウントの$list
したがって、最初の反復では、の最初の値$list
にコピーされます$var
ポインタはの2番目の要素に移動します$list
そして実際のコピーの$list
できるでしょう。だからあなたが電話をするときcurrent
ポインタは2番目の要素を指していますが、2番目以降の反復では変更されません。実際のコピーの$list
存在するcurrent
常に2番目の要素を出力します。
編集する
私はと遊んだdebug_zval_dump
この非常に予想外の動作を理解するには
<pre>
<?php
$list = array("A", "B", "C","D");
echo '<h1>Ref count before entering foreach:</h1><br>';
debug_zval_dump($list); echo '<br><br>';
$i = 0;
echo '<h1>Ref count in foreach:</h1><br>';
foreach ($list as $var) {
$i++;
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
echo '<br>';
}
$list = array("A", "B", "C","D"); //re-assign array to avoid confusion
echo '<h1>Ref count before entering foreach that calls method "item" and passes array by value:</h1><br>';
debug_zval_dump($list);
$i = 0;
echo '<h1>Ref count in foreach that calls method "item" and passes array by value:</h1><br>';
foreach ( $list as $var ) {
$i++;
item($list, $i);
}
function item($list, $i) {
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
}
$list = array("A", "B", "C","D"); //re-assign array to avoid confusion
echo '<h1>Ref count before entering foreach that calls method "item" and passes array by reference:</h1><br>';
debug_zval_dump($list);
$i = 0;
echo '<h1>Ref count in foreach that calls method "item" and passes array by reference:</h1><br>';
foreach ( $list as $var ) {
$i++;
itemWithRef($list, $i);
}
function itemWithRef(&$list, $i) {
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
}
そして、次のような出力が得られました。
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) }
出力は少し混乱します。
最初の例ではforeach
の内部コピーを作成しました$list
そのため、参照カウントは2(結果の4)でした。debug_zval_dump
追加するrefCount
) 2番目の例(値渡し)refCount
3に増加$list
機能用にコピーされました。 3番目の例では、countは1のままです。$list
値渡しされました。その理由を理解するために少し時間が必要です。あなたがこの結果シェアからポイントを得るならば。
私が言えるのは、配列を値渡ししたときだけですforeach
反復していた配列を渡していましたが、参照渡ししたときに元の
$list
。問題は次のとおりです。foreach
その配列を渡す?
嘘ならあなたが使うコード。文字通り同じコードのように見えるかもしれませんが、変数は違います(http://3v4l.org/jainJ)
実際の質問に答えるには、一貫した結果を得るために適切なツールを使用してください。
配列値を持つ変数が必要な場合は、それを代入します。
$list = array(....);
現在の値を取得する必要がある場合それ配列、それを使う前のforeach
:
$current = current($list);
内側からforeach
これは同じ変数名かもしれませんが、値は異なります(想像してみてください、あなたは繰り返しています!)
あなたが必要な場合現在各反復ごとの値、それを使用してください:
foreach ($list as $current) {
...
}
見る$current
?
おお、ええ、それはそれほど簡単でした。結果が安定しているのを待ちますああ、それは私をだまさないためにそれほど簡単でした。わーい! ;)
ログの場合:関数パラメータとして変数を渡すと、新しい変数になります。参考になる場合でも(説明されています)。
疑問がある場合は、PHPの参照を使用しないでください。あるいは変数でさえない:http://3v4l.org/6p5nZ
いい指摘だ。しかし、異なるバージョンのphpではメモリを指す問題があるようです。 また現在適切な出力が得られないように、どこにもインクリメント(ナビゲーション)されていない現在位置のみを表示します。異なるバージョンのphpの次のバージョンと配列の始点を解釈するための解決策は、リセットループ内に何らかの条件があります。 (ところで、ループしてから現在のを使用すると、次のprevはすでにvar:にオブジェクトを持っているので、あまり良い方法ではありません) これはあなたがそれを機能させることができる一つの方法です:
<?php
$list = array("A", "B", "C","D");
$flag =0;
foreach ($list as $var) {
if($flag==0)
{
reset($list);
$flag=1;
}
print(current($list));
next($list);
}
出力はABCDです。 で見るhttp://3v4l.org/5Hm5Y
$list = array("A", "B", "C","D");
foreach ($list as $var) {
echo $var;
}
やるべきだ。
current
foreachの内部では、特定の出力を取得する方法ではありません。 - Charles
これを使うあなたはすでに何が起こるのか知っている!
$list = array('A', 'B', 'C','D');
foreach ($list as $var) {
var_dump(current($list));
}
それはあなたの助けかもしれません!
arrays
そしてforeach
タグも、IMO、それはそれに属しているので。同意しない場合は、私の変更を元に戻してください。 :) - Leri