Форумы портала PHP.SU » PHP » Уроки php » Уроки № 3 - Знакомсво с функциями

Страниц (3): [1] 2 3 »
 

1. Champion - 16 Января, 2009 - 11:16:20 - перейти к сообщению
Уроки № 3 - Знакомство с функциями

Теперь, когда вы уже знакомы с переменными, операциями над ними, познакомились с управляющими структурами языка, можно поговорить и о функциях.

Бывают такие ситуации, что какую-то последовательность инструкций вы повторяете много раз. И эта последовательность выполняет в конечном итоге что-то вполне завершенное.

Функция это кучка действий. Она принимает аргументы, делает с ними некие операции и возвращает результат. К примеру Приготовить пиццу это функция принимающая параметры - тесто, яйца, сыр, кетчуп, анчоусы, плиту, и возвращает Пиццу.
Возвращаемое значение у функций может быть любого типа данных.

Еще одним преимуществом функций, является упрощение чтения кода программы.

Синтактсис
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. function имя(аргументы)
  3. {
  4.   // тело функции;
  5. }

Имя функции может состоять из латинских буков, цифр и знаков подчеркивания. Начинаться с цифры оно не должно. Всё точно так же как с названиями переменных.

Возможно вы заметили, что уже с первого урока мы путаем вас понятиями
параметр и аргумент.

Аргументами называются неопределенные входящие данные.
Параметрами же являются известные данные.
Т.е. когда мы описываем функцию, в скобках аргументы, когда вызываем - параметры. Но застопориваться на этом, думаю, не обязательно.
В функции которая готовит пиццу - аргументом будет "количество требуемых блюд"
а параметром будет цифра 5.
Во всяком случае во всех случаях речь идёт о данных передаваемых функции.
2. Champion - 16 Января, 2009 - 11:36:11 - перейти к сообщению
Рассмотрим создание самодельной функции на примере.
Задача функции найти самый большой элемент массива.

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.  
  3.  
  4.  
  5. function arr_max($arr) // Определяем функцию arr_max принимающую один аргумент
  6. {
  7.  // Входящие данные: Массив
  8.  // Исходящие данные: значение элемента в массиве.
  9.  
  10.   // Устанавливаем значение нулевого элемента как максимального
  11.   $max = $arr[0];
  12.  
  13.   for ($i = 1; isset($arr[$i]); $i++) // для каждого элемента массива
  14.     if ($arr[$i] > $max) // проверяем что больше. Текущее значение или максимум
  15.       $max = $arr[$i]; // Если максимум больше, то меняем максимум на новый
  16.  
  17.         $arr[1] = 95; // Поменяем значение первого элемента массива. Не задумывайтесь почему. Чуть позже увидите.
  18.  
  19.         // функция возвращает результат.
  20.         // Результат в данном случае самое большое число в массиве
  21.         return $max;
  22. }
  23.  
  24. // Закончилось определение функции. Дальше продолжение скрипта.
  25.  
  26.  
  27. $ar1 = array(4, 6, 12, 9, '5', 23);
  28. $ar2 = 'dabcjfutZfgh';
  29.  
  30. echo arr_max($ar1)."<br>\n"; // Выведет 23
  31.  
  32. echo '$ar1[1]'.$ar1[1]; // выведет 6 (а не 95, помните ту загадочную строку функции?). Получается значение не изменилось
  33. // Напомним: в одинарных кавычках значения переменных не подставляются
  34.  
  35. // подумайте над результатами такого вызова:
  36. echo arr_max($ar2);
  37. ?>


Область видимости

Как же так получается что операция присваивания $arr[1] = 95
не изменила значение переменной вне функции?

Существует такое понятие - Область видимости. Понять это понятие легко и понятно =)
Функция представляет из себя своего рода маленькую подпрограмму.
У этой подпрограммы есть свои переменные, а переменные из главной программы она просто не видит.

Так как в эту программку одним из параметров поступил массив $arr[1]
это значит что функция вполне его видела и могла с ним работать.
Но на самом деле в функцию передаётся на переменная, а её точная копия. Ее значение.
Поэтому когда наша подпрограмма завершает свое выполнение - все её переменные
(копии наших) удаляются.


Передача параметров по ссылке

Напомню тебе немного первый урок, в котором я объяснял как хранит данные в памяти компьютер, и что $variable на самом деле хранит вовсе не значение а идентификатор ячейки в памяти, в которой как и находится наше значение.

Как ты заметил в примере выше, все переменные используемые в функциях являются локальными, то-есть создаются используются и удаляются в самой функции, а за её пределами не доступны.

Для того, чтобы передать в функцию не значение переменной, а саму переменную которую функция сможет изменить и за её пределами мы сможем ею пользоваться - существует особый способ передачи параметров - По ссылке. Выглядит вызов функции с параметрами по ссылке с добавлением символа & вот так: myFunction(&$myvar);

