45

ここに単純なループがあります

$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

質問 :

  • 誰かが何が起こっているのか説明してもらえますか?
  • なぜ私はABCDを取得していないのですか
  • 配列のコピーが作成された場合でも、現在の安定版では取得するforeach必要がありますが取得できませんAAAAPHP

注*私は単純に使用できることを知っていますprint $varが、PHP DOCから

current — 配列内の現在の要素を返すcurrent()関数は、内部ポインタが現在指している配列要素の値を返すだけです。ポインターはまったく移動しません。内部ポインターが要素リストの末尾を超えている場合、または配列が空の場合、current() は FALSE を返します。

更新 1 - 新しい観測

ダニエル・フィゲロアに感謝:関数でラップcurrentするだけで、異なる結果が得られます

foreach ( $list as $var ) {
    print(item($list));
}

function item($list) {
    return current($list);
}

出力 (デモ)

 BCDA   // What the hell 

質問 :

  • 「BBBB」を取得しないのはなぜですか?
  • foreach関数内で current をラップすると、出力にどのような影響がありますか?
  • 余分な「A」はどこから来たのですか?

更新 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ですか
4

8 に答える 8

21

なぜ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

于 2013-02-13T09:05:48.527 に答える
3

PHP.netから

current() 関数は、内部ポインタが現在指している配列要素の値を返すだけです。ポインターはまったく移動しません

次に: next()を使用します

$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.

foreach


編集:私はまた、私の新しい紛らわしい発見を共有したい!!!

例1:

$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
  current($list_copy);
  echo current($list);
  //next($list);
}

出力: ああああ

例 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 (WOW! Bがありません)

例: 4

$list = array("A", "B", "C","D");

$refcopy = &$list;

foreach ($list as $key => $val) {
  echo current($list);
  next($list);
}

出力: BCD

foreach ループ内で何が起こるかを正確に決定することはできません!!!

于 2013-02-13T08:11:44.593 に答える
2

これがなぜなのかについての本当の手がかりはありませんが、割り当てがどのように評価/処理されるかに関係があるのではないかと思います。楽しみのためにこれを試してみたところ、別のincorrect動作になりました。

$arr = array('A', 'B', 'C', 'D');
function itm($val) {
    return current($val);
}

foreach ($arr as $item) {
    print itm($arr);
}

結果:BCDA

したがって、ここで起こっていることは、関数呼び出しによって current の評価が正しい方法で強制的に行われるということです。また、ABCD の代わりに BCDA を取得する理由は、おそらく最初に内部ポインターがインクリメントされ (B を指す)、次に en で A を指すようにリセットされるためです。

PHP docの次の行に注目する価値があるかもしれません。

代入は元の変数を新しい変数にコピーする (値による代入) ため、一方を変更しても他方には影響しないことに注意してください。これは、タイトなループ内で大きな配列のようなものをコピーする必要がある場合にも関連する可能性があります。

これは実際には回答としてカウントされないと思いますが、私はあなたの質問が好きで、少し貢献したいと思いました.

于 2013-02-13T08:46:27.620 に答える
1

嘘の場合に使用するコード。文字通り同じコードのように見えるかもしれませんが、変数はそうではありません(http://3v4l.org/jainJ)。

実際の質問に答えるには、一貫した結果を得るために適切なツールを使用してください。

配列値を持つ変数が必要な場合は、次のように割り当てます。

$list = array(....);

その配列の現在の値を取得する必要がある場合は、:の前に使用してください。foreach

$current = current($list);

これは、内部foreachでは同じ変数名である可能性がありますが、値が異なるためです(想像してみてください!)。

各反復ごとの現在の値が必要な場合は、それを使用します。

foreach ($list as $current) {
    ...
}

参照してください$current

おやおや、ええ、それはとても簡単でした。私はすでに一貫した結果を持っているのを待ってください。ああ、自分をだまさないのはとても簡単でした。わーい!;)

ログの場合:変数を関数パラメーターとして渡すと、新しい変数になります。参照(説明されている)の場合でも。

疑わしい場合は、PHP参照を使用しないでください。または変数でさえありません:http://3v4l.org/6p5nZ

于 2013-02-13T12:48:03.770 に答える
0

素晴らしい指摘。しかし、異なるバージョンのphpでのメモリポインティングの問題のようです。また、 currentは、どこにもインクリメント(ナビゲート)していない現在の位置のみを提供するため、適切な出力が得られません。PHP の異なるバージョンが配列の次の開始点を異なる方法で解釈するため、これに対する解決策は、何らかの条件でループ内でリセットすることです。(ちなみに、ループしてから現在の次の prev を使用することは、既に var にオブジェクトがあるため、良い方法ではありません:)それがあなたの選択であっても)これは、それを機能させる1つの方法です:

<?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を参照

于 2013-02-13T08:49:00.150 に答える
-1
$list = array("A", "B", "C","D");
foreach ($list as $var) {
    echo $var;
}

やるべきです。

于 2013-02-13T08:03:38.480 に答える