18

次の PHP コードを検討してください。

//Method 1
$array = array(1,2,3,4,5);
foreach($array as $i=>$number){
  $number++;
  $array[$i] = $number;
}
print_r($array);


//Method 2
$array = array(1,2,3,4,5);
foreach($array as &$number){
  $number++;
}
print_r($array);

どちらの方法も同じタスクを実行します。一方は参照を割り当て、もう一方はキーに基づいて再割り当てします。自分の仕事で優れたプログラミング手法を使用したいのですが、どの方法がより優れたプログラミング手法なのだろうか? それとも、これは本当に問題ではないものの 1 つですか?

4

8 に答える 8

6

パフォーマンスに関する限り、方法 2 の方が優れています。特に、配列が大きい場合や文字列キーを使用している場合はなおさらです。

どちらの方法も同じ量のメモリを使用しますが、最初の方法では配列を検索する必要があります。この検索はインデックスによって行われますが、ルックアップにはいくらかのオーバーヘッドがあります。

このテスト スクリプトが与えられた場合:

$array = range(1, 1000000);

$start = microtime(true);
foreach($array as $k => $v){
    $array[$k] = $v+1;
}
echo "Method 1: ".((microtime(true)-$start));

echo "\n";

$start = microtime(true);
foreach($array as $k => &$v){
    $v+=1;
}
echo "Method 2: ".((microtime(true)-$start));

平均出力は

Method 1: 0.72429609298706
Method 2: 0.22671484947205

テストを100万回ではなく10回だけ実行するようにスケールバックすると、次のような結果が得られます

Method 1: 3.504753112793E-5
Method 2: 1.2874603271484E-5

文字列キーを使用すると、パフォーマンスの違いがより顕著になります。だから走っている。

$array = array();
for($x = 0; $x<1000000; $x++){
    $array["num".$x] = $x+1;
}

$start = microtime(true);
foreach($array as $k => $v){
    $array[$k] = $v+1;
}
echo "Method 1: ".((microtime(true)-$start));

echo "\n";

$start = microtime(true);
foreach($array as $k => &$v){
    $v+=1;
}
echo "Method 2: ".((microtime(true)-$start));

のようなパフォーマンスが得られます

Method 1: 0.90371179580688
Method 2: 0.2799870967865

これは、文字列キーによる検索は、配列インデックスよりもオーバーヘッドが大きいためです。

Elias Van Ootegem's Answerで提案されているように、ループが完了した後に参照を設定解除する必要があることにも注意してください。つまりunset($v);、パフォーマンスの向上は、読みやすさの損失に対して測定する必要があります。

于 2013-08-26T13:31:56.823 に答える
3

多少のパフォーマンスの違いはありますが、大きな影響はありません。

次の 2 つの理由から、最初のオプションを選択します。

  1. それはより読みやすいです。これは少し個人的な好みですが、一見すると$number++、配列を更新していることはすぐにはわかりません。のようなものを明示的に使用すること$array[$i]++で、より明確になり、1 年後にこのコードに戻ったときに混乱が生じる可能性が低くなります。

  2. 配列内の最後の項目へのダングリング参照が残ることはありません。次のコードを検討してください。

    $array = array(1,2,3,4,5);
    foreach($array as &$number){
        $number++;
    }
    
    // ... some time later in an unrelated section of code
    $number = intval("100");
    
    // now unexpectedly, $array[4] == 100 instead of 6
    
于 2013-07-03T23:41:29.190 に答える
1

それによると思います。コードの読みやすさ/保守性、またはメモリ使用量の最小化についてもっと気にしますか。2 番目の方法ではメモリの使用量がわずかに少なくなりますが、foreach 定義での参照による割り当ては PHP では一般的ではないように思われるため、正直なところ最初の使用方法の方が好みです。

個人的に、このように配列を変更したい場合は、3 番目のオプションを使用します。

array_walk($array, function(&$value) {
    $value++;
});  
于 2013-07-03T23:15:46.027 に答える
0

検討しましたarray_mapか?配列内の値を変更するように設計されています。

$array = array(1,2,3,4,5);
$new = array_map(function($number){
  return $number++ ;
}, $array) ;
var_dump($new) ;
于 2013-07-03T23:15:39.827 に答える
0

私は2番を選びますが、個人的な好みです。

foreach ループで配列項目への参照を使用することは非常に一般的ですが、使用しているフレームワークによって異なります。いつものように、プロジェクトまたはフレームワークの既存のコーディング規則に従うようにしてください。

また、array_map または array_walk を提案する他の回答にも同意しません。これらは、各配列要素の関数呼び出しのオーバーヘッドを導入します。小さな配列の場合、これは重要ではありませんが、大きな配列の場合、このような単純な関数に大きなオーバーヘッドが追加されます。ただし、より重要な計算やアクションを実行する場合は適切です。おそらくベンチマークによって、シナリオに応じてどちらを使用するかを決定する必要があります。

于 2013-07-03T23:23:04.983 に答える
0

最初の方法は、ループを通過するたびに $number 変数に新しい値を割り当てるため、わずかに遅くなります。2 番目の方法は変数を直接使用するため、ループごとに新しい値を割り当てる必要はありません。

しかし、私が言ったように、違いは重要ではありません。考慮すべき主なことは読みやすさです。

私の意見では、ループ内で値を変更する必要がない場合は、最初の方法の方が理にかなっています。$number 変数は読み取られるだけです。

2 番目の方法は、$number 変数を頻繁に変更する必要がある場合に適しています。変更するたびにキーを繰り返す必要がなく、読みやすいからです。

于 2013-07-03T23:13:30.163 に答える
0

ほとんどの回答は、あなたの質問がパフォーマンスに関するものであると解釈しました。

これはあなたが尋ねたものではありません。あなたが尋ねたのは:

どの方法がより良いプログラミングの練習になるのだろうか?

おっしゃるとおり、やってることはどちらも同じです。どちらも機能します。結局、より良いものはしばしば意見の問題です。

それとも、これは本当に問題ではないものの 1 つですか?

関係ないとまでは言いません。ご覧のとおり、方法 1 にはパフォーマンスに関する考慮事項があり、方法 2 には参照の落とし穴があります。

もっと重要なのは読みやすさと一貫性だと言えます。PHP で配列要素をインクリメントする方法は数十ありますが、ライン ノイズやコード ゴルフのように見えるものもあります。

将来の開発者がコードを読めるようにし、問題を解決する方法を一貫して適用することは、このコードに存在するどんな小さな違いよりもはるかに優れたマクロプログラミングの実践です。foreach

于 2013-08-28T02:35:39.890 に答える