 |
|
 |
|
|
|
Последние Файлы GTA 4 |
Последние Файлы GTA-MP |
Реклама |
|
|
|
|
|
[Урок] Всё о функциях языка Pawn
| |
| _Dark_ | Дата: Среда, 03.08.2011, 12:39 | Сообщение # 1 |
|
Постоялец
Группа:
I'm V.I.P.
Сообщений: 379
Награды: 4
Город: Dark://core.dll
Репутация: 169
Замечания: 40%
Статус: 
| Добрый всем день. В этой теме я собираюсь продолжить начатую в этой теме серию моих уроков и рассказать вам о функциях.
В каждом нашем скрипте (да и по сути в любой программе на любом языке программирования) встречаются случаи, когда одну и туже последовательность действий необходимо выполнить несколько раз в разных местах. И в итоге эта последовательность выдает нам уже завершенный результат.
Что же такое функция? Функция - скопление действий. Она принимает аргументы, выполняет с ними некоторые действия, и возвращает уже обработанный результат. К примеру, Сварить варенье - функция принимающая аргументы в виде Фруктов, сахара, воды и еще многого чего, и возвращающая варенье. К слову, функция может возвращать любой тип данных, будь то целое число, дробное число, булев тип или строка. При помощи функций так же можно упростить код скрипта. Code public Имя_Функции(аргументы) // Что такое public, stock и когда можно вообще ничего не писать перед именем функции вы узнаете ниже. Имя функции может содержать латинские буквы, цифры, знак пробела. Начинаться с цифры функция не должна. К слову, тоже самое и с названиями переменных.
Выше я применил термины аргумент и параметр. Что же это такое?
Аргумент - неопределенные данные, которые входят в функцию. Параметр - определенные (известные) данные.
Не понятно?
Когда мы описываем функцию, в ее скобках мы указываем аргументы. А когда вызываем ее - параметры.
Приведу пример простой функции. Функция принимает два аргумента, складывает их, умножает на два и выводит ответ в консоль. Code stock MathFunc(Var1,Var2) { printf("%d", (Var1+Var2)*2); return 1; }
Еще один пример:
Code main() { new Key = 50; printf("%d", Key); Test(Key); printf("%d", Key); }
stock Test(Var1) { Var1 = 1000; printf("%d", Var1); return 1; }
Что должно произойти? Мы создали переменную Key со значением 50. Вывели ее значение в консоль. Вызвали функцию Test с параметром Key, в которой присвоили этому параметру Key значение 1000 и вывели результат в консоль. После выполнения функции опять вывели значение Key в консоль.
Как вы думаете, что же получиться в консоли? Если вы думаете, что в консоли будет текст вида то вы неправы. Все дело в том, что существует
Область видимости
В консоли будет отнюдь не В консоли будет 50 1000 50 Как же так? Мы ведь собственноручно вызвали функцию Test, которая присвоила переменной Key значение 1000!
Функция представляет из себя маааленькую программку.
У этой программки есть свои переменные, а переменные из основной программы она просто не видит.
Так как в эту программку одним из параметров поступила переменная Key со значением 50, это значит что функция вполне ее видела и могла с ним работать. Но на самом деле в функцию передаётся на переменная, а её точная копия, то есть... Ее значение. Поэтому когда наша программка (функция) завершает свое выполнение - все её переменные (копии наших, основных) удаляются.
Передача параметров по ссылке
В примере выше, переменная используемая в функции называется локальной (их может быть бесконечно). Локальная переменная - переменная которая создана, изменяется, удаляется в самой функции, а за пределами функции ее не видно.
Для того, что бы передать функции не значение переменной, а саму переменную, изменения которой будут видны и за пределами функции, существует особый способ. Для этого, перед аргументом функции надо поставить знак амперсайд (&), например, вот так: Code stock Test(&Var1) { Var1 = 1000; printf("%d", Var1); }
Теперь, при вызове этой функции с одним-единственным параметром Key, в функцию передастся не значение переменной, а сама переменная. Что это значит? Это значит, что в консоли после кода Code main() { new Key = 50; printf("%d", Key); Test(Key); printf("%d", Key); }
stock Test(&Var1) { Var1 = 1000; printf("%d", Var1); } Появится 50 1000 1000
При вызове функции без параметра по ссылке, в функцию передается значение переменной, она ее обрабатывает, выдает результат, но переменная вне функции не изменяется. Она остается прежней. При передаче параметра по ссылке, в функцию передается сама переменная, она ее обрабатывает, выдает результат, и изменения которые произошли с переменной в функции видны и вне функции.
Оператор функции - return
Оператор return управляет тем, что функция возвращает. Например, мы напишем такую функцию: Code stock IsPlayerRich(playerid) { if(GetPlayerMoney(playerid) > 1000000) return 1; // Если игрок имеет более 1 млн, то возвращаем return 1 или return true; else return 0; //или return false; } Теперь, мы можем вызвать эту функцию откуда угодно, и основываясь на ее действиях (применяя ветвление, о коем я вам рассказывал в предыдущем уроке) выполнить определенный код. Например, вызовем функцию из OnPlayerDisconnect(playerid)
Code OnPlayerDisconnect(playerid) { if(IsPlayerRich(playerid)) // Если у игрока больше 1 млн. денег (функция вернет return 1) { SendClientMessageToAll(COLOR_LIGHTRED, "От нас ушел богатый игрок!"); } else SendClientMessageToAll(COLOR_LIGHTRED, "От нас ушел игрок!"); return 1; } Прокомментирую. Код if(IsPlayerRich(playerid)) проверяет, возвращает ли функция положительное число ( больше нуля). Если да, то мы пишем "От нас ушел богатый игрок!", если нет, то "От нас ушел игрок!"
Кстати, проверить возвращает ли функция ноль, можно так if(!IsPlayerRich(playerid)). Символ ! указывает на отрицание.
Функция не обязательно должна возвращать что-либо. Например, наша функция Test просто выводит в консоль результат своей работы. В случае, если функция ничего не возвращает (нет оператора return), компилятор вернет значение Null (ничего).
Бывает, что функция должна не только вернуть результат, но и сделать так, что бы он был виден и вне функции. Вот для этого и нужна передача параметров по ссылке, о которой я вам рассказал выше. Создадим функцию сложения двух чисел. Первый параметр мы умножим на двое, а во второй запишем произведение двух параметров.
Code main() { new Key1 = 5; new Key2 = 8; printf("%d", Test(Key1, Key2)); //В консоли появится 13 printf("%d", Key1); // 10 printf("%d", Key2); // 18 }
stock Test(&Var1, &Var2) { new ret = Var1 + Var2; Var1 = Var1 * 2; Var2 = Var1 + Var2; return ret; }
Вот так вот.
Рекурсия
Давайте напишем функцию возведения числа в степень:
Code main() { printf("%d", Test(2, 5)); // Напишет 64 }
stock Test(Var, Num) //Где Var - число которое возводим в степень, где Num - степень в которую возводим. { new var = Var; for(new i; i < Num; i++) { var = var*Var; } return var; }
Как видите, здесь мы использовали цикл, но можно обойтись и без него!
Так вот, Функция называется рекурсивной если вызывает саму себя.
Вот вам пример рекурсивной функции. Возведения числа в степень, но уже без цикла.
Code main() { printf("%d", Test(2, 5)); // Напишет 64 }
stock Test(var,num) { if(num>=0) { return var*Test(var,num-1); } return 1; }
Как это работает? Мы вызываем функцию Test с параметром 2 и 5. Напомню, что в нашей функции первый параметр - число которое возводим в степень, второй параметр - степень. После вызова функции, она проверяет чему равна переменная num если она больше или равна (или равна, потому что отсчет в языках программирования идет с нуля), то функция умножает число на вызов самой себя, при этом num становится меньше на единицу. Зачем? Надо помнить, что необходимо предусматривать выход из рекурсии. Иначе она станет бесконечной и ваш скрипт (а вместе с ним и сервер) зависнет. В моем примере, выход из рекурсии - это if(num>=0) и num-1. Код выполняется только если num больше или равна нулю, а так как при каждом выполнении кода num уменьшается на один, бесконечной рекурсии не будет. Попробуйте убрать выход из рекурсии и проверьте что будет.
Stock и public: что это такое.
Функции объявляются по разному. Можно сделать так:
Code stock Func_Name(var) { return 1; } или так Code forward Func_Name(var); public Func_Name(var) { return 1; } или даже
Первый случай чаще всего используют в инклудах. Если функция с префиксом stock нигде не используется (не вызывается), она не включается компилятором в скрипт. Именно по этому, если вы напишите ее с ошибкой и нигде не будете использовать, компилятор не выдаст ошибку. Но стоит вам ее вызвать, ошибки обнаружатся компилятором и он их вам выведет.
Второй случай с public используется в таймерах, в стандартных функциях и в CallRemoteFunction. Причем он требует обязательного объявления функции, путем forward Имя_Функции(аргументы).
Третий случай - аналог public, но он не требует объявления, но и не может использоваться в таймерах и в CallRemoteFunction. Итак: - Я вам рассказал что такое функции, как их создавать и вызывать
- Почему не следует забывать про область видимости
- Объяснил что такое передача параметров по ссылке и для чего это нужно
- Что такое return и для чего он нужен
- Что такое рекурсия и когда удобно ее применять
- Кратко объяснил о типах функций
Ну вот и всё, что я хотел вам рассказать в данной теме. Я надеюсь что информация вам будет полезна. Копирайты и все такое:
Автор текста, темы, урока - _Dark_ (Если кто-то не в курсе, это я). - При перепечатывании статьи обратная ссылка на данную тему с указанием авторства [blink]обязательна[/blink].
От плюсика не откажусь Если есть вопросы - задавайте. Продолжение, как обычно, следует
Если баги возникают из ниоткуда - ты на правильном пути. (с)
Сообщение отредактировал _Dark_ - Среда, 03.08.2011, 13:23 |
| |
|
|
| Ghost-X | Дата: Среда, 03.08.2011, 19:37 | Сообщение # 2 |
|
Мастер джэдай
Группа:
Продвинутые
Сообщений: 3548
Награды: 36
Город: Наб. Челны
Репутация: 856
Замечания: 40%
Статус: 
| Quote (_Dark_) stock IsPlayerRich(playerid) { if(GetPlayerMoney(playerid) > 1000000) return 1; // Если игрок имеет более 1 млн, то возвращаем return 1 или return true; else return 0; //или return false; } Я же тебе вчера написал пример как раз для этого: Code stock IsPlayerRich(playerid) { return (GetPlayerMoney > 1000000)? 1 : 0; }
Спор на форуме, все равно что олимпиада среди умственно отсталых: даже если ты победил, ты все равно гермофродит. Хочешь остаться при своем мнении - держи его при себе.
|
| |
|
|
| Dima-kun | Дата: Среда, 03.08.2011, 20:10 | Сообщение # 3 |
|
Группа:
Разработчики
Сообщений: 6269
Награды: 72
Репутация: 3512
Замечания: 0%
Статус: 
| Quote (Ghost-X) stock IsPlayerRich(playerid) { return (GetPlayerMoney > 1000000)? 1 : 0; } можно и бес скобок
Code stock IsPlayerRich(playerid) return (GetPlayerMoney > 1000000)? 1 : 0;
Мои работы: [INC] Сборник d_includes [LAST],[FS] New Demage Effect,[FS]Авто-Поворотники, [FS]mp3player,[FS] Fun Chat Game,[FS] Car Buy Syst,[FS] Anti-AirBrk,[INC] d_setpos, [GM] Game Move v0.1,[FS+include]Super-Armour [0.3z],[FS + Include] Multi Checkpoints
|
| |
|
|
| Ghost-X | Дата: Среда, 03.08.2011, 20:15 | Сообщение # 4 |
|
Мастер джэдай
Группа:
Продвинутые
Сообщений: 3548
Награды: 36
Город: Наб. Челны
Репутация: 856
Замечания: 40%
Статус: 
| Morino_Reigan, можно и без скобок, но чтобы не запутаться в этих конструкциях я их не убираю
Спор на форуме, все равно что олимпиада среди умственно отсталых: даже если ты победил, ты все равно гермофродит. Хочешь остаться при своем мнении - держи его при себе.
|
| |
|
|
| _Dark_ | Дата: Среда, 03.08.2011, 21:24 | Сообщение # 5 |
|
Постоялец
Группа:
I'm V.I.P.
Сообщений: 379
Награды: 4
Город: Dark://core.dll
Репутация: 169
Замечания: 40%
Статус: 
| Ghost-X, я знаю, но я предпочитаю простоту в чтении кода, поэтому в уроках пишу все по простому.
Если баги возникают из ниоткуда - ты на правильном пути. (с)
|
| |
|
|
| Великс | Дата: Среда, 03.08.2011, 22:05 | Сообщение # 6 |
|
Мастер джэдай
Группа:
Модераторы
Сообщений: 3931
Награды: 287
Город: Мухосранск
Репутация: 8429
Замечания: 0%
Статус: 
| Quote (_Dark_) if(IsPlayerRich(playerid)) // Если у игрока больше 1 млн. денег (функция вернет return 1) { SendClientMessageToAll(COLOR_LIGHTRED, "От нас ушел богатый игрок!"); } else SendClientMessageToAll(COLOR_LIGHTRED, "От нас ушел игрок!"); return 1; Что и требовалось доказать, я был прав. Спасибо.
[off]Фус, вс**и!111!11!!!!1[/off]
[cut=freestyle] ПРИНЕСЛА СОРОКА В РОТЕ ПРИВОРОТ , СВЯЗАЛАСЬ НА ШЕЕ ЛОЗА И БЫЛ ПОЛНЫЙ ПИ**ЕЦ, ОСЕНЬ С ЗИМОЙ, ЛЕТО С ВЕСНОЙ, ГУСЬ С ЛИСОЙ, ЕБ**ИСЬ ВО ТЬМЕ НОЧНОЙ. УЗЕЛ ЗАВЯЗАЛСЯ, ПЕПЕЛ РАЗМЕТАЛСЯ И ЛИСА ЗАБЕРЕМЕНЕЛА. ВМЕСТЕ ВЕК ВЕКОВАТЬ, ПУТУ НЕ РАЗВЯЗАТЬ. ТРАВА ПЕРЕПЛЕТИСЬ, УЗЕЛ ЗАВОРОЖИСЬ. ЛЕНАРУ ДАЙ УМА И ЛЕНУ НА ВЕЛЕ НЕ ОТРАЗИСЬ,ЕБ*СЬ ЕБ*СЬ ЕБ*СЬ © Хасан [/cut]
|
| |
|
|
| _Dark_ | Дата: Среда, 03.08.2011, 22:36 | Сообщение # 7 |
|
Постоялец
Группа:
I'm V.I.P.
Сообщений: 379
Награды: 4
Город: Dark://core.dll
Репутация: 169
Замечания: 40%
Статус: 
| Quote (|MrVelix|) Что и требовалось доказать, я был прав. Спасибо. Я не в теме О чем спор был?
Если баги возникают из ниоткуда - ты на правильном пути. (с)
|
| |
|
|
| Великс | Дата: Среда, 03.08.2011, 22:58 | Сообщение # 8 |
|
Мастер джэдай
Группа:
Модераторы
Сообщений: 3931
Награды: 287
Город: Мухосранск
Репутация: 8429
Замечания: 0%
Статус: 
| Quote (_Dark_) Я не в теме  О чем спор был? С Fus1 был спор изза команды. Его конструкция: Code if(strcmp(cmd, "/deletecar", true) == 0) { if(GetPlayerState(playerid) == PLAYER_STATE_DRIVER) { new vehicleid = GetPlayerVehicleID(playerid); new m = GetVehicleModel(vehicleid)-400; SendClientMessage(playerid, COLOR_WHITE, "Машина уничтожена"); DestroyVehicle(vehicleid); return 1; } } Моя конструкция: Code if(strcmp(cmd, "/deletecar", true) == 0) { if(GetPlayerState(playerid) == PLAYER_STATE_DRIVER) { new vehicleid = GetPlayerVehicleID(playerid); SendClientMessage(playerid, COLOR_WHITE, "Машина уничтожена"); DestroyVehicle(vehicleid); return 1; } else SendClientMessage(playerid, COLOR_WHITE, "* Вы не в авто"); return 1; } Его ответ: Quote (Fus1) Quote (|MrVelix|) else SendClientMessage(playerid, COLOR_WHITE, "* Вы не в авто"); return 1; лол , нах
У кого код вернее? Может я действительно туплю?
[cut=freestyle] ПРИНЕСЛА СОРОКА В РОТЕ ПРИВОРОТ , СВЯЗАЛАСЬ НА ШЕЕ ЛОЗА И БЫЛ ПОЛНЫЙ ПИ**ЕЦ, ОСЕНЬ С ЗИМОЙ, ЛЕТО С ВЕСНОЙ, ГУСЬ С ЛИСОЙ, ЕБ**ИСЬ ВО ТЬМЕ НОЧНОЙ. УЗЕЛ ЗАВЯЗАЛСЯ, ПЕПЕЛ РАЗМЕТАЛСЯ И ЛИСА ЗАБЕРЕМЕНЕЛА. ВМЕСТЕ ВЕК ВЕКОВАТЬ, ПУТУ НЕ РАЗВЯЗАТЬ. ТРАВА ПЕРЕПЛЕТИСЬ, УЗЕЛ ЗАВОРОЖИСЬ. ЛЕНАРУ ДАЙ УМА И ЛЕНУ НА ВЕЛЕ НЕ ОТРАЗИСЬ,ЕБ*СЬ ЕБ*СЬ ЕБ*СЬ © Хасан [/cut]
Сообщение отредактировал [MrVelix] - Среда, 03.08.2011, 23:06 |
| |
|
|
| Эльдар | Дата: Среда, 03.08.2011, 23:03 | Сообщение # 9 |
|
Местный
Группа:
Пользователи
Сообщений: 696
Награды: 55
Город: Москва
Репутация: 335
Замечания: 100%
Статус: 
| [MrVelix], Ответ и так понятен
[b]Прощай SRC:DDDDDDD[/b]
|
| |
|
|
| Ghost-X | Дата: Среда, 03.08.2011, 23:15 | Сообщение # 10 |
|
Мастер джэдай
Группа:
Продвинутые
Сообщений: 3548
Награды: 36
Город: Наб. Челны
Репутация: 856
Замечания: 40%
Статус: 
| [MrVelix], а как же другие PLAYER_STATE? Типа пассажира? З.Ы. для продвинутых - http://wiki.sa-mp.com/wiki/IsPlayerInAnyVehicle
Спор на форуме, все равно что олимпиада среди умственно отсталых: даже если ты победил, ты все равно гермофродит. Хочешь остаться при своем мнении - держи его при себе.
|
| |
|
|
| Великс | Дата: Среда, 03.08.2011, 23:37 | Сообщение # 11 |
|
Мастер джэдай
Группа:
Модераторы
Сообщений: 3931
Награды: 287
Город: Мухосранск
Репутация: 8429
Замечания: 0%
Статус: 
| Ghost-X, я знаю про эту функцию, но изначально планировалось сделать эту команду только для водителей.
[cut=freestyle] ПРИНЕСЛА СОРОКА В РОТЕ ПРИВОРОТ , СВЯЗАЛАСЬ НА ШЕЕ ЛОЗА И БЫЛ ПОЛНЫЙ ПИ**ЕЦ, ОСЕНЬ С ЗИМОЙ, ЛЕТО С ВЕСНОЙ, ГУСЬ С ЛИСОЙ, ЕБ**ИСЬ ВО ТЬМЕ НОЧНОЙ. УЗЕЛ ЗАВЯЗАЛСЯ, ПЕПЕЛ РАЗМЕТАЛСЯ И ЛИСА ЗАБЕРЕМЕНЕЛА. ВМЕСТЕ ВЕК ВЕКОВАТЬ, ПУТУ НЕ РАЗВЯЗАТЬ. ТРАВА ПЕРЕПЛЕТИСЬ, УЗЕЛ ЗАВОРОЖИСЬ. ЛЕНАРУ ДАЙ УМА И ЛЕНУ НА ВЕЛЕ НЕ ОТРАЗИСЬ,ЕБ*СЬ ЕБ*СЬ ЕБ*СЬ © Хасан [/cut]
|
| |
|
|
| Великс | Дата: Среда, 03.08.2011, 23:37 | Сообщение # 12 |
|
Мастер джэдай
Группа:
Модераторы
Сообщений: 3931
Награды: 287
Город: Мухосранск
Репутация: 8429
Замечания: 0%
Статус: 
| Ghost-X, я знаю про эту функцию, но изначально планировалось сделать эту команду только для водителей.
[cut=freestyle] ПРИНЕСЛА СОРОКА В РОТЕ ПРИВОРОТ , СВЯЗАЛАСЬ НА ШЕЕ ЛОЗА И БЫЛ ПОЛНЫЙ ПИ**ЕЦ, ОСЕНЬ С ЗИМОЙ, ЛЕТО С ВЕСНОЙ, ГУСЬ С ЛИСОЙ, ЕБ**ИСЬ ВО ТЬМЕ НОЧНОЙ. УЗЕЛ ЗАВЯЗАЛСЯ, ПЕПЕЛ РАЗМЕТАЛСЯ И ЛИСА ЗАБЕРЕМЕНЕЛА. ВМЕСТЕ ВЕК ВЕКОВАТЬ, ПУТУ НЕ РАЗВЯЗАТЬ. ТРАВА ПЕРЕПЛЕТИСЬ, УЗЕЛ ЗАВОРОЖИСЬ. ЛЕНАРУ ДАЙ УМА И ЛЕНУ НА ВЕЛЕ НЕ ОТРАЗИСЬ,ЕБ*СЬ ЕБ*СЬ ЕБ*СЬ © Хасан [/cut]
|
| |
|
|
| Ghost-X | Дата: Среда, 03.08.2011, 23:50 | Сообщение # 13 |
|
Мастер джэдай
Группа:
Продвинутые
Сообщений: 3548
Награды: 36
Город: Наб. Челны
Репутация: 856
Замечания: 40%
Статус: 
| Вернее было бы сделать так: Code if(!strcmp(cmd,"/deletecar",true)) { if(GetPlayerState(playerid) != PLAYER_STATE_DRIVER) { SendClientMessage(playerid, COLOR_WHITE, "* Вы не в авто"); return 1; } new vehicleid = GetPlayerVehicleID(playerid); SendClientMessage(playerid, COLOR_WHITE, "Машина уничтожена"); DestroyVehicle(vehicleid); return 1; }
Спор на форуме, все равно что олимпиада среди умственно отсталых: даже если ты победил, ты все равно гермофродит. Хочешь остаться при своем мнении - держи его при себе.
|
| |
|
|
| Дикий_Билл | Дата: Среда, 03.08.2011, 23:59 | Сообщение # 14 |
|
Освоившийся
Группа:
Пользователи
Сообщений: 76
Награды: 1
Город: ...
Репутация: -46
Замечания: 100%
Статус: 
| Quote (Ghost-X) if(GetPlayerState(playerid) != PLAYER_STATE_DRIVER) { SendClientMessage(playerid, COLOR_WHITE, "* Вы не в авто"); return 1; } А лучше во обще так: Code if(GetPlayerState(playerid) != PLAYER_STATE_DRIVER) return SendClientMessage(playerid, COLOR_WHITE, "* Вы не в авто");
|
| |
|
|
| Dima-kun | Дата: Четверг, 04.08.2011, 07:01 | Сообщение # 15 |
|
Группа:
Разработчики
Сообщений: 6269
Награды: 72
Репутация: 3512
Замечания: 0%
Статус: 
| Quote (|MrVelix|) new m = GetVehicleModel(vehicleid)-400; вообще не понял нафиг это в его коде?
Мои работы: [INC] Сборник d_includes [LAST],[FS] New Demage Effect,[FS]Авто-Поворотники, [FS]mp3player,[FS] Fun Chat Game,[FS] Car Buy Syst,[FS] Anti-AirBrk,[INC] d_setpos, [GM] Game Move v0.1,[FS+include]Super-Armour [0.3z],[FS + Include] Multi Checkpoints
|
| |
|
|
|
 |
|
 | |
| |
|