【问题标题】:Querying inside foreach is ok?在foreach内部查询可以吗?
【发布时间】:2018-03-30 12:48:19
【问题描述】:

我在 Stack/Google 中搜索了很多,但没有找到我的问题的正确答案,所以我开始了:

我知道在 for 中使用一些计算和东西是非常糟糕的做法,例如:

for ($i = 0; $i < count($items); $i++) 

最近在自己工作的代码中看到一个奇怪的做法:

foreach (SomeModel::find() as $item)

可以直接将 find 放在 foreach 中,比如 count($items) 之类的吗?我不喜欢这个想法,所以本能地我避免放入一些 var,但现在我的任务是改进/审查代码,我想结束我对此的无知,有人可以澄清我是否可以接受该实现为什么?

我知道foreach是一个智能实现,只是不知道这个结构有没有做那种优化。

【问题讨论】:

  • 好吧,我不知道在这种情况下如何优化,但是当谈到可读性和干净的代码时,我确信只使用变量$models 来保存结果会更干净。我很想知道这种代码是否存在一些性能缺陷

标签: php loops foreach iteration


【解决方案1】:

如果您有一个for 循环,则在每次迭代中都会检查该条件。因此,您必须在每次迭代中调用一个函数 (count),而不是仅仅将一个 var 与一个值进行比较。所以在循环之前移动计数条件会有所不同

$count = count($items);
for ($i = 0; $i < $count; ++$i) { ... }

foreach 表现不同。这部分不是在每次迭代中评估SomeModel::find(),而是只评估一次并仅为循环保存。您还可以将SomeModel::find() 的结果保存在一个变量中,并将该变量用于foreach 循环,但这并没有什么不同。如果你对循环使用 var only,编译器应该做的完全一样。

但请记住。这种优化应该是编译器的一部分,并且如果您只有几次迭代,那么时序优势应该接近于 0。

【讨论】:

  • 你的回答很好,但是@ishegg 解释的很详细,谢谢!
【解决方案2】:

for()(在每次迭代中对每条指令进行评估)相比,foreach() 在开头获取array,然后移动指针以遍历它:

第一种形式循环遍历由array_expression 给出的数组。在每次迭代中,当前元素的值被分配给 $value,内部数组指针前移一个(因此在下一次迭代中,您将查看下一个元素)。

强调我的

所以你的函数只被调用一次。您可以通过查看操作码看到这种行为。

Without assignment:

<?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

还有with assignment:

<?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 的赋值。

所以这主要是一个可读性问题。在我看来,将方法的返回值分配到具有适当名称的变量中更具可读性。然而,鲍勃叔叔在 Clean Code 中却相反:

循环的控制变量通常应该在循环语句中声明,就像来自同一来源的这个可爱的小函数一样。

public int countTestCases() {
    int count= 0;
    for (Test each : tests)
        count += each.countTestCases();
    return count;
}

【讨论】:

    猜你喜欢
    • 2019-06-23
    • 2014-02-13
    • 2014-12-17
    • 2016-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多