Difference between revisions of "Ru:Introduction to SourcePawn"

From AlliedModders Wiki
Jump to: navigation, search
 
(76 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 
Это руководство призвано дать Вам самые основные представления по основам написания сприптов в SourcePawn. [[Pawn]] это "скриптовый" язык используемый для внедрения функциональности в других программах. Это означает, что это не самостоятельный язык, как C++ или Java, и его элементы будут отличаться в различных приложениях. SourcePawn это вариация языка Pawn используемая в [[SourceMod]].
 
Это руководство призвано дать Вам самые основные представления по основам написания сприптов в SourcePawn. [[Pawn]] это "скриптовый" язык используемый для внедрения функциональности в других программах. Это означает, что это не самостоятельный язык, как C++ или Java, и его элементы будут отличаться в различных приложениях. SourcePawn это вариация языка Pawn используемая в [[SourceMod]].
  
Это руководство не расскажет Вам как писать SourceMod плагины; оно предназначено для получения общих представлений о синтаксисе и семантике этого языка. Читайте отдельную статью, [[Introduction to SourceMod Plugins]] для введения в SourceMod API.
+
Это руководство не расскажет Вам как писать SourceMod плагины; оно предназначено для получения общих представлений о синтаксисе и семантике этого языка. Читайте отдельную статью [[Ru:Introduction to SourceMod Plugins]] (Введение в SourceMod плагины), для введения в SourceMod API.
  
 
=Введение для новичков=
 
=Введение для новичков=
Line 22: Line 22:
  
 
В SourcePawn, переменные бывают двух типов, которые будут более подробно описаны далее.
 
В SourcePawn, переменные бывают двух типов, которые будут более подробно описаны далее.
*Числовые (могут содержать только произвольные числовые данные), как показано выше.
+
*Однострочные (могут содержать только произвольные числовые данные), как показано выше.
*Строковые (могут содержать целый ряд текстовых символов)
+
*Многострочные (могут содержать целый ряд текстовых символов)
  
==Functions==
+
==Функции==
The next important concept is '''functions'''. Functions are symbols or names that perform an action. That means when you activate them, they carry out a specific sequence of code. There are a few types of functions, but every function is activated the same way. "Calling a function" is the term for invoking a function's action. Function calls are constructed like this:
+
Следующим важным понятием являются '''функции'''. Функции идентификаторов или имен, которые выполняют действия. Это означает, что когда вы их активируете, они выполняют конкретную последовательность кода. Есть несколько типов функций, но все функции активируется одинаковым образом. "Вызов функции" является термином ссылающимся на функцию действия. Функция числовых переменных строятся так:
<pawn>function(<parameters>)</pawn>
+
<pawn>функция(<параметры>)</pawn>
  
Examples:
+
Примеры:
  
<pawn>show(56);  //Activates "show" function, and gives the number 56 to it
+
<pawn>show(56);  //Активирует функцию "show" и присваивает ей число 56
show();    //Activates "show" function with no data, blank
+
show();    //Активирует функцию "show" без каких-либо данных, пустую
show(a);    //Activates "show" function, gives a variable's contents as data
+
show(a);    //Активирует функцию "show" и присваивает ей переменную с данными
 
</pawn>
 
</pawn>
  
Every piece of data passed to a function is called a '''parameter'''. A function can have any number of parameters (there is a "reasonable" limit of 32 in SourceMod). Parameters will be explained further in the article.
+
Каждый фрагмент данных передаваемый вызываемой функции, называется '''параметр'''. Функция может иметь любое количество параметров (но есть "допустимый" предел в SourceMod: 32). Параметры будут описаны далее в этой статье.
  
==Comments==
+
==Комментарии==
Note any text that appears after a "//" is considered a "comment" and is not actual code. There are two comment styles:
+
Примечания и любой текст, который пишется после "//" считается "Комментарием", а не фактическим кодом. Есть два стиля комментариев:
*<tt>//</tt> - Double slash, everything following on that line is ignored.
+
*<tt>//</tt> - Двойная косая черта, всё следующие после этой строки игнорируется.
*<tt>/* */</tt> - Multi-line comment, everything in between the asterisks is ignored. You cannot nest these.
+
*<tt>/* */</tt> - Много-строчный комментарий, весь текст, внутри звездочек игнорируются. You cannot nest these.
  
 
+
==Массивы==
==Block Coding==
+
Описание массивов. Вы можете группировать код в виде "массивов", разделенных { и }. Это фактически создает возможность работать с целым массивом как с одним оператором. Например:
The next concept is block coding. You can group code into "blocks" separated by { and }. This effectively makes one large block of code act as one statement. For example:
 
  
 
<pawn>{
 
<pawn>{
Line 54: Line 53:
 
}</pawn>
 
}</pawn>
  
Block coding using braces is used everywhere in programming. Blocks of code can be nested within each other. It is a good idea to adapt a consistent and readable indentation style early on to prevent spaghetti-looking code.
+
Массивы с фигурными скобками используются достаточно широко в программировании. Массивы кода могут быть вложенными друг в друга. Это хорошая возможность адаптировать последовательность когда и сделать его удобочитаемым, благодаря отступам код не будет смотреться как одна большая и длинная макаронина.
  
  
=Language Paradigms=
+
=Особенности языка=
Pawn may seem similar to other languages, like C, but it has fundamental differences. It is not important that you immediately understand these differences, but they may be helpful if you're familiar with another language already.
+
Pawn может показаться очень похожим на другие языки программирования, например C, но Pawn от них фундаментально отличается. Не столь важно, чтобы Вы сейчас же поняли его отличия, но они понадобятся, если Вы уже знаете один из языков программирования.
*'''Pawn is not typed.''' Pawn only has one data type, the '''cell'''. This will be explained in detail later. [Below it says that there are two types: cell and string.]
+
*'''Pawn не печатает''' Pawn имеет только один тип данных - '''однострочный'''. Подробнее будет описано позже. [В дальнейшем автор рассказывает, что существует два типа данных: однострочный и многострочный]
*'''Pawn is not garbage collected.''' Pawn, as a language, has no built-in memory allocation, and thus has no garbage. If a function allocates memory, you may be responsible for freeing it.
+
*'''Pawn не собирает мусор''' Pawn, как язык, не имеет встроенных ресурсов памяти, и потому он не мусорит. Если функция выделит память, то Вы отвечаете за её освобождение.
*'''Pawn is not object oriented.''' Pawn is procedural, and relies on subroutines. It also does not have C structs.
+
*'''Pawn не объектно-ориентированный язык''' Pawn является процедурным, и полагается на подпрограммы. Также у него нету C подобных структур.
*'''Pawn is not functional.''' Pawn is procedural, and does not support lambda functions or late binding or anything else you might find in a very high-level language, like Python or Ruby.
+
*'''Pawn не функциональный.''' Pawn является процедурным, и не поддерживает функции "лямбды" (Lambda), поздние присвоения, и все то, что можно найти в языках высшего уровня, таких как Python и Ruby.
*'''Pawn is single-threaded.''' As of this writing, Pawn is not thread safe.   
+
*'''Pawn однопоточный''' As of this writing, Pawn is not thread safe.   
*'''Pawn is not interpreted.''' Well, it "sort of" is. It gets interpreted at a very low level. You must run your code through a compiler, which produces a binary. This binary will work on any platform that the host application uses. This speeds up loading time and lets you check errors easier.
+
*'''Pawn не интерпретируемый''' Ну, почти. Он интерпретируется на очень низком уровне. Вы должны скомпилировать код, из которого получится бинарный файл. Эта программа будет работать на той платформе, которую использует хост. Это ускоряет загрузку и позволяет легче находить ошибки.
  
These language design decisions were made by ITB CompuPhase. It is designed for low-level embedded devices and is thus very small and very fast.
+
Этот язык был выпущен ITB CompuPhase. Язык разработан для устройств низкого уровня и таким образом конечные программы очень маленькие по размеру и очень быстрые.
  
=Variables=
+
=Переменные=
In Pawn there are two variable types: the '''cell''' and the '''String'''. A cell can store 32 bits of numerical data. A String is a sequential/flat list of UTF-8 text characters.
+
В Pawn есть всего два типа переменных: '''однострочные''' и '''многострочные'''. Однострочные могут содержать 32 бита цифровых данных. Многострочные - последовательный список из UTF-8 символов.
  
A '''cell''' has no inherent type, however, cells can be '''tagged'''. A tag lets you enforce where certain cells can be usedThe default tags are:
+
'''однострочные''' не имеет своего типа, однако они могут быть '''маркированы'''(tagged). Тег позволяет Вам указывать, где определенную ячейку можно использоватьТипичные теги:
*(nothing), or '''_''' - No tagUsually used for whole numbers ([http://en.wikipedia.org/wiki/Integer Integers]).
+
*(пусто), или '''_''' - Нет тегаОбычно используют для целых чисел ([http://en.wikipedia.org/wiki/Integer Integers]).
*'''Float''' - Used for floating point (fractional) numbers.
+
*'''Float''' - используют для чисел с плавающей точкой (небольших).
*'''bool''' - Used for storing either '''true''' or '''false'''.
+
*'''bool''' - используют для хранения  значений '''true''' (истина) или '''false''' (ложь).
  
Strings are different and will be explained in the next sections.
+
Со строками все по другому, они будут рассмотрены далее.
  
==Declaration==
+
==Объявления==
Examples of different valid variable declarations:
+
Примеры разных правильных объявлений переменных.
 
<pawn>
 
<pawn>
 
new a = 5;
 
new a = 5;
 
new Float:b = 5.0;
 
new Float:b = 5.0;
 
new bool:c = true;
 
new bool:c = true;
new bool:d = 0;      //Works because 0 is false
+
new bool:d = 0;      //Работает, поскольку 0 равно false (ложь) 
 
</pawn>
 
</pawn>
  
Invalid variable usage:
+
Неправильные объявления переменных
 
<pawn>
 
<pawn>
new a = 5.0;        //Tag mismatch. 5.0 is tagged as Float
+
new a = 5.0;        //Несоответствие тегов. 5.0 с тегом Float
new Float:b = 5;    //Tag mismatch. 5 is not tagged.
+
new Float:b = 5;    //Несоответствие тегов. 5 без тега.
 
</pawn>
 
</pawn>
  
If a variable is not assigned upon declaration, it will be set to 0.  For example:
+
Если переменная не определена в объявлении то ее значения станет 0
 
<pawn>
 
<pawn>
new a;        //Set to 0
+
new a;        //значение 0
new Float:b;  //Set to 0.0
+
new Float:b;  //значение 0.0
new bool:c;  //Set to false
+
new bool:c;  //значение false
 
</pawn>
 
</pawn>
  
==Assignment==
+
==Присвоение==
Variables can be re-assigned data after they are created. For example:
+
Переменным могут быть присвоены данные после создания. Пример:
 
<pawn>new a, Float:b, bool:c;
 
<pawn>new a, Float:b, bool:c;
  
Line 109: Line 108:
 
</pawn>
 
</pawn>
  
 +
=Массивы=
 +
Массив это последовательность данных в последовательном списке. Массивы очень полезны для хранения нескольких единиц данных в одной переменной, а зачастую могут значительно упростить многие задачи.
  
=Arrays=
+
==Описание==
An array is a sequence of data in a sequential list.  Arrays are useful for storing multiple pieces of data in one variable, and often greatly simplify many tasks.
+
Массив объявляется с помощью квадратных скобок. Вот некоторые примеры массивов:
 
 
==Declaration==
 
An array is declared using brackets. Some examples of arrays:
 
 
<pawn>
 
<pawn>
new players[32];    //Stores 32 cells (numbers)
+
new players[32];    //Набор из 32 однострочных (числовых) данных
new Float:origin[3]; //Stores 3 floating point numbers
+
new Float:origin[3]; //Набор из 3 чисел с плавающей точкой
 
</pawn>
 
</pawn>
  
By default, arrays are initialized to 0. You can assign them different default values, however:
+
По умолчанию, массивам присваиваются нули. Вы можете присвоить им разные значения по умолчанию, однако:
 
<pawn>
 
<pawn>
new numbers[5] = {1, 2, 3, 4, 5};      //Stores 1, 2, 3, 4, 5 in the cells.
+
new numbers[5] = {1, 2, 3, 4, 5};      //Набор 1, 2, 3, 4, 5 из однострочных данных.
new Float:origin[3] = {1.0, 2.0, 3.0};  //Stores 1.0, 2.0, 3.0 in the cells.
+
new Float:origin[3] = {1.0, 2.0, 3.0};  //Набор 1.0, 2.0, 3.0 из однострочных данных.
 
</pawn>
 
</pawn>
  
You can leave out the array size if you're going to pre-assign data to it. For example:
+
Вы можете оставить массив без размера, если вы собираетесь заранее присвоить ему данные. Например:
 
<pawn>
 
<pawn>
 
new numbers[] = {1, 3, 5, 7, 9};
 
new numbers[] = {1, 3, 5, 7, 9};
 
</pawn>
 
</pawn>
  
The compiler will automatically deduce that you intended an array of size 5.
+
Компилятор будет автоматически делать вывод о том, что Вы хотите получить массив размером 5.
  
==Usage==
+
==Использование==
Using an array is just like using a normal variable. The only difference is the array must be '''indexed'''. Indexing an array means choosing the element which you wish to use.
+
Использование массива равносильно использованию обычных переменной. Единственное отличие массива состоит в том, что он должен быть '''индексируемым'''. Индексирование массива означает присутствие возможности выбрать элемент, который Вы хотите использовать.
  
For example, here is an example of the above code using indexes:
+
Вот пример кода с использованием индексов:
 
<pawn>
 
<pawn>
 
new numbers[5], Float:origin[3];
 
new numbers[5], Float:origin[3];
Line 150: Line 148:
 
</pawn>
 
</pawn>
  
Note that the '''index''' is what's in between the brackets. The index always starts from 0. That is, if an array has N elements, its valid indexes are from 0 to N-1. Accessing the data at these indexes works like a normal variable.
+
Заметим, что '''индекс''' это текст, который находится в квадратных скобках. Индекс всегда начинается с нуля. То есть, если массив имеет N элементов, его действительный индекс от 0 до N-1. Доступ к данным с индексами работает так же, как с обычной переменной.
  
To use an incorrect index will cause an error. For example:
+
Использование неверного индекса вызовет ошибку. Например:
 
<pawn>
 
<pawn>
 
new numbers[5];
 
new numbers[5];
Line 158: Line 156:
 
numbers[5] = 20;</pawn>
 
numbers[5] = 20;</pawn>
  
This may look correct, but 5 is not a valid index. The highest valid index is 4.
+
Это может выглядеть верно, но число 5 не является допустимым индексом. Наибольшим значением индекса является число 4.
  
You can use any expression as an index. For example:
+
Вы можете использовать любые выражения, как индекс. Например:
 
<pawn>new a, numbers[5];
 
<pawn>new a, numbers[5];
  
a = 1;                  //Set a = 1
+
a = 1;                  //Сделает a = 1
numbers[a] = 4;          //Set numbers[1] = 4
+
numbers[a] = 4;          //Сделает numbers[1] = 4
numbers[numbers[a]] = 2; //Set numbers[4] = 2
+
numbers[numbers[a]] = 2; //Сделает numbers[4] = 2
 
</pawn>
 
</pawn>
  
Expressions will be discussed in depth later in the article.
+
Выражения будут обсуждаться подробнее в конце статьи.
  
=Strings=
+
=Строки=
Strings are a convenient method of storing text. The characters are stored in an array. The string is terminated by a '''null terminator''', or a 0. Without a null terminator, Pawn would not know where to stop reading the string. All strings are UTF-8 in SourcePawn.
+
Строки являются удобным способом хранения текста. Символы хранятся в массиве. Строка ограничивается '''нулевым символом''', или 0. Без нулевого символа, Pawn не знает, где остановить чтение строки. Все строки в SourcePawn используют кодировку UTF-8.
  
Notice that Strings are a combination of arrays and cells. Unlike other languages, this means you must know how much space a string will use in advance. That is, strings are not dynamic. They can only grow to the space you allocate for them.
+
Отметим, что строки имеют комбинацию из массивов и однострочных переменных. В отличие от других языков, это означает, что Вы должны знать заранее, как много места будут использовать строки. Это означает, что строки не являются динамичными. Они могут лишь вырасти до размера, которым Вы их ограничили.
  
''Note for experts: They're not actually cells. SourcePawn uses 8-bit storage for String arrays as an optimization. This is what makes String a type and not a tag.''
+
''Примечание для специалистов: они фактически не однострочные. SourcePawn использует 8-битный строки для хранения массивов в качестве оптимизации. Это и есть то, что делает строки типом, а не меткой.''
  
==Usage==
+
==Использование==
Strings are declared almost equivalently to arrays. For example:
+
Строки были созданы почти в равной степени и для массивов. Например:
 
<pawn>
 
<pawn>
 
new String:message[] = "Hello!";
 
new String:message[] = "Hello!";
Line 184: Line 182:
 
</pawn>
 
</pawn>
  
These are equivalent to doing:
+
Это равносильно следующему:
 
<pawn>
 
<pawn>
 
new String:message[7], String:clams[6];
 
new String:message[7], String:clams[6];
Line 203: Line 201:
 
</pawn>
 
</pawn>
  
Although strings are rarely initialized in this manner, it is very important to remember the concept of the null terminator, which signals the end of a string. The compiler, and most SourceMod functions will automatically null-terminate for you, so it is mainly important when manipulating strings directly.
+
Хотя строки редко инициализируют таким образом, очень важно помнить о концепции нулевого символа, который свидетельствует о конце строки. Компилятор и большинство SourceMod функций будут автоматически остановлены нулевым символом, поэтому он является очень важным, при манипулировании строками напрямую.
  
Note that a string is enclosed in double-quotes, but a character is enclosed in single quotes.
+
Заметим, что строка должна быть заключена в двойных кавычках, а символ в одиночных.
  
==Characters==
+
==Символы==
A character of text can be used in either a String or a cell. For example:
+
Особенность текста может быть использована в любой строке или однострочной переменной. Например:
 
<pawn>new String:text[] = "Crab";
 
<pawn>new String:text[] = "Crab";
 
new clam;
 
new clam;
  
clam = 'D';        //Set clam to 'D'
+
clam = 'D';        //Устанавливает однострочной переменной значение 'D'
text[0] = 'A';      //Change the 'C' to 'A', it is now 'Arab'
+
text[0] = 'A';      //Меняет 'C' на 'A', сейчас получилось 'Arab'
clam = text[0];    //Set clam to 'A'
+
clam = text[0];    //Устанавливает однострочной переменной значение 'A'
text[1] = clam;    //Change the 'r' to 'A', is is now 'AAab'
+
text[1] = clam;    //Меняет 'r' на 'A', сейчас получилось 'AAab'
 
</pawn>
 
</pawn>
  
What you can't do is mix character arrays with strings. The internal storage is different. For example:
+
То, что вы не можете сделать, это соотнести символы массивов со строками. Внутреннее хранение отличается. Например:
 
<pawn>
 
<pawn>
new clams[] = "Clams";                      //Invalid, needs String: type
+
new clams[] = "Clams";                      //Не верно, нужен тип String:
new clams[] = {'C', 'l', 'a', 'm', 's', 0};  //Valid, but NOT A STRING.
+
new clams[] = {'C', 'l', 'a', 'm', 's', 0};  //Верно, но это НЕ СТРОКА.
 
</pawn>
 
</pawn>
  
 +
=Функции=
 +
Функции, как отмечалось ранее, имеют отдельные составляющие кода, которые выполняют определенные действия. Функции могут быть задействованы или '''вызвоны''' с '''параметрами''', которые дают особые настройки.
  
=Functions=
+
Существуют два типа вызова функции:
Functions, as stated before, are isolated blocks of code that perform an action.  They can be invoked, or '''called''', with '''parameters''' that give specific options.
+
*'''прямой вызов''' - Вы специально вызываете функцию в своем коде.
 
+
*'''обратный вызов''' - Применение вызова функций в Вашем коде, как если бы это было событием триггера (совокупность условий, инициирующих выполнение действия).
There are two types of ways functions are called:
 
*'''direct call''' - You specifically call a function in your code.
 
*'''callback''' - The application calls a function in your code, as if it were an event trigger.
 
  
There are five types of functions:
+
Существуют шесть видов функций:
*'''native''': A direct, internal function provided by the application.
+
*'''native''': Прямая, внутренняя функция, предусмотренная в приложении.
*'''public''': A callback function that is visible to the application and other scripts.
+
*'''public''': Функция обратного вызова, что делает её видимой для приложения и других сценариев.
*'''normal''': A normal function that only you can call.
+
*'''normal''': Нормальная функция, которую Вы можете только вызвать.
*'''stock''': A normal function provided by an include file. If unused, it won't be compiled.
+
*'''static''': The scope of this function is restricted to the current file, can be used in combination with stock.
*'''forward''': This function is a global event provided by the application. If you implement it, it will be a callback.
+
*'''stock''': Нормальная функция, предусмотренная если включает в себя файл. Если не используется, то не компилируется.
 +
*'''forward''': Эта функция представляет собой глобальное событие, предусмотренная приложением. Если Вы её привели в исполнение, она будет вызвона.
  
All code in Pawn must exist in functions. This is in contrast to languages like PHP, Perl, and Python which let you write global code. That is because Pawn is a callback-based language: it responds to actions from a parent application, and functions must be written to handle those actions. Although our examples often contain free-floating code, this is purely for demonstration purposes. Free-floating code in our examples implies the code is part of some function.
+
Весь код в Pawn должен существовать в функциях. Это основное отличие от языков, таких как PHP, Perl и Python, которые позволяют Вам писать глобальный код. Это происходит потому, что Pawn вызывается на основе другого языка: он реагирует на действия от родительского приложения, и функции должны быть написаны для обработки этих действий. Хотя наш пример, часто содержат свободно плавающий код, это сделано исключительно для демонстрационных целей. Свободно плавающий код в нашем примере означает, что код является частью ряда функций.
  
==Declaration==
+
==Описание==
Unlike variables, functions do not need to be declared before you use them. Functions have two pieces, the '''prototype''' and the '''body'''. The prototype contains the name of your function and the parameters it will accept. The body is the contents of its code.
+
В отличие от переменных, функции, не нужно объявлять, прежде чем использовать их. Функции имеют две части, '''модель''' и '''тело'''. Модель содержит имя Вашей функции и параметры, которые она будет принимать. Тело является контейнером для кода.
  
Example of a function:
+
Пример функции:
 
<pawn>
 
<pawn>
 
AddTwoNumbers(first, second)
 
AddTwoNumbers(first, second)
Line 253: Line 251:
 
}</pawn>
 
}</pawn>
  
This is a simple function. The prototype is this line:
+
Это простая функция. Модель этой строки:
 
<pawn>AddTwoNumbers(first, second)</pawn>
 
<pawn>AddTwoNumbers(first, second)</pawn>
  
Broken down, it means:
+
Распишем по отдельности:
*<tt>AddTwoNumbers</tt> - Name of the function.
+
*<tt>AddTwoNumbers</tt> - Название функции.
*<tt>first</tt> - Name of the first parameter, which is a simple cell.
+
*<tt>first</tt> - Название первого параметра, который представляет собой простой элемент.
*<tt>second</tt> - Name of the second parameter, which is a simple cell.
+
*<tt>second</tt> - Название второго параметра, который представляет собой простой элемент.
  
The body is a simple block of code. It creates a new variable, called <tt>sum</tt>, and assigns it the value of the two parameters added together (more on expressions later). The important thing to notice is the <tt>return</tt> statement, which tells the function to end and return a value to the caller of the function. All functions ''return a cell'' upon completion. That means, for example:
+
Тело представляет собой простой блок кода. Он создает новую переменную, названную <tt>sum</tt>, и присваивает ей значение этих двух параметров, добавленных совместно (другие выражения будут позже). Важно заметить оператор <tt>return</tt>, в котором обозначается конец функции и возврат с полученными значениями из этой функции. Все функции ''возвращают значения'' после завершения. Это означает, например:
  
 
<pawn>new sum = AddTwoNumbers(4, 5);</pawn>
 
<pawn>new sum = AddTwoNumbers(4, 5);</pawn>
  
The above code will assign the number 9 to sum. The function adds the two inputs, and the sum is given as the '''return value'''. If a function has no return statement or does not place a value in the return statement, it returns 0 by default.
+
Приведенный выше код будет присваивать число 9 к sum. Функция получает два значения и передает новое значение sum в качестве '''возвращаемого значения'''. Если функция не имеет возвращаемого значения или не имеет значений для возврата, то возвращается 0 по умолчанию.
  
A function can accept any type of input. It can return any cell, but not arrays or strings. Example:
+
Функция может принимать любые типы значений. Она может вернуть любую однострочную переменную, но не массивы или строки. Пример:
 
<pawn>Float:AddTwoFloats(Float:a, Float:b)
 
<pawn>Float:AddTwoFloats(Float:a, Float:b)
 
{
 
{
Line 275: Line 273:
 
}</pawn>
 
}</pawn>
  
''Note that if in the above function, you returned a non-Float, you would get a tag mismatch.''
+
''Заметим, что если в приведенной выше функции, Вам вернулась не Float значение, Вы получите не соответствие значений.''
  
You can, of course, pass variables to functions:
+
Можно, конечно, передавать переменные в функции:
 
<pawn>new numbers[3] = {1, 2, 0};
 
<pawn>new numbers[3] = {1, 2, 0};
  
 
numbers[2] = AddTwoNumbers(numbers[0], numbers[1]);</pawn>
 
numbers[2] = AddTwoNumbers(numbers[0], numbers[1]);</pawn>
  
Note that cells are passed '''by value'''. That is, their value cannot be changed by the function. For example:
+
Заметим, что однострочные переменные передаются '''по значению'''. То есть, их значение не может быть изменено функцией. Например:
 
<pawn>new a = 5;
 
<pawn>new a = 5;
  
Line 292: Line 290:
 
}</pawn>
 
}</pawn>
  
This code would not change the value of <tt>a</tt>.  That is because a copy of the value in <tt>a</tt> is passed instead of <tt>a</tt> itself.
+
Этот код не будет менять значение <tt>a</tt>. Это происходит потому, что копия этого значения в <tt>a</tt> передается вместо <tt>a</tt> самостоятельно.
 +
 
 +
Больше примеров функций будут демонстрироваться и в других частях статьи.
 +
 
 +
==Publics==
 +
Публичные функции используются для осуществления обратных вызовов. Вы не должны создавать какую-либо публичную функцию, если это вынудит выполнение обратного вызова. Например, вот два обратных вызова из <tt>sourcemod.inc</tt>:
 +
 
 +
<pawn>forward OnPluginStart();
 +
forward OnClientDisconnected(client);</pawn>
 +
 
 +
Чтобы выполнить и получить эти два события, Вы должны написать такие функции как:
 +
 
 +
<pawn>public OnPluginStart()
 +
{
 +
  /* Код здесь */
 +
}
 +
 
 +
public OnClientDisconnected(client)
 +
{
 +
  /* Код здесь */
 +
}</pawn>
 +
 
 +
Ключевое слово '''public''' делает функцию публичной, а также позволяет родительскому приложению непосредственно вызывать функцию.
 +
 
 +
==Natives==
 +
Natives имеют встроенные функции, предоставляемые SourceMod. Вы можете вызвать их, как если бы они были normal функциями. Например, SourceMod имеет следующие функции:
 +
 
 +
<pawn>native FloatRound(Float:num);</pawn>
 +
 
 +
Её можно вызвать таким образом:
 +
<pawn>new num = FloatRound(5.2);    //Результат в num = 5</pawn>
 +
 
 +
==Параметры массива==
 +
Вы можете передавать массивы или строки в качестве параметров. Важно отметить, что они идут '''как ссылка'''. То есть не делать копию данных, а отдавать непосредственно ссылки на данные. Существует простой способ объяснить это более конкретно.
 +
 
 +
<pawn>
 +
new example[] = {1, 2, 3, 4, 5};
 +
 
 +
ChangeArray(example, 2, 29);
 +
 
 +
ChangeArray(array[], index, value)
 +
{
 +
  array[index] = value;
 +
}</pawn>
 +
 
 +
Эта функция устанавливает заданный индекс в массиве с учетом значений. Когда она запускается на примере нашего массива, она меняет индекс 2 для значения 3 на 29. То есть:
 +
<pawn>example[2] = 29;</pawn>
 +
 
 +
Это возможно лишь потому, что массив может быть непосредственно изменён. Чтобы предотвратить массив от изменения, можно пометить его как постоянную <tt>const</tt>. Это позволит понизить риск на ошибку в коде от её изменения. Например:
 +
 
 +
<pawn>CantChangeArray(const array[], index, value)
 +
{
 +
  array[index] = value;    //Не компилируется
 +
}</pawn>
 +
 
 +
Это хорошая идея использовать <tt>const</tt> в параметрах массивов и Вы будете точно знать, что массив не будет изменен; это может предотвратить ошибки кодирования.
 +
 
 +
=Выражения=
 +
Выражения являются точно такими же, какими они существуют в математике. Это группы операторов/символов, которые приходятся на один фрагмент данных. Они часто заключены в скобках (внутри скобок). Они содержат строгий "порядок операций". Они могут содержать переменные, функции, цифры и выражения сами могут быть вложенные внутрь других выражений, и даже приняты в качестве параметров.
 +
 
 +
Приведем пример простейшего выражения:
 +
<pawn>
 +
0;  //Возвращает число 0
 +
(0); //Так же возвращает число 0
 +
</pawn>
 +
 
 +
Хотя выражения могут возвращать значения, они также могут ответить какое значение содержит ответ ''ноль или не ноль''. В этом смысле, ''ноль'' является ''ложью'' (false), а ''не нулевое'' значение ''истиной'' (true). Например, -1 ''истина'' в Pawn, поскольку она не является нулем. Не думайте, что отрицательные числа являются ложными.
 +
 
 +
Порядок операций выражения аналогичен языку C. PMDAS: Parenthesis, Multiplication, Division, Addition, Subtraction. Вот несколько примеров выражений:
 +
<pawn>
 +
5 + 6;                  //Вычисляет как 11
 +
5 * 6 + 3;              //Вычисляет как 33
 +
5 * (6 + 3);            //Вычисляет как 45
 +
5.0 + 2.3;              //Вычисляет как 7.3
 +
(5 * 6) % 7;            //Modulo operator, вычисляет как 2
 +
(5 + 3) / 2 * 4 - 9;    //Вычисляет как 7
 +
</pawn>
 +
 
 +
Как уже отмечалось, выражения могут содержать переменные, или даже функции:
 +
<pawn>
 +
new a = 5 * 6;
 +
new b = a * 3;      //Вычисляет как 90
 +
new c = AddTwoNumbers(a, b) + (a * b);
 +
</pawn>
 +
 
 +
Примечание:  String manipulation routines may be found in the string.inc file located in the include subdirectory.  They may be browsed through the [http://docs.sourcemod.net/api/ API Reference] as well.
 +
 
 +
==Операторы==
 +
Есть несколько полезных дополнительных операторов в Pawn. Первый набор упрощает аутоагрегацию выражения. Например:
 +
<pawn>new a = 5;
 +
 
 +
a = a + 5;</pawn>
 +
 
 +
Может быть переписан, как:
 +
<pawn>new a = 5;
 +
a += 5;</pawn>
 +
 
 +
Это верно в отношении следующих операторов в Pawn:
 +
*Four-function: *, /, -, +
 +
*Bit-wise: |, &, ^, ~, <<, >>
 +
 
 +
Кроме того, существуют инкремент/декремент операторы:
 +
<pawn>a = a + 1;
 +
a = a - 1;</pawn>
 +
 
 +
Может быть упрощено, как:
 +
<pawn>a++;
 +
a--;</pawn>
 +
 
 +
Дополнительно отметим, что ++ или -- может быть представлен до переменной (до-инкремент, до-декремент) или после переменной (пост-инкремент, пост-декремент). Разница заключается в том, как остальная часть выражения содержащие их, видит результат.
 +
 
 +
* ''До:'' Переменная увеличивается до определения и остальная часть выражения видит новое значение.
 +
* ''Пост:'' Переменная увеличивается после определения и остальная часть выражения видит старое значение.
 +
 
 +
Иными словами, <tt>a++</tt> определяет значение как <tt>a</tt> в то время как <tt>++a</tt> определяет значение как <tt>a + 1</tt>. В обоих случаях <tt>a</tt> увеличивается на <tt>1</tt>.
 +
 
 +
Например:
 +
 
 +
<pawn>new a = 5;
 +
new b = a++;  // b = 5, a = 6  (1)
 +
new c = ++a;  // a = 7, c = 7  (2)
 +
</pawn>
 +
 
 +
(1) <tt>b</tt> присваивается <tt>a</tt> со ''старым'' значением, ''до'' того, как он будет увеличено до <tt>6</tt>. (2) <tt>c</tt> присваивается <tt>a</tt> с ''новым'' значением, ''после'' того, как он увеличивается до <tt>7</tt>.
 +
 
 +
==Операторы сравнения==
 +
Существуют шесть операторов для сравнения двух числовых значений, а результат является либо истиной (не ноль) или ложью (ноль):
 +
*<tt>a == b</tt> - Действительно, если и b имеет то же значение.
 +
*<tt>a != b</tt> - Действительно, если b имеет другое значение.
 +
*<tt>a &gt; b</tt> - Действительно, если оно больше b
 +
*<tt>a &gt;= b</tt> - Действительно, если оно больше или равно b
 +
*<tt>a &lt; b</tt> - Действительно, если оно меньше b
 +
*<tt>a &lt;= b</tt> - Действительно, если оно меньше или равно b
 +
 
 +
Например:
 +
<pawn>
 +
(1 != 3);        //Определяется как истина, поскольку 1 не равно 3.
 +
(3 + 3 == 6);    //Определяется как истина, поскольку 3+3 равно 6.
 +
(5 - 2 >= 4);    //Определяется как ложь, поскольку 3 меньше 4.
 +
</pawn>
 +
 
 +
Заметим, что эти операторы не работают с массивами и строками. То есть, вы не можете сравнить их с помощью <tt>==</tt>.
 +
 
 +
==Действительные операторы==
 +
Действительные операторы могут быть скомбинированы тремя булевыми (boolean) операторами:
 +
*<tt>a && b</tt> - Истина, если a и b истинные. Ложь, если a и (или) b ложные.
 +
{| border="1" cellpadding="2" cellspacing="0" align="center"
 +
! <tt>&&</tt> !! 0 !! 1
 +
|-
 +
! 0
 +
| 0 || 0
 +
|-
 +
! 1
 +
| 0 || 1
 +
|}
 +
*<tt>a || b</tt> - Истина, если a или b (или обе переменные) истинные. Ложь, если обе переменные a и b ложные.
 +
{| border="1" cellpadding="2" cellspacing="0" align="center"
 +
! <tt><nowiki>||</nowiki></tt> !! 0 !! 1
 +
|-
 +
! 0
 +
| 0 || 1
 +
|-
 +
! 1
 +
| 1 || 1
 +
|}
 +
*<tt>!a</tt> - Истина, если a ложь. Ложь, если a истина.
 +
{| border="1" cellpadding="2" cellspacing="0" align="center"
 +
! <tt>!</tt> !! 0 !! 1
 +
|-
 +
!
 +
| 1 || 0
 +
|}
 +
 
 +
Например:
 +
<pawn>
 +
(1 || 0);        //Определяется как истина, так как выражение 1 истинное
 +
(1 && 0);        //Определяется как ложь, так как выражение 0 ложное
 +
(!1 || 0);        //Определяется как ложь, так как выражение !1 ложное.
 +
</pawn>
 +
 
 +
==Левое/правое значения==
 +
Два важных понятия левого и правого значений, или левостороннее и правостороннее значения. Левостороннее значение имеет то, что появляется на левой стороне выражения, а правостороннее значение - появляется на правой стороне выражения.
 +
 
 +
Например:
 +
<pawn>
 +
new a = 5;</pawn>
 +
 
 +
В этом примере <tt>a</tt> является левосторонним значением и <tt>5</tt> является правосторонним значением.
 +
 
 +
Правила:
 +
*'''Выражения никогда не будут левосторонними значениями'''.
 +
*'''Переменные являются двумя, левосторонними и правосторонними значениями'''.
 +
 
 +
=Условия=
 +
Условия позволяют Вам запускать код, определенное условие выполнено.
 +
 
 +
==Если соответствует==
 +
Если соответствует одно или более условий. Например:
 +
 
 +
<pawn>
 +
if (a == 5)
 +
{
 +
  /* Код будет запущен, если условие будет истинным */
 +
}</pawn>
 +
 
 +
Они могут быть расширены для более сложных случаев:
 +
<pawn>
 +
if (a == 5)
 +
{
 +
  /* Код */
 +
}
 +
else if (a == 6)
 +
{
 +
  /* Код */
 +
}
 +
else if (a == 7)
 +
{
 +
  /* Код */
 +
}</pawn>
 +
 
 +
Вы так же можете обрабатывать случаи, даже если выражение не верно. Например:
 +
<pawn>
 +
if (a == 5)
 +
{
 +
  /* Код */
 +
}
 +
else
 +
{
 +
  /* Код, который будет запущен если нет истинного выражения */
 +
}</pawn>
 +
 
 +
==Оператор выбора==
 +
Оператор выбора будет ограничен условием. Он необходим для выражения, выполняющего код для целого ряда возможных значений. Например:
 +
 
 +
<pawn>
 +
switch (a)
 +
{
 +
  case 5:
 +
  {
 +
      /* Код */
 +
  }
 +
  case 6:
 +
  {
 +
      /* Код */
 +
  }
 +
  case 7:
 +
  {
 +
      /* Код */
 +
  }
 +
  case 8, 9, 10:
 +
  {
 +
      /* Код */
 +
  }
 +
  default:
 +
  {
 +
      /* будет запущен, если не одно условие не соответствует */
 +
  }
 +
}</pawn>
 +
 
 +
В отличие от некоторых других языков, оператор выбора не проваливается. То есть существуют случаи, когда код не будет запущен. При случае совпадения его код выполняется, а ключ является местом для немедленного прекращения.
 +
 
 +
=Циклы=
 +
Циклы позволяют Вам без труда повторять выполнение кода, пока условие станет истинным.
 +
 
 +
==For циклы==
 +
For циклы, это циклы, которые состоят из четырех частей:
 +
*Оператор '''инициализации''' - запускается один раз перед первым циклом.
 +
*Оператор '''условия''' - проверяет условие и запускает следующий цикл, в том числе первый. Цикл прекращается, когда это выражение становится ложным.
 +
*Оператор '''итерации''' - запускается после каждого цикла.
 +
* '''тело''' цикла - запускается каждый раз, пока оператор '''условия''' вычисляется как истинный.
 +
 
 +
<pawn>
 +
for ( /* инициализация */ ; /* условие */ ; /* итерация */ )
 +
{
 +
  /* тело */
 +
}
 +
</pawn>
 +
 
 +
Простым примером является функция сложения массива:
 +
<pawn>
 +
new array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 +
new sum = SumArray(array, 10);
 +
 
 +
SumArray(const array[], count)
 +
{
 +
  new total;
 +
 
 +
  for (new i = 0; i < count; i++)
 +
  {
 +
      total += array[i];
 +
  }
 +
 
 +
  return total;
 +
}</pawn>
 +
 
 +
По отдельности:
 +
*<tt>new i = 0</tt> - Создает новую переменную для цикла, и устанавливает её в 0.
 +
*<tt>i < count</tt> - Только запускает цикл, если <tt>i</tt> меньше, чем <tt>count</tt>. Это гарантирует, что чтение цикла прекращается в определенный момент. В этом случае, мы не хотим читать недействительные индексы в массиве.
 +
*<tt>i++</tt> - Увеличивает <tt>i</tt> на единицу после каждого цикла. Это гарантирует, что цикл не будет запущен вечно; в конце концов <tt>i</tt> станет слишком большим, и цикл завершится.
 +
 
 +
Таким образом, функция <tt>SumArray</tt> будет циклом каждого действительного индекса массива, каждый раз добавляя это значение в sum. Для циклов очень распространены массивы такие, как в нашем примере.
 +
 
 +
==While циклы==
 +
While циклы являются менее распространенными, чем for циклы, но на самом деле это более простые циклы. Они имеют только две части:
 +
*Оператор '''условия''' - проверяется перед началом каждого цикла. Цикл прекращается, когда условие становится ложным.
 +
*'''тело''' цикла - запускается каждый раз пока цикл выполняется.
 +
 
 +
<pawn>
 +
while ( /* условие */ )
 +
{
 +
  /* тело */
 +
}
 +
</pawn>
 +
 
 +
До тех пор, пока условие выражения остается истинным, цикл будет выполняться. Каждый for цикл может быть переписан, как while цикл:
 +
 
 +
<pawn>
 +
/* инициализация */
 +
while ( /* условие */ )
 +
{
 +
  /* тело */
 +
  /* итерация */
 +
}
 +
</pawn>
 +
 
 +
Вот предыдущий for цикл переписан как while цикл:
 +
<pawn>
 +
SumArray(const array[], count)
 +
{
 +
  new total, i;
 +
 
 +
  while (i < count)
 +
  {
 +
      total += array[i];
 +
      i++;
 +
  }
 +
 
 +
  return total;
 +
}</pawn>
 +
 
 +
Существуют также '''do...while''' циклы, которые используются еще реже. Они работают как и while циклы, но проверяют условие ПОСЛЕ каждого цикла, а не перед ним. Это означает, что цикл всегда будет запущен, по крайней мере один раз. Например:
 +
 
 +
<pawn>
 +
do
 +
{
 +
  /* тело */
 +
}
 +
while ( /* условие */ );
 +
</pawn>
 +
 
 +
==Управление циклами==
 +
Существуют два случая, в которых Мы хотели бы контролировать цикл:
 +
*'''пропустить''' одну итерацию или цикл и продолжить выполнение цикла как обычно, или;
 +
*'''разорвать''' цикл целиком, прежде чем он закончится.
 +
 
 +
Допустим у вас есть функция, которая принимает массив и ищет соответствия цифр. Вы хотите его остановить, когда число будет найдено:
 +
<pawn>
 +
/**
 +
* Возвращает массив, если индекс значения, или -1, не найдены.
 +
*/
 +
SearchInArray(const array[], count, value)
 +
{
 +
  new index = -1;
 +
 +
  for (new i = 0; i < count; i++)
 +
  {
 +
      if (array[i] == value)
 +
      {
 +
        index = i;
 +
        break;
 +
      }
 +
  }
 +
 
 +
  return index;
 +
}</pawn>
 +
 
 +
Конечно, эту функцию можно вернуть и способом <tt>return i</tt>, но пример показывает, как <tt>break</tt> может остановить цикл.
 +
 
 +
Кроме того, ключевое слово <tt>continue</tt> пропускает итерации цикла. Например, Мы хотим суммировать все четные числа:
 +
 
 +
<pawn>
 +
SumEvenNumbers(const array[], count)
 +
{
 +
  new sum;
 +
   
 +
  for (new i = 0; i < count; i++)
 +
  {
 +
      /* If divisibility by 2 is 1, we know it's odd */
 +
      if (array[i] % 2 == 1)
 +
      {
 +
        /* Пропускаем оставшуюся часть итерации цикла */
 +
        continue;
 +
      }
 +
      sum += array[i];
 +
  }
 +
 
 +
  return sum;
 +
}</pawn>
 +
 
 +
=Область действия=
 +
Область действия относится к '''удобочитаемости''' кода. Это означает, что код одного уровня не может быть "виден" в коде другого уровня. Пример:
 +
 
 +
<pawn>
 +
new A, B, C;
 +
 
 +
Function1()
 +
{
 +
  new B;
 +
 
 +
  Function2();
 +
}
 +
 
 +
Function2()
 +
{
 +
  new C;
 +
}</pawn>
 +
 
 +
В этом примере, <tt>A</tt>, <tt>B</tt>, и <tt>C</tt> имеют '''глобальную область действия'''. Их можно увидеть в любой функции. Вместе с тем, <tt>B</tt> в функции <tt>Function1</tt> не является той же переменной, как <tt>B</tt> на глобальном уровне. Вместо этого она находится в '''локальной области действия''', и поэтому является '''локально изменяемой'''.
 +
 
 +
Кроме того, функции <tt>Function1</tt> и <tt>Function2</tt> ничего не знают о существовании других переменных.
 +
 
 +
Она так же является не только локальной переменной функции <tt>Function1</tt>, но и создается заново каждый раз, когда функция вызывается. Попробуйте представить это:
 +
<pawn>
 +
Function1()
 +
{
 +
  new B;
 +
 
 +
  Function1();
 +
}</pawn>
 +
 
 +
В приведенном выше примере, функция <tt>Function1</tt> вызывает сама себя. Конечно, это бесконечной рекурсии (а это очень плохо), но идея заключается в том, что каждый раз, когда функция срабатывает, то создается новая копия <tt>B</tt>. Когда функция завершается, <tt>B</tt> уничтожается, и её значение теряется.
 +
 
 +
Это свойство можно объяснить проще тем, что область действия переменной равна уровню её вложенности. То есть, переменная на глобальной области действия видна для всех функций. Переменная в локальной области действия видна всему блоку кода расположенному "ниже" этого уровня. Например:
 +
 
 +
<pawn>Function1()
 +
{
 +
  new A;
 +
 
 +
  if (A)
 +
  {
 +
      A = 5;
 +
  }
 +
}</pawn>
 +
 
 +
Приведенный выше код является действительным, поскольку в область действия распространяется по всей функции. Однако этот код, работать не будет:
 +
<pawn>
 +
Function1()
 +
{
 +
  new A;
 +
 
 +
  if (A)
 +
  {
 +
      new B = 5;
 +
  }
 +
 
 +
  B = 5;
 +
}</pawn>
 +
 
 +
Отметим, что <tt>B</tt> объявляется в новом блоке кода. Это означает, что <tt>B</tt> доступна только в том блоке кода, в котором была создана (и всем под-блокам вложенных внутри него). Как только блок кода прекращается, <tt>B</tt> становится не действительной.
 +
 
 +
=Динамические массивы=
 +
Динамические массивы это массивы, которые не имеют фиксированного размера. Например:
 +
 
 +
<pawn>Function1(size)
 +
{
 +
  new array[size];
 +
 
 +
  /* Код */
 +
}</pawn>
 +
 
 +
Динамические массивы могут иметь любое выражение, соответствующее их размеру до тех пор, пока выражение вычисляется, как число большее чем 0. Как и для обычных массивов, SourcePawn не сможет узнать размер массива после того, как он будет создан; Вы должны задать его, если хотите использовать массив позднее.
 +
 
 +
Динамические массивы, действительны только в локальной области действия, так как код не может существовать на глобальном уровне.
 +
 
 +
=Расширенное объявление переменных=
 +
Переменные могут быть объявлены более длинным путем чем просто <tt>new</tt>.
 +
 
 +
==decl==
 +
===Назначение===
 +
По умолчанию, все переменные в Pawn будут инициализированы как нуль. Если есть явная инициализация, переменная инициализируется для выражения <tt>=</tt> определенным символам. В локальной области действия, это может потребовать время на выполнение. Ключевое слово <tt>decl</tt> (которое действительно только в локальной области действия) было введено, чтобы позволить решать пользователю, хочит ли он инициализировать переменную или нет.
 +
 
 +
Примечание: <tt>decl</tt> не должно быть использовано на одну однострочную переменную. Это почти никогда не будет выгодно.
 +
 
 +
===Объяснение===
 +
Например:
 +
 
 +
<pawn>
 +
new c = 5;
 +
new d;
 +
new String:blah[512];
 +
 
 +
Format(blah, sizeof(blah), "%d %d", c, d);
 +
</pawn>
 +
 
 +
В этом коде, <tt>c</tt> равна 5 и <tt>d</tt> равна 0. Во время выполнения этого кода затраты на инициализацию незначительные. Вместе с тем, <tt>blah</tt> является большим массивом, и затраты на инициализацию всего массива могут быть больше 0 сек. и иметь плохие последствия в определенных ситуациях.
 +
 
 +
Заметим, что <tt>blah</tt> не должен быть нулевой. В период времени с объявления <tt>new</tt> и и перемещения в <tt>Format()</tt>, массив <tt>blah</tt> никогда не будет загружен или прочитан. Данный код будет более эффективен, если будет написанный следующим образом:
 +
 
 +
<pawn>
 +
new c = 5;
 +
new d;
 +
decl String:blah[512];
 +
 
 +
Format(blah, sizeof(blah), "%d %d", c, d);
 +
</pawn>
 +
 
 +
===Предостережения===
 +
Обратная сторона <tt>decl</tt> состоит в том, что его переменные будут начинаться с "ненужного" содержания. Например, если мы будем использовать:
 +
 
 +
<pawn>
 +
new c = 5;
 +
new d;
 +
decl String:blah[512];
 +
 
 +
PrintToServer("%s", blah);
 +
 
 +
Format(blah, sizeof(blah), "%d %d", c, d);
 +
</pawn>
 +
 
 +
Этот код может привести к падению сервера, так как массив <tt>blah</tt> может быть полностью испорчен (строки требуют нулевой символ, который может отсутствовать). Точно так же, если мы сделаем:
 +
 
 +
<pawn>
 +
new c = 5;
 +
decl d;
 +
decl String:blah[512];
 +
 
 +
Format(blah, sizeof(blah), "%d %d", c, d);
 +
</pawn>
 +
 
 +
Значение <tt>d</tt> в настоящее время не определенно. Оно может быть любым значением, отрицательным или положительным.
 +
 
 +
Заметим, что это легко и эффективно обезопасит строки. Пример ниже показывает, как предотвратить строки от мусора:
 +
<pawn>
 +
decl String:blah[512];
 +
 
 +
blah[0] = '\0';
 +
</pawn>
 +
 
 +
===Золотые правила===
 +
*'''Используйте decl только, если в период объявления и загрузки/чтения значения, Вы абсолютно уверены, что есть по крайней мере одно хранилище/операция, которая отдает переменной действительные данные.'''
 +
*'''Не оптимизируйте преждевременно.''' Кроме того, нет необходимости использовать <tt>decl</tt> на не-массивы, поскольку нет никаких дополнительных затрат на инициализацию однго однострочного значения.
 +
 
 +
===Примечания===
 +
Обратите внимание, что <tt>decl</tt> переменную инициализировать явно, но '''ТОЛЬКО''' как строку:
 +
<pawn>
 +
decl String:blah[512] = "a";
 +
</pawn>
 +
 
 +
However, any other tag will fail to compile, because the purpose of <tt>decl</tt> is to avoid any initialization:
 +
<pawn>
 +
decl Float:blah[512] = {1.0};
 +
</pawn>
 +
 
 +
Даже несмотря на то, что строка имеет только один символ, оператор <tt>new</tt> гарантирует, что остальная часть массива будет нулевой.
 +
 
 +
Также обратите внимание, он является неправильным для явной инициализации <tt>decl</tt>:
 +
<pawn>decl String:blah[512] = "a";</pawn>
 +
 
 +
Приведенный выше код не будет компилироваться, потому что цель <tt>decl</tt> состоит в том, чтобы избежать каких-либо инициализаций.
 +
 
 +
==static==
 +
Ключевое слово <tt>static</tt> входит в глобальную и локальную область действия. Оно имеет различные значения в каждой из них.
 +
 
 +
===Глобальный static===
 +
Глобальные static переменные могут быть доступны только в рамках того же файла. Например:
 +
 
 +
<pawn>//file1.inc
 +
static Float:g_value1 = 0.15f;
 +
 
 +
//file2.inc
 +
static Float:g_value2 = 0.15f;</pawn>
 +
 
 +
Если плагин включает в себя оба этих файла, он не сможет использовать <tt>g_value1</tt> или <tt>g_value2</tt>. Это простой механизм сокрытия информации, и аналогичен элементам объявления переменных, например <tt>private</tt> в таких языках, как C++, Java, или C#.
 +
 
 +
===Локальный static===
 +
Локальная static переменная является глобальной переменной, которая является видимой лишь из её местной области действия. Например:
 +
 
 +
<pawn>
 +
MyFunction(inc)
 +
{
 +
  static counter = -1;
 +
 
 +
  counter += inc;
 +
 
 +
  return counter;
 +
}</pawn>
 +
 
 +
В этом примере, <tt>counter</tt> технически глобальная переменная -- она инициализируется один раз как -1 и никогда не инициализируется заново. Оно не существует в наборе. Это означает, что при каждом запуске функции <tt>MyFunction</tt>, переменная <tt>counter</tt> и ее хранение в памяти одно и тоже.
 +
 
 +
Возьмем следающий пример:
 +
<pawn>MyFunction(5);
 +
MyFunction(6);
 +
MyFunction(10);</pawn>
  
More examples of functions will be provided throughout the article.
+
В этом примере, <tt>counter</tt> может быть <tt>-1 +5 +6 +10</tt>, или <tt>20</tt>, поскольку она сохраняется за рамками этой функции. Это может создавать проблемы для рекурсивной функции: если ваша функция может быть рекурсивной, то <tt>static</tt>, как правило, не является хорошей идеей, если только Ваш код не реентерабельный.
  
 +
Преимуществом локальных static переменных является то, что Вам не придется загромождать свой сценарий глобальными переменными. До тех пор, пока переменной не нужно читать другую функцию, Вы можете его пихать внутрь функции и её сохранение будет гарантировано.
  
..в стадии перевода
+
Заметим, что statics может существовать в любой локальной области действия:
 +
 
 +
<pawn>
 +
MyFunction(inc)
 +
{
 +
  if (inc > 0)
 +
  {
 +
      static counter;
 +
      return (counter += inc);
 +
  }
 +
  return -1;
 +
}</pawn>
  
[[Category:Russian]]
+
[[Category:Ru:SourceMod Scripting]]

Latest revision as of 12:10, 25 June 2011

Это руководство призвано дать Вам самые основные представления по основам написания сприптов в SourcePawn. Pawn это "скриптовый" язык используемый для внедрения функциональности в других программах. Это означает, что это не самостоятельный язык, как C++ или Java, и его элементы будут отличаться в различных приложениях. SourcePawn это вариация языка Pawn используемая в SourceMod.

Это руководство не расскажет Вам как писать SourceMod плагины; оно предназначено для получения общих представлений о синтаксисе и семантике этого языка. Читайте отдельную статью Ru:Introduction to SourceMod Plugins (Введение в SourceMod плагины), для введения в SourceMod API.

Введение для новичков

Этот раздел создан не для программистов. Если Вы по прежнему в замешательстве, Вы можете прочитать книги о других языках программирования, таких как PHP, Python, или Java, чтобы получить более полное представление о программировании.

Идентификаторы/Ключевые слова

Идентификаторы представляет собой набор букв, цифр и/или нижнего подчеркивания, что представляет собой нечто уникальное. Идентификаторы вводятся с учетом регистра (в отличие от PHP, где иногда это не требуется). Идентификаторы не начинаются с какого-либо специального символа, но они должны начинаться с буквы.

Есть несколько зарезервированных символов, которые имеют особое значение. Например, if, for, и return специальные конструкции в языке, которые будут описаны позднее. Они не могут быть использованы в качестве названий идентификаторов.

Переменные

Существует несколько важных конструкций, которые Вы должны знать, прежде чем приступить к написанию сценария. Во-первых, это переменные. Переменная это идентификатор, который содержит данные. Например, переменная "a" может содержать числа "2", "16", "0", и так далее. Переменные создаются для хранения данных внутри программы. Переменные должны быть объявлены до их использования, с помощью ключевого слова "new". Данные можно присвоить переменной, используя знак равенства (=). Пример:

new a, b, c, d;
 
a = 5;
b = 16;
c = 0;
d = 500;

В SourcePawn, переменные бывают двух типов, которые будут более подробно описаны далее.

  • Однострочные (могут содержать только произвольные числовые данные), как показано выше.
  • Многострочные (могут содержать целый ряд текстовых символов)

Функции

Следующим важным понятием являются функции. Функции идентификаторов или имен, которые выполняют действия. Это означает, что когда вы их активируете, они выполняют конкретную последовательность кода. Есть несколько типов функций, но все функции активируется одинаковым образом. "Вызов функции" является термином ссылающимся на функцию действия. Функция числовых переменных строятся так:

функция(<параметры>)

Примеры:

show(56);   //Активирует функцию "show" и присваивает ей число 56
show();     //Активирует функцию "show" без каких-либо данных, пустую
show(a);    //Активирует функцию "show" и присваивает ей переменную с данными

Каждый фрагмент данных передаваемый вызываемой функции, называется параметр. Функция может иметь любое количество параметров (но есть "допустимый" предел в SourceMod: 32). Параметры будут описаны далее в этой статье.

Комментарии

Примечания и любой текст, который пишется после "//" считается "Комментарием", а не фактическим кодом. Есть два стиля комментариев:

  • // - Двойная косая черта, всё следующие после этой строки игнорируется.
  • /* */ - Много-строчный комментарий, весь текст, внутри звездочек игнорируются. You cannot nest these.

Массивы

Описание массивов. Вы можете группировать код в виде "массивов", разделенных { и }. Это фактически создает возможность работать с целым массивом как с одним оператором. Например:

{
   here;
   is;
   some;
   code;
}

Массивы с фигурными скобками используются достаточно широко в программировании. Массивы кода могут быть вложенными друг в друга. Это хорошая возможность адаптировать последовательность когда и сделать его удобочитаемым, благодаря отступам код не будет смотреться как одна большая и длинная макаронина.


Особенности языка

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

  • Pawn не печатает Pawn имеет только один тип данных - однострочный. Подробнее будет описано позже. [В дальнейшем автор рассказывает, что существует два типа данных: однострочный и многострочный]
  • Pawn не собирает мусор Pawn, как язык, не имеет встроенных ресурсов памяти, и потому он не мусорит. Если функция выделит память, то Вы отвечаете за её освобождение.
  • Pawn не объектно-ориентированный язык Pawn является процедурным, и полагается на подпрограммы. Также у него нету C подобных структур.
  • Pawn не функциональный. Pawn является процедурным, и не поддерживает функции "лямбды" (Lambda), поздние присвоения, и все то, что можно найти в языках высшего уровня, таких как Python и Ruby.
  • Pawn однопоточный As of this writing, Pawn is not thread safe.
  • Pawn не интерпретируемый Ну, почти. Он интерпретируется на очень низком уровне. Вы должны скомпилировать код, из которого получится бинарный файл. Эта программа будет работать на той платформе, которую использует хост. Это ускоряет загрузку и позволяет легче находить ошибки.

Этот язык был выпущен ITB CompuPhase. Язык разработан для устройств низкого уровня и таким образом конечные программы очень маленькие по размеру и очень быстрые.

Переменные

В Pawn есть всего два типа переменных: однострочные и многострочные. Однострочные могут содержать 32 бита цифровых данных. Многострочные - последовательный список из UTF-8 символов.

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

  • (пусто), или _ - Нет тега. Обычно используют для целых чисел (Integers).
  • Float - используют для чисел с плавающей точкой (небольших).
  • bool - используют для хранения значений true (истина) или false (ложь).

Со строками все по другому, они будут рассмотрены далее.

Объявления

Примеры разных правильных объявлений переменных.

new a = 5;
new Float:b = 5.0;
new bool:c = true;
new bool:d = 0;      //Работает, поскольку 0 равно false (ложь)

Неправильные объявления переменных

new a = 5.0;         //Несоответствие тегов. 5.0 с тегом Float
new Float:b = 5;     //Несоответствие тегов. 5 без тега.

Если переменная не определена в объявлении то ее значения станет 0

new a;        //значение 0
new Float:b;  //значение 0.0
new bool:c;   //значение false

Присвоение

Переменным могут быть присвоены данные после создания. Пример:

new a, Float:b, bool:c;
 
a = 5;
b = 5.0;
c = true;

Массивы

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

Описание

Массив объявляется с помощью квадратных скобок. Вот некоторые примеры массивов:

new players[32];     //Набор из 32 однострочных (числовых) данных
new Float:origin[3]; //Набор из 3 чисел с плавающей точкой

По умолчанию, массивам присваиваются нули. Вы можете присвоить им разные значения по умолчанию, однако:

new numbers[5] = {1, 2, 3, 4, 5};       //Набор 1, 2, 3, 4, 5 из однострочных данных.
new Float:origin[3] = {1.0, 2.0, 3.0};  //Набор 1.0, 2.0, 3.0 из однострочных данных.

Вы можете оставить массив без размера, если вы собираетесь заранее присвоить ему данные. Например:

new numbers[] = {1, 3, 5, 7, 9};

Компилятор будет автоматически делать вывод о том, что Вы хотите получить массив размером 5.

Использование

Использование массива равносильно использованию обычных переменной. Единственное отличие массива состоит в том, что он должен быть индексируемым. Индексирование массива означает присутствие возможности выбрать элемент, который Вы хотите использовать.

Вот пример кода с использованием индексов:

new numbers[5], Float:origin[3];
 
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;
origin[0] = 1.0;
origin[1] = 2.0;
origin[2] = 3.0;

Заметим, что индекс это текст, который находится в квадратных скобках. Индекс всегда начинается с нуля. То есть, если массив имеет N элементов, его действительный индекс от 0 до N-1. Доступ к данным с индексами работает так же, как с обычной переменной.

Использование неверного индекса вызовет ошибку. Например:

new numbers[5];
 
numbers[5] = 20;

Это может выглядеть верно, но число 5 не является допустимым индексом. Наибольшим значением индекса является число 4.

Вы можете использовать любые выражения, как индекс. Например:

new a, numbers[5];
 
a = 1;                   //Сделает a = 1
numbers[a] = 4;          //Сделает numbers[1] = 4
numbers[numbers[a]] = 2; //Сделает numbers[4] = 2

Выражения будут обсуждаться подробнее в конце статьи.

Строки

Строки являются удобным способом хранения текста. Символы хранятся в массиве. Строка ограничивается нулевым символом, или 0. Без нулевого символа, Pawn не знает, где остановить чтение строки. Все строки в SourcePawn используют кодировку UTF-8.

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

Примечание для специалистов: они фактически не однострочные. SourcePawn использует 8-битный строки для хранения массивов в качестве оптимизации. Это и есть то, что делает строки типом, а не меткой.

Использование

Строки были созданы почти в равной степени и для массивов. Например:

new String:message[] = "Hello!";
new String:clams[6] = "Clams";

Это равносильно следующему:

new String:message[7], String:clams[6];
 
message[0] = 'H';
message[1] = 'e';
message[2] = 'l';
message[3] = 'l';
message[4] = 'o';
message[5] = '!';
message[6] = 0;
clams[0] = 'C';
clams[1] = 'l';
clams[2] = 'a';
clams[3] = 'm';
clams[4] = 's';
clams[5] = 0;

Хотя строки редко инициализируют таким образом, очень важно помнить о концепции нулевого символа, который свидетельствует о конце строки. Компилятор и большинство SourceMod функций будут автоматически остановлены нулевым символом, поэтому он является очень важным, при манипулировании строками напрямую.

Заметим, что строка должна быть заключена в двойных кавычках, а символ в одиночных.

Символы

Особенность текста может быть использована в любой строке или однострочной переменной. Например:

new String:text[] = "Crab";
new clam;
 
clam = 'D';         //Устанавливает однострочной переменной значение 'D'
text[0] = 'A';      //Меняет 'C' на 'A', сейчас получилось 'Arab'
clam = text[0];     //Устанавливает однострочной переменной значение 'A'
text[1] = clam;     //Меняет 'r' на 'A', сейчас получилось 'AAab'

То, что вы не можете сделать, это соотнести символы массивов со строками. Внутреннее хранение отличается. Например:

new clams[] = "Clams";                       //Не верно, нужен тип String:
new clams[] = {'C', 'l', 'a', 'm', 's', 0};  //Верно, но это НЕ СТРОКА.

Функции

Функции, как отмечалось ранее, имеют отдельные составляющие кода, которые выполняют определенные действия. Функции могут быть задействованы или вызвоны с параметрами, которые дают особые настройки.

Существуют два типа вызова функции:

  • прямой вызов - Вы специально вызываете функцию в своем коде.
  • обратный вызов - Применение вызова функций в Вашем коде, как если бы это было событием триггера (совокупность условий, инициирующих выполнение действия).

Существуют шесть видов функций:

  • native: Прямая, внутренняя функция, предусмотренная в приложении.
  • public: Функция обратного вызова, что делает её видимой для приложения и других сценариев.
  • normal: Нормальная функция, которую Вы можете только вызвать.
  • static: The scope of this function is restricted to the current file, can be used in combination with stock.
  • stock: Нормальная функция, предусмотренная если включает в себя файл. Если не используется, то не компилируется.
  • forward: Эта функция представляет собой глобальное событие, предусмотренная приложением. Если Вы её привели в исполнение, она будет вызвона.

Весь код в Pawn должен существовать в функциях. Это основное отличие от языков, таких как PHP, Perl и Python, которые позволяют Вам писать глобальный код. Это происходит потому, что Pawn вызывается на основе другого языка: он реагирует на действия от родительского приложения, и функции должны быть написаны для обработки этих действий. Хотя наш пример, часто содержат свободно плавающий код, это сделано исключительно для демонстрационных целей. Свободно плавающий код в нашем примере означает, что код является частью ряда функций.

Описание

В отличие от переменных, функции, не нужно объявлять, прежде чем использовать их. Функции имеют две части, модель и тело. Модель содержит имя Вашей функции и параметры, которые она будет принимать. Тело является контейнером для кода.

Пример функции:

AddTwoNumbers(first, second)
{
  new sum = first + second;
 
  return sum;
}

Это простая функция. Модель этой строки:

AddTwoNumbers(first, second)

Распишем по отдельности:

  • AddTwoNumbers - Название функции.
  • first - Название первого параметра, который представляет собой простой элемент.
  • second - Название второго параметра, который представляет собой простой элемент.

Тело представляет собой простой блок кода. Он создает новую переменную, названную sum, и присваивает ей значение этих двух параметров, добавленных совместно (другие выражения будут позже). Важно заметить оператор return, в котором обозначается конец функции и возврат с полученными значениями из этой функции. Все функции возвращают значения после завершения. Это означает, например:

new sum = AddTwoNumbers(4, 5);

Приведенный выше код будет присваивать число 9 к sum. Функция получает два значения и передает новое значение sum в качестве возвращаемого значения. Если функция не имеет возвращаемого значения или не имеет значений для возврата, то возвращается 0 по умолчанию.

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

Float:AddTwoFloats(Float:a, Float:b)
{
   new Float:sum = a + b;
 
   return sum;
}

Заметим, что если в приведенной выше функции, Вам вернулась не Float значение, Вы получите не соответствие значений.

Можно, конечно, передавать переменные в функции:

new numbers[3] = {1, 2, 0};
 
numbers[2] = AddTwoNumbers(numbers[0], numbers[1]);

Заметим, что однострочные переменные передаются по значению. То есть, их значение не может быть изменено функцией. Например:

new a = 5;
 
ChangeValue(a);
 
ChangeValue(b)
{
   b = 5;
}

Этот код не будет менять значение a. Это происходит потому, что копия этого значения в a передается вместо a самостоятельно.

Больше примеров функций будут демонстрироваться и в других частях статьи.

Publics

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

forward OnPluginStart();
forward OnClientDisconnected(client);

Чтобы выполнить и получить эти два события, Вы должны написать такие функции как:

public OnPluginStart()
{
   /* Код здесь */
}
 
public OnClientDisconnected(client)
{
   /* Код здесь */
}

Ключевое слово public делает функцию публичной, а также позволяет родительскому приложению непосредственно вызывать функцию.

Natives

Natives имеют встроенные функции, предоставляемые SourceMod. Вы можете вызвать их, как если бы они были normal функциями. Например, SourceMod имеет следующие функции:

native FloatRound(Float:num);

Её можно вызвать таким образом:

new num = FloatRound(5.2);     //Результат в num = 5

Параметры массива

Вы можете передавать массивы или строки в качестве параметров. Важно отметить, что они идут как ссылка. То есть не делать копию данных, а отдавать непосредственно ссылки на данные. Существует простой способ объяснить это более конкретно.

new example[] = {1, 2, 3, 4, 5};
 
ChangeArray(example, 2, 29);
 
ChangeArray(array[], index, value)
{
   array[index] = value;
}

Эта функция устанавливает заданный индекс в массиве с учетом значений. Когда она запускается на примере нашего массива, она меняет индекс 2 для значения 3 на 29. То есть:

example[2] = 29;

Это возможно лишь потому, что массив может быть непосредственно изменён. Чтобы предотвратить массив от изменения, можно пометить его как постоянную const. Это позволит понизить риск на ошибку в коде от её изменения. Например:

CantChangeArray(const array[], index, value)
{
   array[index] = value;    //Не компилируется
}

Это хорошая идея использовать const в параметрах массивов и Вы будете точно знать, что массив не будет изменен; это может предотвратить ошибки кодирования.

Выражения

Выражения являются точно такими же, какими они существуют в математике. Это группы операторов/символов, которые приходятся на один фрагмент данных. Они часто заключены в скобках (внутри скобок). Они содержат строгий "порядок операций". Они могут содержать переменные, функции, цифры и выражения сами могут быть вложенные внутрь других выражений, и даже приняты в качестве параметров.

Приведем пример простейшего выражения:

0;   //Возвращает число 0
(0); //Так же возвращает число 0

Хотя выражения могут возвращать значения, они также могут ответить какое значение содержит ответ ноль или не ноль. В этом смысле, ноль является ложью (false), а не нулевое значение истиной (true). Например, -1 истина в Pawn, поскольку она не является нулем. Не думайте, что отрицательные числа являются ложными.

Порядок операций выражения аналогичен языку C. PMDAS: Parenthesis, Multiplication, Division, Addition, Subtraction. Вот несколько примеров выражений:

5 + 6;                   //Вычисляет как 11
5 * 6 + 3;               //Вычисляет как 33
5 * (6 + 3);             //Вычисляет как 45
5.0 + 2.3;               //Вычисляет как 7.3
(5 * 6) % 7;             //Modulo operator, вычисляет как 2
(5 + 3) / 2 * 4 - 9;     //Вычисляет как 7

Как уже отмечалось, выражения могут содержать переменные, или даже функции:

new a = 5 * 6;
new b = a * 3;      //Вычисляет как 90
new c = AddTwoNumbers(a, b) + (a * b);

Примечание: String manipulation routines may be found in the string.inc file located in the include subdirectory. They may be browsed through the API Reference as well.

Операторы

Есть несколько полезных дополнительных операторов в Pawn. Первый набор упрощает аутоагрегацию выражения. Например:

new a = 5;
 
a = a + 5;

Может быть переписан, как:

new a = 5;
a += 5;

Это верно в отношении следующих операторов в Pawn:

  • Four-function: *, /, -, +
  • Bit-wise: |, &, ^, ~, <<, >>

Кроме того, существуют инкремент/декремент операторы:

a = a + 1;
a = a - 1;

Может быть упрощено, как:

a++;
a--;

Дополнительно отметим, что ++ или -- может быть представлен до переменной (до-инкремент, до-декремент) или после переменной (пост-инкремент, пост-декремент). Разница заключается в том, как остальная часть выражения содержащие их, видит результат.

  • До: Переменная увеличивается до определения и остальная часть выражения видит новое значение.
  • Пост: Переменная увеличивается после определения и остальная часть выражения видит старое значение.

Иными словами, a++ определяет значение как a в то время как ++a определяет значение как a + 1. В обоих случаях a увеличивается на 1.

Например:

new a = 5;
new b = a++;   // b = 5, a = 6  (1)
new c = ++a;   // a = 7, c = 7  (2)

(1) b присваивается a со старым значением, до того, как он будет увеличено до 6. (2) c присваивается a с новым значением, после того, как он увеличивается до 7.

Операторы сравнения

Существуют шесть операторов для сравнения двух числовых значений, а результат является либо истиной (не ноль) или ложью (ноль):

  • a == b - Действительно, если и b имеет то же значение.
  • a != b - Действительно, если b имеет другое значение.
  • a > b - Действительно, если оно больше b
  • a >= b - Действительно, если оно больше или равно b
  • a < b - Действительно, если оно меньше b
  • a <= b - Действительно, если оно меньше или равно b

Например:

(1 != 3);         //Определяется как истина, поскольку 1 не равно 3.
(3 + 3 == 6);     //Определяется как истина, поскольку 3+3 равно 6.
(5 - 2 >= 4);     //Определяется как ложь, поскольку 3 меньше 4.

Заметим, что эти операторы не работают с массивами и строками. То есть, вы не можете сравнить их с помощью ==.

Действительные операторы

Действительные операторы могут быть скомбинированы тремя булевыми (boolean) операторами:

  • a && b - Истина, если a и b истинные. Ложь, если a и (или) b ложные.
&& 0 1
0 0 0
1 0 1
  • a || b - Истина, если a или b (или обе переменные) истинные. Ложь, если обе переменные a и b ложные.
|| 0 1
0 0 1
1 1 1
  • !a - Истина, если a ложь. Ложь, если a истина.
! 0 1
1 0

Например:

(1 || 0);         //Определяется как истина, так как выражение 1 истинное
(1 && 0);         //Определяется как ложь, так как выражение 0 ложное
(!1 || 0);        //Определяется как ложь, так как выражение !1 ложное.

Левое/правое значения

Два важных понятия левого и правого значений, или левостороннее и правостороннее значения. Левостороннее значение имеет то, что появляется на левой стороне выражения, а правостороннее значение - появляется на правой стороне выражения.

Например:

new a = 5;

В этом примере a является левосторонним значением и 5 является правосторонним значением.

Правила:

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

Условия

Условия позволяют Вам запускать код, определенное условие выполнено.

Если соответствует

Если соответствует одно или более условий. Например:

if (a == 5)
{
   /* Код будет запущен, если условие будет истинным */
}

Они могут быть расширены для более сложных случаев:

if (a == 5)
{
   /* Код */
}
else if (a == 6)
{
   /* Код */
}
else if (a == 7)
{
   /* Код */
}

Вы так же можете обрабатывать случаи, даже если выражение не верно. Например:

if (a == 5)
{
   /* Код */
}
else
{
   /* Код, который будет запущен если нет истинного выражения */
}

Оператор выбора

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

switch (a)
{
   case 5:
   {
      /* Код */
   }
   case 6:
   {
      /* Код */
   }
   case 7:
   {
      /* Код */
   }
   case 8, 9, 10:
   {
      /* Код */
   }
   default:
   {
      /* будет запущен, если не одно условие не соответствует */
   }
}

В отличие от некоторых других языков, оператор выбора не проваливается. То есть существуют случаи, когда код не будет запущен. При случае совпадения его код выполняется, а ключ является местом для немедленного прекращения.

Циклы

Циклы позволяют Вам без труда повторять выполнение кода, пока условие станет истинным.

For циклы

For циклы, это циклы, которые состоят из четырех частей:

  • Оператор инициализации - запускается один раз перед первым циклом.
  • Оператор условия - проверяет условие и запускает следующий цикл, в том числе первый. Цикл прекращается, когда это выражение становится ложным.
  • Оператор итерации - запускается после каждого цикла.
  • тело цикла - запускается каждый раз, пока оператор условия вычисляется как истинный.
for ( /* инициализация */ ; /* условие */ ; /* итерация */ )
{
   /* тело */
}

Простым примером является функция сложения массива:

new array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
new sum = SumArray(array, 10);
 
SumArray(const array[], count)
{
   new total;
 
   for (new i = 0; i < count; i++)
   {
      total += array[i];
   }
 
   return total;
}

По отдельности:

  • new i = 0 - Создает новую переменную для цикла, и устанавливает её в 0.
  • i < count - Только запускает цикл, если i меньше, чем count. Это гарантирует, что чтение цикла прекращается в определенный момент. В этом случае, мы не хотим читать недействительные индексы в массиве.
  • i++ - Увеличивает i на единицу после каждого цикла. Это гарантирует, что цикл не будет запущен вечно; в конце концов i станет слишком большим, и цикл завершится.

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

While циклы

While циклы являются менее распространенными, чем for циклы, но на самом деле это более простые циклы. Они имеют только две части:

  • Оператор условия - проверяется перед началом каждого цикла. Цикл прекращается, когда условие становится ложным.
  • тело цикла - запускается каждый раз пока цикл выполняется.
while ( /* условие */ )
{
   /* тело */
}

До тех пор, пока условие выражения остается истинным, цикл будет выполняться. Каждый for цикл может быть переписан, как while цикл:

/* инициализация */
while ( /* условие */ )
{
   /* тело */
   /* итерация */
}

Вот предыдущий for цикл переписан как while цикл:

SumArray(const array[], count)
{
   new total, i;
 
   while (i < count)
   {
      total += array[i];
      i++;
   }
 
   return total;
}

Существуют также do...while циклы, которые используются еще реже. Они работают как и while циклы, но проверяют условие ПОСЛЕ каждого цикла, а не перед ним. Это означает, что цикл всегда будет запущен, по крайней мере один раз. Например:

do
{
   /* тело */
}
while ( /* условие */ );

Управление циклами

Существуют два случая, в которых Мы хотели бы контролировать цикл:

  • пропустить одну итерацию или цикл и продолжить выполнение цикла как обычно, или;
  • разорвать цикл целиком, прежде чем он закончится.

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

/**
 * Возвращает массив, если индекс значения, или -1, не найдены.
 */
SearchInArray(const array[], count, value)
{
   new index = -1;
 
   for (new i = 0; i < count; i++)
   {
      if (array[i] == value)
      {
         index = i;
         break;
      }
   }
 
   return index;
}

Конечно, эту функцию можно вернуть и способом return i, но пример показывает, как break может остановить цикл.

Кроме того, ключевое слово continue пропускает итерации цикла. Например, Мы хотим суммировать все четные числа:

SumEvenNumbers(const array[], count)
{
   new sum;
 
   for (new i = 0; i < count; i++)
   {
      /* If divisibility by 2 is 1, we know it's odd */
      if (array[i] % 2 == 1)
      {
         /* Пропускаем оставшуюся часть итерации цикла */
         continue;
      }
      sum += array[i];
   }
 
   return sum;
}

Область действия

Область действия относится к удобочитаемости кода. Это означает, что код одного уровня не может быть "виден" в коде другого уровня. Пример:

new A, B, C;
 
Function1()
{
   new B;
 
   Function2();
}
 
Function2()
{
   new C;
}

В этом примере, A, B, и C имеют глобальную область действия. Их можно увидеть в любой функции. Вместе с тем, B в функции Function1 не является той же переменной, как B на глобальном уровне. Вместо этого она находится в локальной области действия, и поэтому является локально изменяемой.

Кроме того, функции Function1 и Function2 ничего не знают о существовании других переменных.

Она так же является не только локальной переменной функции Function1, но и создается заново каждый раз, когда функция вызывается. Попробуйте представить это:

Function1()
{
   new B;
 
   Function1();
}

В приведенном выше примере, функция Function1 вызывает сама себя. Конечно, это бесконечной рекурсии (а это очень плохо), но идея заключается в том, что каждый раз, когда функция срабатывает, то создается новая копия B. Когда функция завершается, B уничтожается, и её значение теряется.

Это свойство можно объяснить проще тем, что область действия переменной равна уровню её вложенности. То есть, переменная на глобальной области действия видна для всех функций. Переменная в локальной области действия видна всему блоку кода расположенному "ниже" этого уровня. Например:

Function1()
{
   new A;
 
   if (A)
   {
      A = 5;
   }
}

Приведенный выше код является действительным, поскольку в область действия распространяется по всей функции. Однако этот код, работать не будет:

Function1()
{
   new A;
 
   if (A)
   {
      new B = 5;
   }
 
   B = 5;
}

Отметим, что B объявляется в новом блоке кода. Это означает, что B доступна только в том блоке кода, в котором была создана (и всем под-блокам вложенных внутри него). Как только блок кода прекращается, B становится не действительной.

Динамические массивы

Динамические массивы это массивы, которые не имеют фиксированного размера. Например:

Function1(size)
{
   new array[size];
 
   /* Код */
}

Динамические массивы могут иметь любое выражение, соответствующее их размеру до тех пор, пока выражение вычисляется, как число большее чем 0. Как и для обычных массивов, SourcePawn не сможет узнать размер массива после того, как он будет создан; Вы должны задать его, если хотите использовать массив позднее.

Динамические массивы, действительны только в локальной области действия, так как код не может существовать на глобальном уровне.

Расширенное объявление переменных

Переменные могут быть объявлены более длинным путем чем просто new.

decl

Назначение

По умолчанию, все переменные в Pawn будут инициализированы как нуль. Если есть явная инициализация, переменная инициализируется для выражения = определенным символам. В локальной области действия, это может потребовать время на выполнение. Ключевое слово decl (которое действительно только в локальной области действия) было введено, чтобы позволить решать пользователю, хочит ли он инициализировать переменную или нет.

Примечание: decl не должно быть использовано на одну однострочную переменную. Это почти никогда не будет выгодно.

Объяснение

Например:

new c = 5;
new d;
new String:blah[512];
 
Format(blah, sizeof(blah), "%d %d", c, d);

В этом коде, c равна 5 и d равна 0. Во время выполнения этого кода затраты на инициализацию незначительные. Вместе с тем, blah является большим массивом, и затраты на инициализацию всего массива могут быть больше 0 сек. и иметь плохие последствия в определенных ситуациях.

Заметим, что blah не должен быть нулевой. В период времени с объявления new и и перемещения в Format(), массив blah никогда не будет загружен или прочитан. Данный код будет более эффективен, если будет написанный следующим образом:

new c = 5;
new d;
decl String:blah[512];
 
Format(blah, sizeof(blah), "%d %d", c, d);

Предостережения

Обратная сторона decl состоит в том, что его переменные будут начинаться с "ненужного" содержания. Например, если мы будем использовать:

new c = 5;
new d;
decl String:blah[512];
 
PrintToServer("%s", blah);
 
Format(blah, sizeof(blah), "%d %d", c, d);

Этот код может привести к падению сервера, так как массив blah может быть полностью испорчен (строки требуют нулевой символ, который может отсутствовать). Точно так же, если мы сделаем:

new c = 5;
decl d;
decl String:blah[512];
 
Format(blah, sizeof(blah), "%d %d", c, d);

Значение d в настоящее время не определенно. Оно может быть любым значением, отрицательным или положительным.

Заметим, что это легко и эффективно обезопасит строки. Пример ниже показывает, как предотвратить строки от мусора:

decl String:blah[512];
 
blah[0] = '\0';

Золотые правила

  • Используйте decl только, если в период объявления и загрузки/чтения значения, Вы абсолютно уверены, что есть по крайней мере одно хранилище/операция, которая отдает переменной действительные данные.
  • Не оптимизируйте преждевременно. Кроме того, нет необходимости использовать decl на не-массивы, поскольку нет никаких дополнительных затрат на инициализацию однго однострочного значения.

Примечания

Обратите внимание, что decl переменную инициализировать явно, но ТОЛЬКО как строку:

decl String:blah[512] = "a";

However, any other tag will fail to compile, because the purpose of decl is to avoid any initialization:

decl Float:blah[512] = {1.0};

Даже несмотря на то, что строка имеет только один символ, оператор new гарантирует, что остальная часть массива будет нулевой.

Также обратите внимание, он является неправильным для явной инициализации decl:

decl String:blah[512] = "a";

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

static

Ключевое слово static входит в глобальную и локальную область действия. Оно имеет различные значения в каждой из них.

Глобальный static

Глобальные static переменные могут быть доступны только в рамках того же файла. Например:

//file1.inc
static Float:g_value1 = 0.15f;
 
//file2.inc
static Float:g_value2 = 0.15f;

Если плагин включает в себя оба этих файла, он не сможет использовать g_value1 или g_value2. Это простой механизм сокрытия информации, и аналогичен элементам объявления переменных, например private в таких языках, как C++, Java, или C#.

Локальный static

Локальная static переменная является глобальной переменной, которая является видимой лишь из её местной области действия. Например:

MyFunction(inc)
{
   static counter = -1;
 
   counter += inc;
 
   return counter;
}

В этом примере, counter технически глобальная переменная -- она инициализируется один раз как -1 и никогда не инициализируется заново. Оно не существует в наборе. Это означает, что при каждом запуске функции MyFunction, переменная counter и ее хранение в памяти одно и тоже.

Возьмем следающий пример:

MyFunction(5);
MyFunction(6);
MyFunction(10);

В этом примере, counter может быть -1 +5 +6 +10, или 20, поскольку она сохраняется за рамками этой функции. Это может создавать проблемы для рекурсивной функции: если ваша функция может быть рекурсивной, то static, как правило, не является хорошей идеей, если только Ваш код не реентерабельный.

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

Заметим, что statics может существовать в любой локальной области действия:

MyFunction(inc)
{
   if (inc > 0)
   {
      static counter;
      return (counter += inc);
   }
   return -1;
}