Когда в вызове функции переходит передача переменной по ссылке происходит передача идентификатора ячейки, а не значения. Когда ты вызываешь функцию func($a) интерпретатор в первую очередь достает из памяти значение переменной $a и передает это значение в функцию. После этого в самой функции создается новая переменная которой присваивается это значение.

Когда функция вызывается с параметром по ссылке func(&$a) интерпретатор передает в функцию не значение переменной, а идентификатор ячейки в памяти
При вызове функции с параметрами обычным способом все изменения происходят в другой, новой ячейке памяти и за пределами функции работа продолжается со старой ячейкой. А при передаче по ссылке все изменения происходят в той же ячейке и поэтому изменения видны и из вне самой функции.
3. Champion - 16 Января, 2009 - 12:07:27 - перейти к сообщению
Оператор return

Наша функция не обязана что либо возвращать конструкцией return.
Возможно, она просто выполняет ряд операций, например,
вывод нескольких строк или еще что-либо.
Например, уже знакомый нам код из предыдущей главы можно оформить так:

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. function who_is_it($sName)
  3. {
  4.    if($sName=="EuGen")  echo("Да, это я");
  5.    elseif($sName=="Valenok")  echo("Теперь я точно уверен, это Valenok");
  6.    elseif($sName=="Champion") echo("Это тоже наш автор, Champion");
  7.    else  echo("Я запутался..");
  8. }
  9. // основная программа
  10. $val = 'Valenok';        who_is_it($val);
  11. $eu = 'EuGen';           who_is_it($eu);
  12. $ch = 'Champion';      who_is_it($ch);
  13. $unknown = 'Гость';   who_is_it($unknown);
  14. ?>
  15.  


Мы не просили функцию что либо возвращать.. В таком случае интерпретатор возвращает значение Null.

Но бывает, что одно возвращенного значения не хватает. Также возможно, что вы хотите, чтобы изменения, произошедшие с переменными в функции отразились и в основной программе .


Для этого можно воспользоваться двумя решениями.
Либо заставить нашу подпрограмму увидеть переменную извне, либо передать ей оригинал параметра вместо копии.

Поставив значок & перед переменной, мы укажем интерпретатору что мы передаем
не саму переменную, а ссылку на переменную. По сути это можно понимать как тип Resource.
Функция будет уже работать не с копией данных в памяти, а непосредственно с определенной ячейкой в памяти. Таким образов все изменения будут записаны прямиком
в нужный нам участок оперативной памяти и изменения сохранятся.

Пусть функция возвращает сумму 2 слогаемых, в первый параметр запишется произведение, а второй параметр просто удвоится.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. function func(&$var1, &$var2)
  3. {
  4.         $res = $var1 + $var2;
  5.         $var2 = $var2 * 2;
  6.         $var1 = $var1 * $var2;
  7.         return $res;
  8. }
  9. $a = 3;
  10. $b = 4;
  11. echo func($a, $b); // 7
  12. echo $a; // 12
  13. echo $b; //8
  14. ?>



Конструкция global позволяет функции увидеть и использовать переменную из внешнего мира.
В данном случае даже не придётся передавать ей параметры, так как она их увидит сама.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. function func()
  3. {
  4.         global $var1, $var2;
  5.         $res = $var1 + $var2;
  6.         $var2 = $var2 * 2;
  7.         $var1 = $var1 * $var2;
  8.         return $res
  9. }
  10. $var1 = 3;
  11. $var2 = 4;
  12. echo func(); // 7
  13. echo $var1; // 12
  14. echo $var2; //8
  15. ?>
4. Champion - 17 Января, 2009 - 09:05:10 - перейти к сообщению
Рекурсия.
Давайте возьмем функцию, которая ищет какой-то элемент ряда Фибоначии(0,1,1,2,3,5,8... - первые два элемента 0 и 1, остальные - сумма двух предыдущих). Напишем ее без рекурсии, потому что пока не знаем что это такое.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. function fibonacci($num) // $num - номер интересующего нас элемента
  3. {
  4.         if ($num < 1) { // номера элемента меньше 1 не существует, заканчиваем функцию
  5.                 return false;
  6.         }
  7.         if ($num <= 2) { // если это один из первых элементов, нетрудно увидеть как они определяются
  8.                 return ($num - 1);
  9.         }
  10.  
  11.         // общий случай. Идем от 3го до требуемого номера
  12.         $pre_pre = 0; // элемент, скажем так, предпредыдущий.
  13.         $current = 1; // текущий
  14.         for ($i = 3; $i <= $num; $i++) {
  15.                 $pre = $current; // бывший текущий становится предыдущим
  16.                 $current = $pre + $pre_pre; // определяем текущий элемент
  17.                 $pre_pre = $pre; // бывший предыдущий становится предпредыдущим
  18.         }
  19.         return $current;
  20. }
  21.  
  22. /*** Основная часть программы ***/
  23. $n = 5;
  24. echo fibonacci($n); // 0,1,1,2,3 - получается 3
  25. ?>
