Меню

Първи стъпки в правене на сайтове

Влез Излез

Тестване на палиндроми – част 4

В трета част на урока постигнахме някакво решение на задачата, която си поставихме, но това решение определено не беше оптималното.

Днес ще видим как може да улесним и нашата работа, а и на самия компютър, така че да достигнем до по-кратък и изчистен код и по-добро бързодействие.

Кодът, който имаме до момента е:

$test = 'If I Had a Hi-Fi';

$simplified = preg_replace('/\W/', '', $test);
$simplified = strtolower($simplified);

$is_palindrome = true;

$repetition = strlen($simplified);
$repetition = $repetition / 2; # достатъчно е да проверим до средата, тъй като се очаква да е огледално

for ($i = 0; $i < $repetition; $i++) {
    $a = substr($simplified, $i, 1);
    $b = substr($simplified, -($i+1), 1);

    if ($a !== $b) { # стриктната проверка работи маааалко по-бързо
        $is_palindrome = false;
        break; # няма нужда да правим повече проверки -- вече е ясно, че не е палиндром
    }
}

var_dump($is_palindrome);

Едно от нещата, което можем да направим е да премахнем функцията strtolower() и просто по-нататък в кода да използваме функции, които не зависят от главни и малки букви (case insensitive).

Но най-съществената част по оптимизирането може да стане когато използваме съчетанието от две функции, които са вградени в PHP вместо цялата гимнастика, която правим със следене на индекси и проверка буква по буква.

Първо чрез strrev() можем да обърнем целия string, а след това със strcasecmp() можем да направим case insensitive проверка между оригиналния текст и версията му в обратен ред.

Така че опростеният код би бил:

$test = 'If I Had a Hi-Fi';
$simplified = preg_replace('/\W/', '', $test);
$rev = strrev($simplified);
$is_palindrome = (strcasecmp($simplified, $rev) === 0);

var_dump($is_palindrome);

Функцията strcasecmp() приема като аргументи 2 string-а. Ако първият string е „по-малък“ от втория, резултатът ще е по-малък от 0, ако са равни (еднакви), то резултатът ще е 0, а ако първият е „по-голям“ от втория – резултатът ще е по-голям от 0.

В документацията не пише точно как се определя големина на string-ове, но в нашия случай е достатъчно да знаем, че търсим резултат 0, а не нещо различно, затова и тази проверка при присвояването на резултат към променливата $is_palindrome.

Направих няколко теста и се оказа, че този нов вариант е повече от 2 пъти по-бърз (при тестове с текста „If I Had a Hi-Fi“). И това като се отчете, че основното забавяне в кода идва от регулярния израз, който присъства и в двата варианта:

$simplified = preg_replace('/\W/', '', $test);

Което означава само едно – за бързодействие е важно да се познават вградените в PHP функции, вместо да пишем всичко самички. Такива вградени функции са писани на друго ниво и са част от самия PHP парсър, което винаги ще доведе до по-добро бързодействие, отколкото ако седнем да пишем custom функция.

Винаги, когато имате да правите нещо стандартно – проверете дали вече няма някаква готова функция за него (или 2-3 функции, които комбинирани да вършат това, което ви трябва).

Ако преди сте се чудели за какво ви е бил урокът Първи стъпки в PHP – функции за текст – сега вече се надявам, че е по-ясно.

И ако искаме да го направим под формата на функция получаваме:

function test_palindrome($test) {
    $simplified = preg_replace('/\W/', '', $test);
    $rev = strrev($simplified);

    $comparison = strcasecmp($simplified, $rev);

    return ($comparison === 0);
}

$is_palindrome = test_palindrome('If I Had a Hi-Fi');

var_dump($is_palindrome);

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax