如果不赞成使用每个功能,如何恢复该功能所提供的美感?

时间:2018-10-29 12:51:17

标签: php

在PHP中以及对于某些我们无法使用语言数组搜索功能(我认为)的数组,找到直到找到的典型搜索算法可以通过以下方式实现:

    $found = false;

    while (list($key, $alreadyRP) = each($this->relatedProducts) && !$found) {
        if ($alreadyRP->getProduct()->getId() === $rp->getProduct()->getId()) {
            $found = true;
        }
    }
    if (!$found) {
        // Do something here
    }

请把它当作伪代码,我没有执行它。我喜欢它的地方是,如果找到我们想要的东西,它就会优雅地停止。

现在,由于不赞成使用“每个”功能,我必须编写如下代码:

    $found = false;
    foreach ($this->relatedProducts as $alreadyRP) {
        if ($alreadyRP->getProduct()->getId() === $rp->getProduct()->getId()) {
            $found = true;
            break;
        }
    }
    if (!$found) {
        // Do something here
    }

在结构化编程中,将“ break”语句放入“ for”循环中是很难的。是的,它是可选的,但是如果我们避免使用它,那么“ foreach”将遍历所有数组,这不是最有效的方法。

在这种情况下,有什么想法可以恢复“每个”所提供的效率和结构吗?

谢谢。

3 个答案:

答案 0 :(得分:3)

each()方法的优点在旁观者的眼中,但是还有其他一些理由更喜欢foreach,包括来自the RFC that led to the deprecation of each()

的有趣信息。
  

each()函数几乎在每种可以想象的方式下都不如前者,包括慢10倍以上。

如果该方法的目的是// Do something here,而$rp中没有找到$this->relatedProducts,那么我认为一种更“美丽”的处理方式是提取搜索结果通过将相关产品转化为单独的方法。

protected function inRelatedProducts($id) {
    foreach ($this->relatedProducts as $alreadyRP) {
        if ($alreadyRP->getProduct()->getId() === $id) {
            return true;
        }
    }
    return false;
}

将相关产品搜索移至另一种方法是有利的,因为

  • 它将功能与原始方法分开,从而使其变得可重用,而不是与// Do something here所做的任何事情捆绑在一起。
  • 它简化了原始方法,因此可以专注于其主要任务

    $id = $rp->getProduct()->getId();
    if (!$this->inRelatedProducts($id)) {        
        // Do something here
    }
    
  • 它简化了搜索代码,因为如果它包含在自己的方法中,则只要找到匹配项就可以return true;,因此您无需中断或跟踪一个$found变量。


另一方面,如果这是我的项目,那么我将寻找一种方法,通过填充$this->relatedProducts来消除对此方法的需要,以便按ID索引(假定ID在此处是唯一的),因此确定可以减少到

$id = $rp->getProduct()->getId();
if (isset($this->relatedProducts[$id])) { ...

答案 1 :(得分:1)

如果您要查找不涉及额外变量的重写,则可以将each调用替换为对currentnext的调用:

do {
  $found = (current($this->relatedProducts)->getProduct()->getId() === $rp->getProduct()->getId());
} while (empty($found) && false !== next($array));

这是一种机械翻译,它仅依赖于each(强调我的意思)的定义:

  

从数组中返回当前键和值对,并提前数组光标

它也遭受与原始each版本相同的缺陷:它不处理空数组。

也就是说,请不要将each或其任何同胞用于新代码。 这是由一个在RFC上投票“不”的人造成的!为什么?因为表现糟透了:

1017$ cat trial.php
<?php
$array = range(0, 999);                                                       

$begin = -microtime(true);                                                    
for ($i = 0; $i < 10000; $i++) {                                              
    reset($array);                                                            
    $target = rand(333, 666);                                                 
    do {                                                                      
        $found = (current($array) === $target);                               
    } while (empty($found) && false !== next($array));                        
}                                                                             
printf("%.5f\n", $begin + microtime(true));                                   

$begin = -microtime(true);                                                    
for ($i = 0; $i < 10000; $i++) {                                              
    $target = rand(333, 666);                                                 
    foreach ($array as $current) {                                            
        if ($current === $target) break;                                      
    }                                                                         
}                                                                             
printf("%.5f\n", $begin + microtime(true));                                   

1018$ php -d error_reporting=-1 trial.php
8.87178
0.33585

下一个/当前版本的时间为近9秒,而foreach版本的时间为半秒。只是不要。

答案 2 :(得分:0)

看起来每个基本上都是current()next()的一个版本

http://php.net/manual/en/function.current.php

http://php.net/manual/en/function.next.php

each()给出当前数组项,并移至下一个索引。

current()给出当前数组项,但不增加索引。

因此,您可以将each()替换为current(),并在foreach内部使用next()将索引上移

next()提供下一项,并增加索引。

while (list($key, $alreadyRP) = current($this->relatedProducts) && !$found) {
    if ($alreadyRP->getProduct()->getId() === $rp->getProduct()->getId()) {
        $found = true;
    }
    next($this->relatedProducts);
}