Функция достаточно большая. Давайте посмотрим на нее и увидим, что в этой функции используется цикл. У цикла есть условие выхода и в каждой итерации мы работаем с теми значениями, которые остались после предыдущей итерации. Тогда бывает удобно применять рекурсию. Функция называется рекурсивной, если она вызывает саму себя. Вот как! Давайте перепишем ту функцию с использованием рекурсии.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. // $pre и $pre_pre, как и в тот раз - предыдущий и предпредыдущий элемент.
  3. // $n номер элемента, который мы ищем, НО реально смысл в этой переменной несколько другой. Она хранит в себе количество элементов, которое осталось посчитать
  4. // Считать сумму начинаем с 3го элемента.
  5. function fib($n, $pre = 1, $pre_pre = 0)
  6. {      
  7.         if ($n == 1) return 1;
  8.         if ($n < 1) return false;
  9.         if ($n == 2) // начинали с 3го, поэтому выходим, когда осталось посчитать два.
  10.                 return $pre;
  11.        
  12.         return fib($n - 1, $pre + $pre_pre, $pre);
  13. }
  14.  
  15.  
  16. echo fib(5);
  17. ?>

Как видите, получилось гораздо меньше строк.

Но рекурсию используют не только вместо циклов. Так поступают нечасто, потому что обычно цикл проще. Бывают задачи, которые решить без рекурсии не возможно. Например, построение дерева файлов и подкаталогов в каталоге. С помощью рекурсии, мы бы написали функцию сименем папки в качестве входного параметра. Функция эта берет элемент и, если это файл, просто выводит его имя, но если это директория, то функция вызывает саму себя для этой директории. Когда вы узнаете функции работы с файловой системой, вы ее напишете)
Как реализовать это без рекурсии... затрудняюсь придумать.

При описании рекурсивных функций необходимо предусмотреть условие выхода из функции, чтобы она не была бесконечной. Как и в цикле.
Да, кстати, видите, что параметров у функции 3, а вызывал ее я в программе с одним параметром? Заметьте, при объявлении функции два последних параметра объявлены следующим видом: $variable = value. Им задано значение по умолчанию. Т.е. при вызове передавать эти параметры не обязательно, если, конечно, они не отличаются от значений по умолчанию. Если отличаются, надо передать. Вы также можете передать только один из таких параметров, можете все. Но если вы передаете не все параметры, как php определяет, какие перменные вы имели в виду? Очень просто слева на право. Если передано меньше значений, чем нужно функции, самые правые переменные примут значение по умолчанию.
5. Champion - 17 Января, 2009 - 15:36:34 - перейти к сообщению
Теперь вопросы по главе!
1. В каком варианте (вариантах) функция объявлена синтаксически не верно? Почему?


2. В каком варианте функция function func($a, $b = 4, &$c, $d = 5, $e = 6) вызвана неправильно? Почему? (все переменные имеют значение, функция возвращает значение)


3. Напишите функцию, которая ищет минимум массива и его индекс, чтобы и с индексом и с самим минимумом можно было работать в основной программе.

4. Напишите рекурсивную функцию вычисления факториала.

Ответы тут
6. like_you - 18 Июня, 2010 - 14:45:37 - перейти к сообщению
Существует ли возможность передать переменной имя функции, а потом подставлять эту переменную в двух разных качествах:
1, как текст;
2, как функцию.
Сомневаюсь, что это возможно, но решил спросить...
Пример:
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. $f='is_home()';
  3. if(function_exists(substr_replace ($f,'',-2,2))){
  4. echo "функция $f существует";
  5. if($f) echo " и она имеет значение TRUE";
  6. else echo ", но она имеет значение FALSE";
  7. }
  8. else
  9. echo "функция $f не существует";
  10. ?>

Понемаю, что для обработчика PHP нужно указать когда переменную воспринимать как текст, а когда как функцию, но пока не понял как это реализовать.

