【问题标题】:Generating a range with irregular steps. Optimized生成具有不规则步长的范围。优化
【发布时间】:2012-01-27 06:22:50
【问题描述】:

PHP 是否具有针对不规则步长范围的现有功能,是否有提供此功能的通用解决方案,或者如何优化以下功能?

第一个函数是我关心的函数。第二个函数是一个真实的用例,它生成一个数组来填充函数的值,该函数输出 HTML 的选择下拉列表。

<?php

function range_multistep($min, $max, Array $steps, $jmp = 10) {
    $steps = array_unique($steps);
    sort($steps, SORT_NUMERIC);

    $bigstep = ($jmp > 0) ? $jmp : $jmp * -1;

    $e = ($min > 0) ? floor(log($min, $bigstep)) : 0;
    for (; ; $e++) {
        foreach ($steps as $step) {
            $jump = pow($bigstep, $e);
            $num = $step * $jump;
            if ($num > $max) {
                break 2;
            } elseif ($num >= $min) {
                $arr[] = $num;
            }
        }
    }

    $arr = array_unique($arr);
    sort($arr, SORT_NUMERIC);

    return $arr;
}

function prices() {
    $price_steps = range_multistep(50, 100000, array(5, 10, 25));

    $prev_step = 0;
    foreach ($price_steps as $price) {
        $price_str = '$' . $prev_step . ' - $' . ($price - 1);
        $price_arr[] = $price_str;
        $prev_step = $price;
    }
    $price_arr[] = '$' . end($price_steps) . "+";

    return $price_arr;
}

print_r(prices());

前面的结果:

Array
(
    [0] => $0 - $49
    [1] => $50 - $99
    [2] => $100 - $249
    [3] => $250 - $499
    [4] => $500 - $999
    [5] => $1000 - $2499
    [6] => $2500 - $4999
    [7] => $5000 - $9999
    [8] => $10000 - $24999
    [9] => $25000 - $49999
    [10] => $50000 - $99999
    [11] => $100000+
)

【问题讨论】:

  • 需要思考的一件事是,基于 min/max 生成一个范围并通过 foreach 运行该数组一段时间/for 会更好吗?

标签: php arrays optimization logarithm


【解决方案1】:

重复加法最好用乘法代替,重复乘法最好用乘方代替——你已经做到了。

假设您在面对 $jmp = 1 或 $min &gt;= $max 行为不良的输入时不需要“防弹”行为,我认为这里没有任何需要改进的地方。

【讨论】:

  • 防弹不是必需的,但让它感觉完整会很好。
【解决方案2】:

for 循环中的$e 增量器更像是一个while(1) 无限循环。

因此,请不要滥用pow() 中的增量器,而是自己执行pow,每次迭代只需乘一次。调用pow() 可能会非常昂贵,因此您自己进行pow 计算会更好地将乘法分配到每次迭代中。

编辑:以下是您的函数的变体,它在迭代中分配pow() 计算。此外,它还进行了更正确的变量初始化(例如,未设置返回值),如果 $min 和 $max 被交换并更正它,使用 abs 而不是您的三元,如果无效值则抛出异常为log() 提供,重命名一些变量并在返回值中添加$num 作为键,以节省最后的array_unique 操作:

/**
 * @param int $min
 * @param int $max
 * @param array $steps
 * @param int $jmp
 * @return array range
 */
function range_multistep($min, $max, Array $steps, $jmp = 10) {
    $range = array();
    if (!$steps) return $range;

    if ($min < $max) {
       trigger_error(__FUNCTION__.'(): Minima and Maxima mal-aligned.', E_USER_NOTICE);
       list($max, $min) = array($min, $max);
    }

    $steps = array_unique($steps);
    sort($steps, SORT_NUMERIC);

    $bigstep = abs($jmp);
    if ($bigstep === 0) {
        throw new InvalidArgumentException(sprintf('Value %d is invalid for jmp', $jmp));
    }

    $initExponent = ($min > 0) ? floor(log($min, $bigstep)) : 0;

    for ($multiplier = pow($bigstep, $initExponent); ; $multiplier *= $bigstep) {
        foreach ($steps as $step) {
            $num = $step * $multiplier;
            if ($num  > $max) {
                break 2;
            } elseif ($num >= $min) {
                $range[$num] = 1;
            }
        }
    }

    $range = array_keys($range);
    sort($range, SORT_NUMERIC);

    return $range;
}

如果您觉得是实验性的,也可以将两个循环 (for+foreach) 合二为一,但代码的可读性并不能从中受益:

for(
    $multiplier = pow($bigstep, $initExponent),
    $step = reset($steps)
        ;
    $num = $step * $multiplier,
    $num <= $max
        ;
    # infinite array iterator:
    ($step=next($steps))?:
    (
      $step=reset($steps)
      # with reset expression:
      AND $multiplier *= $bigstep
    )
){
    if ($num >= $min)
        $range[$num] = 1;
}

我认为,如果您注意不要重复使用变量(如函数参数)并让它们更好地阅读名称,那么改进就会自行产生。

【讨论】:

  • $jmp 和 $jump 是两个令人困惑但又相互独立的变量。 $min != min($min, $max) 比 $min &lt; $max 好吗?如果有,为什么?
  • 不是真的 ;),现在正如你所问,我会选择 $min &lt; $max ;) - 我在最后很快完成了这部分,并且也在尝试其他东西,所以只是一个人工制品。我也完全删除了$jump,将检查答案的措辞,这是我的错误。
  • 另外,参数中的类型转换数组不会触发错误或给我们一个数组,这将使我们永远不会执行if (!$steps)后面的代码。也许if (empty($steps)) 会更合适。
  • 这不是一个 typecast 它只是一个类型提示。如果您将数组以外的任何内容传递给该参数的函数,PHP 将抛出异常。如果它为空,则为 FALSE,因此 !$steps 在这种情况下只是空数组的简短版本(没有元素的数组在 PHP 中是布尔值 FALSE)。然后它将返回一个空范围,这与原始函数中的相同,但只是作为捷径(提前退出)。
  • 这不是布尔错误,这引起了我的关注。虽然这是一个虚假的值,但出于某种原因,我有点期望空数组为真。我想这是因为它与0、""、null 和false 背后的主旨相对不同。
猜你喜欢
  • 1970-01-01
  • 2018-10-20
  • 1970-01-01
  • 2016-05-13
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
  • 2013-12-29
  • 1970-01-01
相关资源
最近更新 更多