VVWVV
24.03.2017, 02:35
foreach - одна из библиотек проекта YSI (y_iterate), добавляющая возможность создавать собственные итераторы (https://ru.wikipedia.org/wiki/%D0%98%D1%82%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80). Из-за особенностей алгоритма процесс итерирования может проходить намного быстрее простого перебора. Помимо этого, библиотека имеет обширный функционал.
Как было сказано выше, библиотека является частью проекта YSI, но, тем не менее, существует версия без зависимости от YSI. Отличие заключается лишь в том, что на данный момент только версия из YSI поддерживается самим разработчиком.
Ограничения и особенности алгоритма
Некоторые ограничения:
В итератор не могут входить числа меньше нуля и больше, чем размер итератора.
В итераторе все числа идут по порядку, кроме самого меньшего (он расположен в самом конце итератора)
Алгоритм и его особенности
Дело в том, что при инициализации итератора вы создаёте всегда на одну ячейку больше. Эта ячейка будет содержать размер итератора (не всегда, см. выше); она также помогает определить границу итератора при итерировании.
Итерирование происходит так: сначала читается последний элемент, если он не равен размеру итератора, то значение в ячейке записывается. Затем происходит ещё одна итерация, теперь уже сравнивается размер и значение элемента массива с индексом записанного числа и так много раз, пока значение элемента не будет равно размеру итератора. Поэтому вы не можете указывать числа больше размера итератора или меньше нуля, т.к. это нарушит алгоритм и вызовет ошибки.
http://ihost.pro-pawn.ru/image.php?di=85FQ
Как видим, все остальные ячейки заполняются числами больше размера массива, это делается для того, чтобы поместить в последнюю ячейку именно размер.
Создание итераторов
Обычный итератор
Для создания такого итератора необходимо знать только шаблон макроса.
Iterator:name<10>
name - имя итератора.
10 - размер итератора.
Предостережения:
Макрос на самом деле генерирует сразу две переменные: переменную с количеством действующих элементов, а также сам массив.
Массив заполняется значениями при его декларировании, следовательно, вы не сможете установить собственные значения.
К имени прибавляется суффиксы "@YSII_Cg" для переменной с количеством элементов и "@YSII_Ag" для массива.
Многомерные итераторы
Для создания многомерных итераторов необходимо знать не только шаблон, но и функцию заполнения, поскольку массивы не могут быть инициализированы под алгоритм во время компиляции.
Рассмотрим шаблон:
IteratorArray:name[12]<10>
name - имя итератора.
12 - размер первого итератора.
10 - размер второго итератора.
Предостережения:
Макрос на самом деле генерирует сразу две переменные: массив с количеством действующих элементов для каждого массива, а также сам многомерный массив.
Необходимо инициализировать функцией.
К имени прибавляется суффиксы "@YSII_Cg" для переменной с количеством элементов и "@YSII_Ag" для массива.
Пример инициализации:
new IteratorArray:PlayerObjects[MAX_PLAYERS]<10>;
public OnGameModeInit() {
Iter_Init(PlayerObjects);
}
Документация и использование итераторов
Документация к функциям
В библиотеке присутствует множество разнообразных функций, помогающих в работе с итераторами.
Название
Описание
Iter_Init
Инициализирует многомерные итераторы.
Параметры:
name - имя итератора
Возвращает:
Всегда нуль.
Iter_Add
Добавляет новое значение в итератор.
Параметры:
name - имя итератора
value - добавляемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь.
Iter_Free
Находит свободный слот в итераторе.
Параметры:
name - имя итератора
Возвращает:
Индекс свободного слота.
Iter_Remove
Удаляет значение из итератора
Параметры:
name - имя итератора
value - удаляемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь.
Iter_Contains
Проверяет наличие значения в итераторе
Параметры:
name - имя итератора
value - проверяемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь.
Iter_SafeRemove
Безопасно удаляет значение из итератора.
Параметры:
name - имя итератора
value - удаляемое значение
next - индекс на следующий элемент.
Возвращает:
В случае успеха возвращает истину, иначе ложь.
Iter_Random
Случайно определяет число из итератора.
Параметры:
name - имя итератора
Возвращает:
Случайное число из итератора
Iter_Count
Количество добавленных элементов.
Параметры:
name - имя итератора
Возвращает:
Количество добавленных элементов.
Iter_Clear
Освобождает итератор от всех значений.
Параметры:
name - имя итератора
Возвращает:
Всегда нуль.
Iter_First
Получает первый элемент в итераторе.
Параметры:
name - имя итератора
Возвращает:
Возвращает индекс.
Iter_Last
Получает последний элемент в итераторе.
Параметры:
name - имя итератора
Возвращает:
Возвращает индекс.
Iter_Next
Получает следующий элемент в итераторе.
Параметры:
name - имя итератора
current - текущий индекс.
Возвращает:
Возвращает следующий индекс.
Iter_Prev
Получает предыдущий элемент в итераторе.
Параметры:
name - имя итератора
current - текущий индекс.
Возвращает:
Возвращает следующий индекс.
Iter_InternalArray
Получает доступ к массиву.
Параметры:
name - имя итератора.
Возвращает:
-
Iter_InternalSize
Получает действительный размер массива.
Параметры:
name - имя итератора.
Возвращает:
Размер массива.
Созданные итераторы
В библиотеке присутствуют уже созданные итераторы такие как: Player, Vehicle, Bot, NPC (то же самое, что и Bot), Character (NPC и игроки). Поэтому вам не придётся создавать их снова.
Псевдооператор foreach
Именно с помощью данного оператора и происходит итерирование. Данный оператор схож с range-based циклами в С++11. Однако у foreach есть и старая версия синтаксиса, которую не рекомендуют использовать. Кстати, в новой версии библиотеки она всё ещё присутствуют, но, тем не менее, при использовании появляется предупреждение, говорящее как раз об этом. Поэтому в данном уроке его не будет.
Рассмотрим синтаксис:
foreach(new i : Player)
new - спецификатор, говорящий о том, что необходимо создать новую переменную.
i - название переменной, в которую будет записываться значение из итератора. Оно может быть абсолютно любое.
Player - название итератора, из которого мы будет брать значения.
Примеры использования
Пример №1
Допустим, что нам нужно сделать определённый массив, в котором будут храниться идентификаторы администраторов. С помощью этого массива мы, например, будет отправлять сообщения всем администраторам. Как раз для этого и необходимы итераторы.
Создадим итератор, например, с названием ConnectedAdmins.
new Iterator:ConnectedAdmins<MAX_PLAYERS>;
Далее создадим функцию отправки сообщения этим администраторам:
stock SendAdminMessage(adminid, color, const message[])
{
foreach(new i : ConnectedAdmins) {
SendClientMessage(i, color, message);
}
}
Теперь нам остаётся сделать только добавление/удаление идентификаторов в итератор при входе/выходе администратора.
Пример №2
Итак. Суть данного примера заключается в том, что мы должны вывести цифры в обратном порядке, т.е. сделать простой обратный цикл.
Создадим итератор, например, с названием Nums. Он будет содержать, допустим, 10 ячеек.
new Iterator:Nums<10>;
В библиотеки не оказалось обратного цикла, поэтому мы сделаем свой:
stock PrintNums() {
for (new i = Iter_End(Nums); (i = Iter_Prev(Nums, i)) != Iter_Begin(Nums);)
printf("%d", i);
}
Нам осталось лишь загрузить значения и скомпилировать.
Пример №3
Создадим скрипт, суммирующий счёт каждой команды. В нём нам необходимо использовать массива итераторов.
#define MAX_TEAMS 255
new
Iterator:Team[MAX_TEAMS]<MAX_PLAYERS>;
public OnGameModeInit()
{
Iter_Init(Team);
#if defined Team_OnGameModeInit
return Team_OnGameModeInit();
#else
return 1;
#endif
}
#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit Team_OnGameModeInit
#if defined Team_OnGameModeInit
forward Team_OnGameModeInit();
#endif
stock Team_SetPlayerTeam(playerid, teamid)
{
new current_team = GetPlayerTeam(playerid);
if (current_team != NO_TEAM) {
Iter_Remove(Team[current_team], playerid);
}
if (teamid != NO_TEAM) {
Iter_Add(Team[teamid], playerid);
}
return SetPlayerTeam(playerid, teamid);
}
#if defined _ALS_SetPlayerTeam
#undef SetPlayerTeam
#else
#define _ALS_SetPlayerTeam
#endif
#define SetPlayerTeam Team_SetPlayerTeam
Использовать так:
new scores[MAX_TEAMS];
for (new teamid; teamid < MAX_TEAMS; teamid++) {
foreach (new playerid : Team[teamid]) {
scores[teamid] += GetPlayerScore(playerid);
}
}
for (new teamid; teamid < MAX_TEAMS; teamid++) {
printf("Team %d: %d", teamid, scores[teamid]);
}
Разные версии
Существует две версии данной библиотеки:
foreach - автономная версия, не зависящая от других инклудов. Если вы не используете YSI, то можете смело скачивать эту версию. Автор (Y_Less) отказался поддерживать этот инклуд, поэтому форки инклуда есть сразу у нескольких представителей сообщества SA-MP - на момент написания наиболее актуальный форк можно найти у ziggi.
Отличается данная версия наличием итераторов для актёров и транспорта (в том числе итераторов Stream, которые выключены по умолчанию), поддержкой множественных скриптов (FS + GM), оптимизированным ALS (без использования CallLocalFunction) и отсутствием поддержки npcmodes (для более чистого кода).
y_iterate - это версия, поддерживаемая самим разработчиком YSI. Однако, чтобы использовать её, необходимо скачать полностью весь проект YSI, ибо она привязана к другим инклудам. Таким образом, если вы используете YSI в своём проекте, то вам больше подойдёт y_iterate.
Скачать:
foreach - https://github.com/Open-GTO/foreach
y_iterate - https://github.com/Misiur/YSI-Includes
Для скачивания на открывшейся странице нажмите на кнопку "Clone or download" (зелёного цвета) и в открывшемся меню - "Download ZIP".
Статью подготовил: VVWVV (http://pro-pawn.ru/member.php?4348)
Исключительно для pro-pawn.ru
Копирование данной статьи на других ресурсах без разрешения автора запрещено!
Как было сказано выше, библиотека является частью проекта YSI, но, тем не менее, существует версия без зависимости от YSI. Отличие заключается лишь в том, что на данный момент только версия из YSI поддерживается самим разработчиком.
Ограничения и особенности алгоритма
Некоторые ограничения:
В итератор не могут входить числа меньше нуля и больше, чем размер итератора.
В итераторе все числа идут по порядку, кроме самого меньшего (он расположен в самом конце итератора)
Алгоритм и его особенности
Дело в том, что при инициализации итератора вы создаёте всегда на одну ячейку больше. Эта ячейка будет содержать размер итератора (не всегда, см. выше); она также помогает определить границу итератора при итерировании.
Итерирование происходит так: сначала читается последний элемент, если он не равен размеру итератора, то значение в ячейке записывается. Затем происходит ещё одна итерация, теперь уже сравнивается размер и значение элемента массива с индексом записанного числа и так много раз, пока значение элемента не будет равно размеру итератора. Поэтому вы не можете указывать числа больше размера итератора или меньше нуля, т.к. это нарушит алгоритм и вызовет ошибки.
http://ihost.pro-pawn.ru/image.php?di=85FQ
Как видим, все остальные ячейки заполняются числами больше размера массива, это делается для того, чтобы поместить в последнюю ячейку именно размер.
Создание итераторов
Обычный итератор
Для создания такого итератора необходимо знать только шаблон макроса.
Iterator:name<10>
name - имя итератора.
10 - размер итератора.
Предостережения:
Макрос на самом деле генерирует сразу две переменные: переменную с количеством действующих элементов, а также сам массив.
Массив заполняется значениями при его декларировании, следовательно, вы не сможете установить собственные значения.
К имени прибавляется суффиксы "@YSII_Cg" для переменной с количеством элементов и "@YSII_Ag" для массива.
Многомерные итераторы
Для создания многомерных итераторов необходимо знать не только шаблон, но и функцию заполнения, поскольку массивы не могут быть инициализированы под алгоритм во время компиляции.
Рассмотрим шаблон:
IteratorArray:name[12]<10>
name - имя итератора.
12 - размер первого итератора.
10 - размер второго итератора.
Предостережения:
Макрос на самом деле генерирует сразу две переменные: массив с количеством действующих элементов для каждого массива, а также сам многомерный массив.
Необходимо инициализировать функцией.
К имени прибавляется суффиксы "@YSII_Cg" для переменной с количеством элементов и "@YSII_Ag" для массива.
Пример инициализации:
new IteratorArray:PlayerObjects[MAX_PLAYERS]<10>;
public OnGameModeInit() {
Iter_Init(PlayerObjects);
}
Документация и использование итераторов
Документация к функциям
В библиотеке присутствует множество разнообразных функций, помогающих в работе с итераторами.
Название
Описание
Iter_Init
Инициализирует многомерные итераторы.
Параметры:
name - имя итератора
Возвращает:
Всегда нуль.
Iter_Add
Добавляет новое значение в итератор.
Параметры:
name - имя итератора
value - добавляемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь.
Iter_Free
Находит свободный слот в итераторе.
Параметры:
name - имя итератора
Возвращает:
Индекс свободного слота.
Iter_Remove
Удаляет значение из итератора
Параметры:
name - имя итератора
value - удаляемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь.
Iter_Contains
Проверяет наличие значения в итераторе
Параметры:
name - имя итератора
value - проверяемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь.
Iter_SafeRemove
Безопасно удаляет значение из итератора.
Параметры:
name - имя итератора
value - удаляемое значение
next - индекс на следующий элемент.
Возвращает:
В случае успеха возвращает истину, иначе ложь.
Iter_Random
Случайно определяет число из итератора.
Параметры:
name - имя итератора
Возвращает:
Случайное число из итератора
Iter_Count
Количество добавленных элементов.
Параметры:
name - имя итератора
Возвращает:
Количество добавленных элементов.
Iter_Clear
Освобождает итератор от всех значений.
Параметры:
name - имя итератора
Возвращает:
Всегда нуль.
Iter_First
Получает первый элемент в итераторе.
Параметры:
name - имя итератора
Возвращает:
Возвращает индекс.
Iter_Last
Получает последний элемент в итераторе.
Параметры:
name - имя итератора
Возвращает:
Возвращает индекс.
Iter_Next
Получает следующий элемент в итераторе.
Параметры:
name - имя итератора
current - текущий индекс.
Возвращает:
Возвращает следующий индекс.
Iter_Prev
Получает предыдущий элемент в итераторе.
Параметры:
name - имя итератора
current - текущий индекс.
Возвращает:
Возвращает следующий индекс.
Iter_InternalArray
Получает доступ к массиву.
Параметры:
name - имя итератора.
Возвращает:
-
Iter_InternalSize
Получает действительный размер массива.
Параметры:
name - имя итератора.
Возвращает:
Размер массива.
Созданные итераторы
В библиотеке присутствуют уже созданные итераторы такие как: Player, Vehicle, Bot, NPC (то же самое, что и Bot), Character (NPC и игроки). Поэтому вам не придётся создавать их снова.
Псевдооператор foreach
Именно с помощью данного оператора и происходит итерирование. Данный оператор схож с range-based циклами в С++11. Однако у foreach есть и старая версия синтаксиса, которую не рекомендуют использовать. Кстати, в новой версии библиотеки она всё ещё присутствуют, но, тем не менее, при использовании появляется предупреждение, говорящее как раз об этом. Поэтому в данном уроке его не будет.
Рассмотрим синтаксис:
foreach(new i : Player)
new - спецификатор, говорящий о том, что необходимо создать новую переменную.
i - название переменной, в которую будет записываться значение из итератора. Оно может быть абсолютно любое.
Player - название итератора, из которого мы будет брать значения.
Примеры использования
Пример №1
Допустим, что нам нужно сделать определённый массив, в котором будут храниться идентификаторы администраторов. С помощью этого массива мы, например, будет отправлять сообщения всем администраторам. Как раз для этого и необходимы итераторы.
Создадим итератор, например, с названием ConnectedAdmins.
new Iterator:ConnectedAdmins<MAX_PLAYERS>;
Далее создадим функцию отправки сообщения этим администраторам:
stock SendAdminMessage(adminid, color, const message[])
{
foreach(new i : ConnectedAdmins) {
SendClientMessage(i, color, message);
}
}
Теперь нам остаётся сделать только добавление/удаление идентификаторов в итератор при входе/выходе администратора.
Пример №2
Итак. Суть данного примера заключается в том, что мы должны вывести цифры в обратном порядке, т.е. сделать простой обратный цикл.
Создадим итератор, например, с названием Nums. Он будет содержать, допустим, 10 ячеек.
new Iterator:Nums<10>;
В библиотеки не оказалось обратного цикла, поэтому мы сделаем свой:
stock PrintNums() {
for (new i = Iter_End(Nums); (i = Iter_Prev(Nums, i)) != Iter_Begin(Nums);)
printf("%d", i);
}
Нам осталось лишь загрузить значения и скомпилировать.
Пример №3
Создадим скрипт, суммирующий счёт каждой команды. В нём нам необходимо использовать массива итераторов.
#define MAX_TEAMS 255
new
Iterator:Team[MAX_TEAMS]<MAX_PLAYERS>;
public OnGameModeInit()
{
Iter_Init(Team);
#if defined Team_OnGameModeInit
return Team_OnGameModeInit();
#else
return 1;
#endif
}
#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit Team_OnGameModeInit
#if defined Team_OnGameModeInit
forward Team_OnGameModeInit();
#endif
stock Team_SetPlayerTeam(playerid, teamid)
{
new current_team = GetPlayerTeam(playerid);
if (current_team != NO_TEAM) {
Iter_Remove(Team[current_team], playerid);
}
if (teamid != NO_TEAM) {
Iter_Add(Team[teamid], playerid);
}
return SetPlayerTeam(playerid, teamid);
}
#if defined _ALS_SetPlayerTeam
#undef SetPlayerTeam
#else
#define _ALS_SetPlayerTeam
#endif
#define SetPlayerTeam Team_SetPlayerTeam
Использовать так:
new scores[MAX_TEAMS];
for (new teamid; teamid < MAX_TEAMS; teamid++) {
foreach (new playerid : Team[teamid]) {
scores[teamid] += GetPlayerScore(playerid);
}
}
for (new teamid; teamid < MAX_TEAMS; teamid++) {
printf("Team %d: %d", teamid, scores[teamid]);
}
Разные версии
Существует две версии данной библиотеки:
foreach - автономная версия, не зависящая от других инклудов. Если вы не используете YSI, то можете смело скачивать эту версию. Автор (Y_Less) отказался поддерживать этот инклуд, поэтому форки инклуда есть сразу у нескольких представителей сообщества SA-MP - на момент написания наиболее актуальный форк можно найти у ziggi.
Отличается данная версия наличием итераторов для актёров и транспорта (в том числе итераторов Stream, которые выключены по умолчанию), поддержкой множественных скриптов (FS + GM), оптимизированным ALS (без использования CallLocalFunction) и отсутствием поддержки npcmodes (для более чистого кода).
y_iterate - это версия, поддерживаемая самим разработчиком YSI. Однако, чтобы использовать её, необходимо скачать полностью весь проект YSI, ибо она привязана к другим инклудам. Таким образом, если вы используете YSI в своём проекте, то вам больше подойдёт y_iterate.
Скачать:
foreach - https://github.com/Open-GTO/foreach
y_iterate - https://github.com/Misiur/YSI-Includes
Для скачивания на открывшейся странице нажмите на кнопку "Clone or download" (зелёного цвета) и в открывшемся меню - "Download ZIP".
Статью подготовил: VVWVV (http://pro-pawn.ru/member.php?4348)
Исключительно для pro-pawn.ru
Копирование данной статьи на других ресурсах без разрешения автора запрещено!