我在Stack / Google中搜索了很多,并没有找到我问题的正确答案,所以我在这里:
我知道使用一些计算和内容是完全不好的做法,如:
for ($i = 0; $i < count($items); $i++)
最近我在代码中看到了一个奇怪的做法:
foreach (SomeModel::find() as $item)
将查找直接放在foreach中可以,比如count($ items)或者某种类型的?我不喜欢这个想法,所以我本能地避免放入一些var,但现在我的任务是改进/审查代码,我想结束我对此的无知,有人可以澄清我是否&# 39;是否可以接受实施?为什么?
我知道foreach是一个智能实现,只是不知道这个结构是否进行了那种优化。
答案 0 :(得分:2)
如果您有for
循环,则会在每次迭代中检查条件。因此,不是仅仅将var与值进行比较,而是必须在每次迭代中调用一个函数(count
)。因此,在循环之前移动计数条件会产生差异
$count = count($items);
for ($i = 0; $i < $count; ++$i) { ... }
foreach
表现不同。不是在每次迭代中评估SomeModel::find()
,而是仅对此部分进行一次评估并仅为循环保存。您还可以将SomeModel::find()
的结果保存在var中,并将该变量用于foreach
循环,但这没有什么区别。如果对循环使用var only ,则编译器应该完全相同。
但请记住。如果只进行一些迭代,那种优化应该是编译器的一部分,并且时序优势应接近0。
答案 1 :(得分:1)
与for()
相反,每次迭代都会评估每条指令,foreach()
在开头获取array
,然后移动指针迭代它:
第一个表单循环遍历array_expression给出的数组。在每次迭代中,当前元素的值被赋值为$ value,内部数组指针被提前一个(因此在下一次迭代中,您将查看下一个元素)
强调我的
所以你的函数只被调用一次。你可以看到这种行为,看看操作码。
<?php
function find() {
return [1, 2, 3, 4, 5, 6, 7, 8];
}
foreach (find() as $n) {
echo $n.PHP_EOL;
}
操作码:
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > NOP
7 1 INIT_FCALL 'find'
2 DO_FCALL 0 $1
3 > FE_RESET_R $2 $1, ->8
4 > > FE_FETCH_R $2, !0, ->8
8 5 > CONCAT ~3 !0, '%0A'
6 ECHO ~3
7 > JMP ->4
8 > FE_FREE $2
9 9 > RETURN 1
<?php
function find() {
return [1, 2, 3, 4, 5, 6, 7, 8];
}
$f = find();
foreach ($f as $n) {
echo $n.PHP_EOL;
}
操作码:
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > NOP
6 1 INIT_FCALL 'find'
2 DO_FCALL 0 $2
3 ASSIGN !0, $2
7 4 > FE_RESET_R $4 !0, ->9
5 > > FE_FETCH_R $4, !1, ->9
8 6 > CONCAT ~5 !1, '%0A'
7 ECHO ~5
8 > JMP ->5
9 > FE_FREE $4
9 10 > RETURN 1
正如您所看到的,唯一的区别是ASSIGN
指令,即指定$f
时的指令。
所以这主要是一个可读性问题。在我看来,在具有适当名称的变量中分配方法的返回值会更具可读性。然而,鲍勃叔叔在清洁代码中说明了相反的意见:
循环的控制变量通常应该在循环语句中声明,就像来自同一来源的这个可爱的小函数一样。
public int countTestCases() {
int count= 0;
for (Test each : tests)
count += each.countTestCases();
return count;
}