0

これはばかげた例ですが、私の問題が正確に示されています。ある状況ではアレイが正常に変更され、別の状況では変更されないのはなぜですか?値は値によってforeachに与えられますか?また、出力もねじ込まれています。一部の行には'\ r\n'が含まれているようです。

$arr = file('text.txt');
echo '<pre>';

foreach( $arr as $x => $line){
    if( $x % 3){ unset( $arr[$x]); } // this works
    else{ $arr[$x+1] += 1;} // this don't
    echo "[$x] => ${arr[$x+1]}";
}


print_r( $arr);

text.txt:

0
1
2
3
4
5
6

出力:

[0] => 2 [1] => 2
[2] => 3
[3] => 5 [4] => 5
[5] => 6 [6] => 1Array

    [0] => 0

    [ 3] => 3

    [6] => 6
    [7] => 1

編集:

この例は実際には何も達成しませんでした。予期しないことが起こったことを示すだけでは役に立たなかったので、これが私がしなければならないことに近いものです。

<?php

$arr = file('text.txt');
echo '<pre>';

foreach( $arr as $x => $line){
    if( preg_match("/word$/", $line)){
        $line = preg_replace( "/word$/", '', $line);
        $arr[$x+1] = 'word ' . $arr[$x+1];
    }
}


print_r( $arr);

text.txt:

test0
test1word
test2

配列の期待値:

[0] => test0
[1] => test1
[2]=>単語test2

4

6 に答える 6

3

反復中に配列を変更することは一般的に安全ではなく、予期しない動作を引き起こす可能性があります。

于 2010-07-27T15:06:29.693 に答える
3

参照を渡さない限り、foreachイテレータ内の値を変更することはできません。PHPのマニュアルページに記載されているように:

注:配列が参照されていない限り、foreachは、配列自体ではなく、指定された配列のコピーを操作します。foreachは、配列ポインターにいくつかの副作用があります。foreachの実行中または実行後に、リセットせずに配列ポインターに依存しないでください。

そのため、foreach行を次のように更新する必要があります。

foreach( $arr as $x => &$line){
...
于 2010-07-27T15:13:22.373 に答える
0

さて、あなたの例では、3で割り切れないすべての要素の設定を解除します。これで、$arr[$x+1] += 1;値を1ずつ増やしていきます。次の反復では、3で割り切れないため、設定が解除されます。

于 2010-07-27T15:10:01.497 に答える
0

ただし、これは予想される動作ですが、3の倍数ではないものはすべて設定解除され、その後、設定されていない変数を1ずつインクリメントしようとします。

于 2010-07-27T15:10:05.060 に答える
0

これで何を達成しようとしていますか?

if( $x % 3){ 

$ xが0の場合、$ x%3は0であるため、falseです。$ xが1の場合、$ x%3は1であるため、trueです。$ xが2の場合、$ x%3は2であるため、trueです...したがって、$ xが0の場合、$ x + 1(1)がインクリメントされ、エントリ1と2の設定がすぐに解除されます。$ xが3(エントリ4)の場合、$ x+1をインクリメントします。次に、エントリ4と5などの設定を解除します。

意味ですか

if(( $x % 3) == 0) { 
于 2010-07-27T15:17:01.837 に答える
0

2番目の例はまだ期待される動作です... 「参照による」を使用しない限り、実際の配列値ではなく、配列とその値のコピーで作業しています。

foreach( $arr as $x => &$line){
    if( preg_match("/word$/", $line)){
        $line = preg_replace( "/word$/", '', $line);
        $arr[$x+1] = 'word ' . $arr[$x+1];
    }
}
unset($line);

$ lineではなく&$ lineを使用していることに注意してください。ループが終了した後は、設定を解除するのが常に最も安全です。

編集

PHPマニュアルからの引用:

注:配列が参照されていない限り、foreachは、配列自体ではなく、指定された配列のコピーを操作します。foreachは、配列ポインターにいくつかの副作用があります。foreachの実行中または実行後に、リセットせずに配列ポインターに依存しないでください。

編集

foreach()での参照の使用はお勧めしません。これは非常に遅く、私の場合は16倍遅くなりました。この行を追加するための解決策:$ line = $ arr [$ x]; ループの始めに、それはいくつかの魔法のトリックをしているようで、すべてが私が期待するように機能します

本当に魔法のトリックではありません。foreachループを介して抽出された$lineの値を、キー($ x)を介して配列から直接$lineで上書きするだけです。

YMMVですが、私にはそれほど遅くは見えません。

次のテストスクリプト:

$arr = range(1,9999);


$callStartTime = microtime(true);

foreach($arr as &$line) {
    $line += 1;
}
unset($line);

$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
echo '<br />Call time to access by reference was '.sprintf('%.4f',$callTime)." seconds<br />\n";


foreach($arr as $x => &$line) {
    $line += 1;
}
unset($line);

$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
echo '<br />Call time to access by reference (retrieving key as well) was '.sprintf('%.4f',$callTime)." seconds<br />\n";


$callStartTime = microtime(true);

foreach($arr as $x => $line) {
    $arr[$x] += 1;
}
unset($line);

$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
echo '<br />Call time and then access array element directly was '.sprintf('%.4f',$callTime)." seconds<br />\n";

$callStartTime = microtime(true);

foreach(array_keys($arr) as $x) {
    $arr[$x] += 1;
}

$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
echo '<br />Call time to access array_keys was '.sprintf('%.4f',$callTime)." seconds<br />\n";

次のタイミングを返します。

Call time to access by reference was 0.0018 seconds
Call time to access by reference (retrieving key as well) was 0.0039 seconds
Call time to access key and then access array element directly was 0.0077 seconds
Call time to access array_keys was 0.0071 seconds
于 2010-07-27T16:49:27.460 に答える