Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/280.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 在foreach循环中有什么更好。。。使用&;基于密钥的符号或重新分配?_Php_Arrays_Reference_Foreach_Assign - Fatal编程技术网

Php 在foreach循环中有什么更好。。。使用&;基于密钥的符号或重新分配?

Php 在foreach循环中有什么更好。。。使用&;基于密钥的符号或重新分配?,php,arrays,reference,foreach,assign,Php,Arrays,Reference,Foreach,Assign,考虑以下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); 这两种方

考虑以下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);

这两种方法都完成相同的任务,一种是通过分配引用,另一种是通过基于键重新分配。我想在工作中使用好的编程技术,我想知道哪种方法是更好的编程实践?或者这是其中一个并不重要的方法吗?

第一个方法的速度会稍微慢一些,因为每次它通过循环时,都会给$number变量分配一个新值。第二种方法直接使用变量,因此不需要为每个循环分配新值

但是,正如我所说,差异并不显著,主要考虑的是可读性。

在我看来,当不需要修改循环中的值时,第一种方法更有意义,$number变量将只被读取


当您需要经常修改$number变量时,第二种方法更有意义,因为您不需要每次修改时都重复该键,而且它更可读。

您考虑过
数组映射吗?它设计用于更改数组内的值

$array = array(1,2,3,4,5);
$new = array_map(function($number){
  return $number++ ;
}, $array) ;
var_dump($new) ;

我想这要看情况了。您是否更关心代码的可读性/可维护性或最小化内存使用。第二种方法使用的内存会稍微少一些,但我更喜欢第一种用法,因为在foreach定义中通过引用分配似乎在PHP中并不常见

就我个人而言,如果我想修改这样的阵列,我会选择第三个选项:

array_walk($array, function(&$value) {
    $value++;
});  
我会选择2号,但这是我个人的偏好

我不同意其他答案,在foreach循环中使用数组项的引用是很常见的,但这取决于您使用的框架。一如既往,在项目或框架中尝试遵循现有的编码约定


我也不同意其他建议使用数组映射或数组行走的答案。这将为每个数组元素引入函数调用的开销。对于小型阵列,这并不重要,但对于大型阵列,这将为这样一个简单的函数增加大量开销。但是,如果您正在执行更重要的计算或操作,它们是合适的-您需要根据场景决定使用哪一种,可能是通过基准测试。

存在一些小的性能差异,但它们不会产生任何显著的影响

我之所以选择第一个选项,有两个原因:

  • 更具可读性。这是一种个人偏好,但乍一看,
    $number++
    正在更新数组,这对我来说并不明显。通过显式地使用类似于
    $array[$i]++
    的东西,它更清晰,并且在一年后回到这段代码时不太可能引起混淆

  • 它不会留下对数组中最后一项的悬空引用。考虑这个代码:

    $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
    

  • 就性能而言,方法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
    
    如果我将测试缩减到只运行10次,而不是100万次,我会得到如下结果

    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));
    
    这是因为按字符串键搜索比数组索引有更多的开销


    还值得注意的是,正如中所建议的,要在完成循环后正确清理,应该在循环完成后取消设置引用。即,
    unset($v)和性能增益应根据可读性损失来衡量

    由于得分最高的答案表明第二种方法在各方面都更好,我觉得有必要在这里发布答案。诚然,通过引用进行循环更有效,但并非没有风险/陷阱。
    底线,一如既往:“X和Y哪个更好”,你能得到的唯一真正答案是:

    • 这取决于你在追求什么/你在做什么
    • 哦,如果你知道你在做什么,两个都可以
    • X是这样的好,Y是这样的好
    • 不要忘记Z,即使是那样……(“X、Y或Z哪个更好”是同一个问题,所以相同的答案适用:这取决于,如果……,两者都可以)
    尽管如此,正如Orangepill所示,参考方法提供了更好的性能。在这种情况下,性能与代码之间的折衷是不易出错、更易于阅读/维护。通常,人们认为最好使用更安全、更可靠、更易于维护的代码:

    调试的难度是编写代码的两倍。因此,如果你尽可能聪明地编写代码,那么根据定义,你就没有足够的智慧来调试它

    我想这意味着第一种方法必须被视为最佳实践。但这并不意味着第二种方法应该一直避免,因此下面是在
    foreach
    循环中使用引用时必须考虑的缺点、陷阱和怪癖:

    范围:
    首先,PHP并不像C(++)、C#、Java、Perl或(幸运的是)ECMAScript6那样具有真正的块范围。。。这意味着循环完成后,
    $value
    变量将不会被取消设置。当通过引用循环时,这意味着对正在迭代的任何对象/数组的最后一个值的引用是浮动的。“等待发生的事故”这句话应该会浮现在脑海中。
    考虑下面的代码:<代码> $value <代码>,随后<代码> $数组< /代码>:

    $array = range(1,10);
    foreach($array as &$value)
    {
        $value++;
    }
    echo json_encode($array);
    $value++;
    echo json_encode($array);
    $value = 'Some random value';
    echo json_encode($array);
    
    此代码段的输出将是:

    [2,3,4,5,6,7,8,9,10,11]
    [2,3,4,5,6,7,8,9,10,12]
    [2,3,4,5,6,7,8,9,10,"Some random value"]
    
    换句话说,通过重用
    $value
    变量(whic
    [2,3,4,5,6,7,8,9,10,11]
    [2,3,4,5,6,7,8,9,10,12]
    [2,3,4,5,6,7,8,9,10,"Some random value"]
    
    $array = range(1,10);
    $array[] = 'foobar';
    foreach($array as $k => $v)
    {
        $array[$k]++;//increments foobar, to foobas!
        if ($array[$k] === ($v +1))//$v + 1 yields 1 if $v === 'foobar'
        {//so 'foobas' === 1 => false
            $array[$k] = $v;//restore initial value: foobar
        }
    }
    
    foreach($array as &$value)
    {
        $value++;
    }
    unset($value);
    
    foreach($nestedArr as &$array)
    {
        if (count($array)%2 === 0)
        {
            foreach($array as &$value)
            {//pointless, but you get the idea...
                $value = array($value, 'Part of even-length array');
            }
            //$value now references the last index of $array
        }
        else
        {
            $value = array_pop($array);//assigns new value to var that might be a reference!
            $value = is_numeric($value) ? $value/2 : null;
            array_push($array, $value);//congrats, X-references ==> traveling value!
        }
    }
    
    function recursiveFunc($n, $max = 10)
    {
        if (--$max)
        {
            return $n === 1 ? 10-$max : recursiveFunc($n%2 ? ($n*3)+1 : $n/2, $max);
        }
        return null;
    }
    $array = range(10,20);
    foreach($array as $k => $v)
    {
        $v = recursiveFunc($v);//reassigning $v here
        if ($v !== null)
        {
            $array[$k] = $v;//only now, will the actual array change
        }
    }
    echo json_encode($array);
    
    [7,11,12,13,14,15,5,17,18,19,8]
    
    $array = range(10,20);
    foreach($array as &$v)
    {
        $v = recursiveFunc($v);//Changes the original array...
        //granted, if your version permits it, you'd probably do:
        $v = recursiveFunc($v) ?: $v;
    }
    echo json_encode($array);
    //[7,null,null,null,null,null,5,null,null,null,8]
    
    foreach($array as &$v)
    {
        $temp = recursiveFunc($v);//creating copy here, anyway
        $v = $temp ? $temp : $v;//assignment doesn't require the lookup, though
    }
    //or:
    foreach($array as &$v)
    {
        $v = recursiveFunc($v) ? recursiveFunc($v) : $v;//2 calls === twice the overhead!
    }
    //or
    $base = reset($array);//get the base value
    foreach($array as $k => &$v)
    {//silly combine both methods to fix what needn't be a problem to begin with
        $v = recursiveFunc($v);
        if ($v === 0)
        {
            $v = $base + $k;
        }
    }