П.С. В моём примере кода вставленом выше if($f) постоянно выдаёт TRUE, поскольку переменная воспринимается как текст.
7. Champion - 18 Июня, 2010 - 18:12:10 - перейти к сообщению
like_you пишет:
(function_exists(substr_replace ($f,'',-2,2))
Это что?
И что подразумевается здесь под значением функции?
(Добавление)
Советую нажать на ссылочки в коде и прочитать описание функций, чтобы уяснить непонятности, которые, по-моему, имеют здесь место.
8. like_you - 18 Июня, 2010 - 20:20:15 - перейти к сообщению
Champion пишет:
(function_exists(substr_replace ($f,'',-2,2))

функция substr_replace($f,'',-2,2) убирает со значения переменной $f две скопки () справа. Это сделал для функции function_exists, я просто думал, что она со скобками будет плохо работь, но сейчас проверил и удачно работает и так:
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. $f='is_home()';
  3. echo "функция $f существует";
  4. if($f) echo " и она имеет значение TRUE";
  5. else echo ", но она имеет значение FALSE";
  6. }
  7. else
  8. echo "функция $f не существует";
  9. ?>

(Добавление)
Но всёже интересно узнать возможно ли в переменную вставить функцию, а потом эту переменную использовать где-то в коде.
Да - и под значением функции имеется ввиду булевое значение возвращяемое функцией.
9. Champion - 18 Июня, 2010 - 20:36:45 - перейти к сообщению
Что бы увидеть, что функция возвращает, надо ее вызвать.
PHP:
скопировать код в буфер обмена
  1. function foo() {return 'qwerty';}
  2. $f = 'foo';
  3. echo $f();
Думаю, всё понятно.
10. like_you - 18 Июня, 2010 - 20:49:34 - перейти к сообщению
Бомба! Спасибо большое. То что мне было нужно. Логично до этого врядли можно дойти, разве что методом "тыка" или имея солидный опыт с PHP.
Вообще респект Вам и уважуга! Что сайт суперский, что на форумах можно почитать содержательные посты. Супер!
11. headstik - 30 Июня, 2010 - 15:07:03 - перейти к сообщению
А если, например, для функции func($a = 8, $b = 4, $c = 9, $d = 5, $e = 6) мне необходимо заменить значение третьего аргумента $c, а остальные оставить по умолчанию. Как будет вызываться функция в этом случае?
12. RomAndry - 30 Июня, 2010 - 16:11:44 - перейти к сообщению
как вариант, для таких целей я бы сделал так:
PHP:
скопировать код в буфер обмена
  1.  
  2. func($a = 8, $b = 4, $c = 9, $d = 5, $e = 6) {
  3.     $a = (is_null($a) ? 8 : $a);
  4.     $b = (is_null($b) ? 4 : $b);
  5.     // и.т.п.
  6. }
  7.  

тогда вызов:
PHP:
скопировать код в буфер обмена
  1.  
  2. func(NULL, NULL, 15);
  3.  
13. EuGen - 02 Июля, 2010 - 11:06:41 - перейти к сообщению
Champion пишет:
Функция называется рекурсивной, если она вызывает саму себя

Это утверждение не совсем правильно.
Если быть точным, то это - явная рекурсия. Существует еще так же и неявная, как например, функция func1, вызывающая func2, func2, вызывающая func3 и func3, вызывыющая func1 (элементов такого "кольца" может быть сколько угодно)
14. Druid - 10 Октября, 2010 - 10:20:03 - перейти к сообщению
Хотелось бы показать, что у меня получилось по заданиям в этом уроке. Есть ли какие замечания?
PHP:
скопировать код в буфер обмена
  1. //Напишите функцию, которая ищет минимум массива и его индекс, чтобы и с индексом и с самим минимумом можно было работать в основной программе.
  2. function minfunc ($mass)
  3. {
  4.     global $min, $index;
  5.     $min = $mass[0];
  6.     $index = 0;
  7.     for ($i=1; isset($mass[$i]); $i++)
  8.     {
  9.         if ($mass[$i]<$min)
  10.         {
  11.             $min = $mass[$i];
  12.         }
  13.         $index++;
  14.     }
  15.     return $min;
  16. }
  17. $mass_t = array (4, 6, 43, 3, 93, 2, -9);
  18. echo minfunc($mass_t)." или ".$min."<br />";
  19. echo $index."<br /><br />";

PHP:
скопировать код в буфер обмена
  1. //Напишите рекурсивную функцию вычисления факториала.
  2. function factor_rec ($num, $all = 1, $next = 2)
  3. {
  4.     if ($num < 1) return false;
  5.     elseif ($num >= 2) return factor_rec ($num - 1, $all * $next, $next + 1);
  6.     elseif ($num = 1) return $all;
  7. }
  8. echo factor_rec (5);
15. zardoz - 10 Октября, 2010 - 12:04:22 - перейти к сообщению
Druid пишет:
PHP:
скопировать код в буфер обмена
  1.  
  2.         if ($mass[$i]<$min)
  3.         {
  4.             $min = $mass[$i];
  5.         }
  6.         $index++;
  7.  


Нужно в $index запоминать то текущеее зачение $i при котором наступает минимум:
PHP:
скопировать код в буфер обмена
  1.  
  2.         if ($mass[$i]<$min)
  3.         {
  4.             $min = $mass[$i];
  5.             $index = $i;
  6.         }        
  7.  


С уважением.

 

Powered by ExBB FM 1.0 RC1