Генерация диапазона с нерегулярными шагами. оптимизированный - PullRequest
4 голосов
/ 24 декабря 2011

Имеет ли 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+
)

Ответы [ 2 ]

2 голосов
/ 24 декабря 2011

Повторное сложение лучше всего заменять умножением, а повторное умножение лучше всего заменять повышением до степеней - что вы и сделали.

Я не вижу здесь ничего, что требует улучшения, если вам не нужно "пуленепробиваемый"поведение перед лицом $jmp = 1 или $min >= $max плохого поведения.

1 голос
/ 29 декабря 2011

Инкремент $e в цикле for - это скорее бесконечный цикл while(1).

Так что вместо неправильного использования инкремента в pow(), выполните pow самостоятельно, простоумножение один раз за итерацию.Вызов pow() может быть довольно дорогим, поэтому выполнение собственного вычисления pow лучше распределит умножение на каждую итерацию.

Edit: Ниже приведен вариант вашей функции, котораяраспределяет вычисление 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;
}

Я думаю, если вы позаботитесь о том, чтобы не использовать повторнопеременные (например, параметр функции) и дают им возможность лучше читать имена, улучшение приходит само по себе.

...