foreach - одна из библиотек проекта YSI (y_iterate), добавляющая возможность создавать собственные
итераторы. Из-за особенностей алгоритма процесс итерирования может проходить намного быстрее простого перебора. Помимо этого, библиотека имеет обширный функционал.
Как было сказано выше, библиотека является частью проекта YSI, но, тем не менее, существует версия без зависимости от YSI. Отличие заключается лишь в том, что на данный момент только версия из YSI поддерживается самим разработчиком.
Некоторые ограничения:
- В итератор не могут входить числа меньше нуля и больше, чем размер итератора.
- В итераторе все числа идут по порядку, кроме самого меньшего (он расположен в самом конце итератора)
Алгоритм и его особенности
Дело в том, что при инициализации итератора вы создаёте всегда на одну ячейку больше. Эта ячейка будет содержать размер итератора (не всегда, см. выше); она также помогает определить границу итератора при итерировании.
Итерирование происходит так: сначала читается последний элемент, если он не равен размеру итератора, то значение в ячейке записывается. Затем происходит ещё одна итерация, теперь уже сравнивается размер и значение элемента массива с индексом записанного числа и так много раз, пока значение элемента не будет равно размеру итератора. Поэтому вы не можете указывать числа больше размера итератора или меньше нуля, т.к. это нарушит алгоритм и вызовет ошибки.
Как видим, все остальные ячейки заполняются числами больше размера массива, это делается для того, чтобы поместить в последнюю ячейку именно размер.
Документация к функциям
В библиотеке присутствует множество разнообразных функций, помогающих в работе с итераторами.
Документация к функциям Название |
Описание |
Iter_Init |
Инициализирует многомерные итераторы.
Параметры:
Возвращает:
Всегда нуль. |
Iter_Add |
Добавляет новое значение в итератор.
Параметры:
- name - имя итератора
- value - добавляемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь. |
Iter_Free |
Находит свободный слот в итераторе.
Параметры:
Возвращает:
Индекс свободного слота. |
Iter_Remove |
Удаляет значение из итератора
Параметры:
- name - имя итератора
- value - удаляемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь. |
Iter_Contains |
Проверяет наличие значения в итераторе
Параметры:
- name - имя итератора
- value - проверяемое значение
Возвращает:
В случае успеха возвращает истину, иначе ложь. |
Iter_SafeRemove |
Безопасно удаляет значение из итератора.
Параметры:
- name - имя итератора
- value - удаляемое значение
- next - индекс на следующий элемент.
Возвращает:
В случае успеха возвращает истину, иначе ложь. |
Iter_Random |
Случайно определяет число из итератора.
Параметры:
Возвращает:
Случайное число из итератора |
Iter_Count |
Количество добавленных элементов.
Параметры:
Возвращает:
Количество добавленных элементов. |
Iter_Clear |
Освобождает итератор от всех значений.
Параметры:
Возвращает:
Всегда нуль. |
Iter_First |
Получает первый элемент в итераторе.
Параметры:
Возвращает:
Возвращает индекс. |
Iter_Last |
Получает последний элемент в итераторе.
Параметры:
Возвращает:
Возвращает индекс. |
Iter_Next |
Получает следующий элемент в итераторе.
Параметры:
- name - имя итератора
- current - текущий индекс.
Возвращает:
Возвращает следующий индекс. |
Iter_Prev |
Получает предыдущий элемент в итераторе.
Параметры:
- name - имя итератора
- current - текущий индекс.
Возвращает:
Возвращает следующий индекс. |
Iter_InternalArray |
Получает доступ к массиву.
Параметры:
Возвращает:
- |
Iter_InternalSize |
Получает действительный размер массива.
Параметры:
Возвращает:
Размер массива. |
Созданные итераторы
В библиотеке присутствуют уже созданные итераторы такие как: Player, Vehicle, Bot, NPC (то же самое, что и Bot), Character (NPC и игроки). Поэтому вам не придётся создавать их снова.
Псевдооператор foreach
Именно с помощью данного оператора и происходит итерирование. Данный оператор схож с range-based циклами в С++11. Однако у foreach есть и старая версия синтаксиса, которую не рекомендуют использовать. Кстати, в новой версии библиотеки она всё ещё присутствуют, но, тем не менее, при использовании появляется предупреждение, говорящее как раз об этом. Поэтому в данном уроке его не будет.
Рассмотрим синтаксис:
Код:
foreach(new i : Player)
new - спецификатор, говорящий о том, что необходимо создать новую переменную.
i - название переменной, в которую будет записываться значение из итератора. Оно может быть абсолютно любое.
Player - название итератора, из которого мы будет брать значения.
Примеры использования
Пример №1
Допустим, что нам нужно сделать определённый массив, в котором будут храниться идентификаторы администраторов. С помощью этого массива мы, например, будет отправлять сообщения всем администраторам. Как раз для этого и необходимы итераторы.
Создадим итератор, например, с названием ConnectedAdmins.
PHP код:
new Iterator:ConnectedAdmins<MAX_PLAYERS>;
Далее создадим функцию отправки сообщения этим администраторам:
PHP код:
stock SendAdminMessage(adminid, color, const message[])
{
foreach(new i : ConnectedAdmins) {
SendClientMessage(i, color, message);
}
}
Теперь нам остаётся сделать только добавление/удаление идентификаторов в итератор при входе/выходе администратора.
Пример №2
Итак. Суть данного примера заключается в том, что мы должны вывести цифры в обратном порядке, т.е. сделать простой обратный цикл.
Создадим итератор, например, с названием Nums. Он будет содержать, допустим, 10 ячеек.
PHP код:
new Iterator:Nums<10>;
В библиотеки не оказалось обратного цикла, поэтому мы сделаем свой:
PHP код:
stock PrintNums() {
for (new i = Iter_End(Nums); (i = Iter_Prev(Nums, i)) != Iter_Begin(Nums);)
printf("%d", i);
}
Нам осталось лишь загрузить значения и скомпилировать.
Пример №3
Создадим скрипт, суммирующий счёт каждой команды. В нём нам необходимо использовать массива итераторов.
PHP код:
#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
Использовать так:
PHP код:
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]);
}