Возврат из функции — не единственная причина использования оператора return. Во многих функциях операторы return используются для обмена данными с вызывающим их кодом. Функция была более полезной, если бы вместо вывода на экран результата сравнения в функции larger() она возвращала бы ответ. В этом случае вызвавший ее код мог бы принимать решение, нужно ли и когда именно отображать или использовать этот ответ. Эквивалентная встроенная функция mах() действует именно таким образом.
Функцию larger() можно было бы определить следующим образом:
function larger($x, $y)
{
if (!isset($x)||!isset($y))
return false;
else if ($x >= $y)
return $x;
else return $y;
}
$a = 1;
$b = 2.5;
$c = 1.8;
echo larger($a, $b).'<br>';
echo larger($c, $a).'<br>';
echo larger($a, $d).'<br>';
В окне веб-браузера это будет выглядеть ТАК.
Эта функция возвращает большее из двух переданных ей значений. В случае ошибки функция будет явно возвращать другое число. Если одно из чисел отсутствует, функция larger () возвращает false. При этом следует иметь ввиду, что тип возврата необходимо проверить с помощью операции ===, чтобы убедиться, что значение false не спутано с нулем.
Для сравнения, встроенная функция шах() ничего не возвращает, если обе переменные не установлены, а если только одна переменная была установлена, функция возвращает упомянутое значение.
Часто функции, которые выполняют определенную задачу, но не должны возвращать значение, возвращают значение true или false для указания на успешное или неуспешное выполнение. Значения true и false могут быть соответственно представлены значением 1 или 0.
Группа операторов объявляется блоком путем помещения их в фигурные скобки. Это не влияет на действие большей части кода, но оказывает специфичное воздействие в том числе и на способ выполнения таких управляющих структур, как циклы и условные операторы.
Следующие два очень похожих примера кода дают совершенно разные результаты.
Пример без блока кода
for ($i = 0; $i < 3; $i++)
echo 'строка 1<br>';
echo 'строка 1<br>';
Пример блоком кода
for ($i = 0; $i < 3; $i++)
{
echo 'строка 1<br>';
echo 'строка 1<br>';
}
В окне веб-браузера это будет выглядеть ТАК.
В обоих примерах цикл for повторяется три раза. В первом примере только единственная строка, расположенная непосредственно под этим кодом, выполняется в цикле for.
Во втором примере блок кода используется для группирования двух строк. Это означает, что обе строки выполняются в цикле for три раза.
Поскольку код в этих примерах отображен с соответствующими отступами, вероятно, несложно с первого взгляда увидеть различие между ними. Отступы в коде служат для упрощения понимания того, на какие строки воздействует цикл for. Однако следует помнить, что пробелы не влияют на обработку кода в РНР.
РНР поддерживает рекурсивные функции. Под рекурсивной функцией понимают функцию, которая вызывает саму себя. Эти функции особенно полезны для перемещения по динамическим структурам данных, таким как связные списки и деревья.
Однако лишь немногие Web-приложения требуют столь сложных структур данных, поэтому рекурсия используется очень редко. Во многих случаях рекурсия может использоваться вместо итерации, поскольку оба эти подхода позволяют повторно выполнять те или иные действия. Рекурсивные функции работают медленнее и используют больший объем памяти, чем итерация, поэтому всегда по возможности следует отдавать предпочтение итерации.
Рассмотрим пример:
function reverse_r ($str)
{
if (strlen ($str) > 0)
reverse_r (substr ($str, 1);
echo substr ($str, 0, 1).'<br>';
return;
}
function reverse_i ($str)
{
for ($i = 1; $i <= strlen ($str); $i++)
{
echo substr ($str, -$i, 1).'<br>';
}
return;
}
reverse_r ('Пример');
reverse_i ('Пример');
В этом листинге реализованы две функции. Обе будут выводить строку в обратном порядке. Функция reverse_r() — рекурсивная, a reverse_i() — итеративная.
Функция reverse_r() принимает строку в качестве параметра. При ее вызове она будет вызывать саму себя, каждый раз передавая символы строки со второго до последнего.
Например, если вызвать функцию:
reverse_r ('Пример');
В окне веб-браузера это будет выглядеть ТАК.
При каждом обращении к самой себе функция создает новую копию кода функции в памяти сервера, но с другим параметром. Внешне это выглядит так, словно в действительности каждый раз вызывается другая функция. Это предотвращает возникновение путаницы с экземплярами функции.
При каждом вызове проверяется длина переданной строки. По достижении конца строки условие оказывается ложным (strlen()==0). После этого последний экземпляр функции (reverse_r("")) будет завершен и будет выполнена следующая строка кода, повторяющая на экране первый символ переданной строки — в данном случае никакой, поскольку строка пуста.
Затем этот экземпляр функции возвращает управление экземпляру, который вызвал его, а именно — reverse_r("е"). Этот экземпляр выводит первый символ -в своей строке, "р", и возвращает управление вызвавшему его экземпляру.
Этот процесс — вывод символа и возврат к экземпляру функции, расположенному над ним в порядке вызова — продолжается до тех пор, пока управление не будет возвращено основной программе.
Рекурсивные решения весьма изящны и соответствуют математическим методам. Однако, в большинстве случаев лучше использовать итеративные решения. Код для реализации этого метода также приведен выше. Обратите внимание, что по длине он не превышает рекурсивную программу (хотя при использовании итеративных функций это и не всегда так) и обеспечивает совершенно такой же результат.
Основное различие между ними заключается в том, что рекурсивная функция будет создавать в памяти копии самой себя и, соответственно, приводит к накладным расходам, связанным с несколькими вызовами функции.
Рекурсивное решение имеет смысл использовать, когда соответствующий код оказывается гораздо короче и изящнее итеративной версии, но в данной области применения подобное случается нечасто.
Хотя рекурсия выглядит более элегантной, программисты часто забывают определить условие прекращения рекурсии. Это означает, что функция будет продолжать вызывать себя до тех пор, пока сервер не столкнется с нехваткой памяти или пока не истечет максимальное время выполнения, в зависимости от того, что произойдет раньше.