Php 在foreach循环中有什么更好。。。使用&;基于密钥的符号或重新分配?
考虑以下PHP代码: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); 这两种方
//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;
}
}