[Tutorial] y_iterate (foreach 3.0)
#1

foreach 3.0


Introduction

This is a major new version of "foreach", I'm going to assume some familiarity with the existing version. If you don't have that, read this topic first: [MISSING LINK]

Breaking Changes
  • Most of the internal names, prefixes, and macros have changed. This shouldn't be a problem though as you shouldn't be using the internals directly...
  • "Iter_InternalArray" is now "Iter_TrueArray", and "Iter_InternalSize" is now "Iter_TrueCount". The use of "Internal" in the name was confusing, because they were designed as an external API to provide direct access to some implementation details.
  • Special iterator functions have changed significantly. The old style looked like:

    Код:
    iterfunc Tag:MyIterator(other, Tag:cur)
    {
        return Tag:-1;
    }
    The new version looks like:

    Код:
    #define iterstart@MyIterator -1
    iterfunc Tag:MyIterator(cur, other)
    {
        return Tag:-1;
    }
    There are two main changes: The "iterstart@" definition tells the system what the invalid value for your iterator is - this used to default to -1, but needed to be made more generic. The "cur" value has moved to the start of the parameter list, and has lost any optional tags (even if the function still returns a tag) - this is to allow more than one additional parameter to the iterator function: "MyIterator(cur, other, more, yes)" and "foreach (new i : MyIterator(3, 4, 5))"
  • The return values of "Iter_Add", "Iter_Remove", and some other functions are no longer boolean. Instead, they return the value just added to/removed from the iterator, or -1 on failure.
  • This now REQUIRES YSI - there are too many changes to keep two parallel versions going since many features would require twice the code without YSI.
Note that these changes should not affect basic uses of just "Player" iterators and others.


New Features
  • Multi-Iterators
A multi-iterator is an iterator where there are multiple lists over a set of elements, and each element can be in just one list. For example, a list of owned cars. The set of elements is the set of all vehicles, each player can have a list of vehicles they own, and each vehicle can have at most one owner. Previously, this looked like:

Код:
new
    Iterator:OwnedVehicles[MAX_PLAYERS]<MAX_VEHICLES>;
Iter_Init(OwnedVehicles);
foreach (new i : OwnedVehicles[playerid])
{
}
That works, but is very inefficient. Every player has a slot for every vehicle, but since each vehicle can only be owned by one player, most of those slots are wasted. What's more, there's nothing actually stopping you doing:

Код:
Iter_Add(OwnedVehicles[5], 5);
Iter_Add(OwnedVehicles[7], 5);
To assign vehicle "5" as owned by two different people.

A multi-iterator for this would look like:

Код:
new
    Iterator:OwnedVehicles<MAX_PLAYERS, MAX_VEHICLES>;
foreach (new i : OwnedVehicles<playerid>)
{
}
There are several advantages to this. Firstly, this array is much smaller - instead of using (more than) "MAX_PLAYERS * MAX_VEHICLES" slots, it only uses "MAX_PLAYERS + MAX_VEHICLES", which is close to optimal. Secondly, because each vehicle can only appear in a single iterator, this code will fail:

Код:
Iter_Add(OwnedVehicles<5>, 5);
Iter_Add(OwnedVehicles<7>, 5);
When trying to add a vehicle, you can check if it succeeded:

Код:
if (Iter_Add(OwnedVehicles<7>, 5) == -1)
{
    // Can't add the vehicle to this iterator.
}
You can check if a value is in a specific list:

Код:
if (Iter_Contains(MyIter<5>, 11))
{
}
Or is in any of the lists:

Код:
if (Iter_Contains(MyIter<>, 11))
{
}
Removing a value from the wrong list will do nothing, because it isn't in that list!

Код:
Iter_Add(MyIter<5>, 11);
foreach (new i : MyIter<5>) printf("%d", i);
Iter_Remove(MyIter<7>, 11);
foreach (new i : MyIter<5>) printf("%d", i);
Outputs:

Код:
11
11
  • Reverse Iterators
The old version of foreach had reverse iterators in the form of "Iter_Prev(iter, x)", but they were clunky and slow. The new version has made two important changes. Firstly, for NORMAL iterators (i.e. not multi-iterators or special iterators), going backwards is almost as fast as going forwards). Secondly, you can reverse an iterator directly:

Код:
Iter_Add(MyIter, 4);
Iter_Add(MyIter, 7);
Iter_Add(MyIter, 20);
foreach (new i : Reverse(MyIter))
{
}
Outputs:

Код:
20
7
4
  • New Iterators
The old version of "foreach" came with four iterators: "Player", "NPC", "Character", and "Bot". This new version will have more, currently it only has one more, and that is mainly for a special iterator function demo:

Код:
foreach (new i : Range(4, 8))
{
    printf("%d", i);
}
Outputs:

Код:
4
5
6
7
Код:
foreach (new i : Range(4, 8, 2))
{
    printf("%d", i);
}
Outputs:

Код:
4
6
Код:
foreach (new i : Range(10, 6, -1))
{
    printf("%d", i);
}
Outputs:

Код:
10
9
8
7
  • YSI Iterators
These are not new to this version, but they aren't really documented. YSI includes a number of iterators:

Код:
new
    BitArray:x<100>;
foreach (new i : Bits(x))
{
    // Get all the slots that are "1" in "x".
}
Код:
new
    BitArray:x<100>;
foreach (new i : Blanks(x))
{
    // Get all the slots that are "0" in "x".
}
Код:
foreach (new i : Command())
{
    // Loop over all y_commands commands.
}
Код:
foreach (new i : PlayerCommand(playerd))
{
    // Loop over all y_commands commands a given player can use.
}
Код:
foreach (new Group:i : GroupChild(g))
{
    // Loop over all the child groups of y_groups Group:g.
}
Код:
// Should be "PlayerGroup", sorry!
foreach (new Group:i : PlayerGroups(playerid))
{
    // Loop over all the groups a player is in.
}
Код:
foreach (new i : GroupMemeber(g))
{
    // Loop over all the players in a group.
}
Код:
foreach (new Group:i : CreatedGroup())
{
    // Loop over all groups in y_groups.
}
  • Deprecation
The "foreach (Player, i)" syntax, which was deprecated YEARS ago now gives a warning when used. The warning contains the text "using_deprecated_foreach_syntax". This is the only change that will affect regular users, which is why I made it non-breaking.
  • Nicer Errors
Doing this:

Код:
new
    Iterator:ArrayIter[10]<20>;
Iter_Add(ArrayIter[5], 10);
Now gives the following warning:

Код:
Symbol is never used "Iter_Init@ArrayIter"
The solution, as with all multi-dimensional iterators, is to call "Iter_Init":

Код:
new
    Iterator:ArrayIter[10]<20>;
Iter_Init(ArrayIter);
Iter_Add(ArrayIter[5], 10);
Sadly, the warning isn't fool-proof. This will not work, but will compile silently:

Код:
new
    Iterator:ArrayIter[10]<20>;
Iter_Add(ArrayIter[5], 10);
Iter_Init(ArrayIter);
Using a standard iterator as a multi-iterator will give the following error:

Код:
Undefined symbol "Iter_Multi@IterName"
Likewise, using a multi-iterator like a regular iterator will say:

Код:
Undefined symbol "Iter_Single@IterName"
  • Forgiving Syntax
With the old version of "foreach", you HAD to do this spacing:

Код:
foreach (new i : Player)
The following subtly different code gave horrible errors:

Код:
foreach ( new i : Player )
Now it doesn't. In fact, almost any spacings in any functions are now accepted.
  • Iter_Alloc
Previously, "Iter_Free" would return a value not present in an iterator, and "Iter_Add" would add a value. "Iter_Alloc" simply combines the two features - finds an unused value, adds it to the iterator, and returns it:

Код:
new
    val = Iter_Alloc(MyIter);
  • Correct Spellings
For years there were two copies of all the functions, because the very first version spelt them all wrong - for example "Iter_Add" and "Itter_Add". "Iterate" does not have a double "t" in it! The incorrect copies have now been removed.
  • Code Cleanup
This isn't really an end-user affecting change, but everything internal to the library is now MUCH nicer to work with and read. It was actually even better during development, but I renamed some macros to make the resulting code shorter.
  • Tests
I've added this to y_testing, so changes can be verified quickly.


Download


This version of "foreach" is in the latest version of "YSI" on github:

https://github.com/Misiur/YSI-Includes/tree/YSI.tl
Reply


Messages In This Thread
y_iterate (foreach 3.0) - by Misiur - 16.04.2015, 18:18
Re: y_iterate (foreach 3.0) - by hamzajaved780 - 17.04.2015, 09:18
Re: y_iterate (foreach 3.0) - by Konstantinos - 17.04.2015, 09:22
Re: y_iterate (foreach 3.0) - by RaeF - 12.05.2015, 07:45
Re: y_iterate (foreach 3.0) - by Emmet_ - 12.05.2015, 08:45
Re: y_iterate (foreach 3.0) - by RaeF - 12.05.2015, 09:21
Re: y_iterate (foreach 3.0) - by Konstantinos - 12.05.2015, 12:41
Re: y_iterate (foreach 3.0) - by Psymetrix - 12.05.2015, 20:45
Re: y_iterate (foreach 3.0) - by Konstantinos - 12.05.2015, 20:51
Re: y_iterate (foreach 3.0) - by Psymetrix - 12.05.2015, 20:57
Re: y_iterate (foreach 3.0) - by Crayder - 17.07.2015, 10:28
Re: y_iterate (foreach 3.0) - by IstuntmanI - 21.08.2015, 16:44
Re: y_iterate (foreach 3.0) - by Crayder - 21.08.2015, 20:23
Re: y_iterate (foreach 3.0) - by vannesenn - 04.06.2016, 12:54
Re: y_iterate (foreach 3.0) - by Konstantinos - 04.06.2016, 13:02
Re: y_iterate (foreach 3.0) - by vannesenn - 04.06.2016, 13:18
Re: y_iterate (foreach 3.0) - by Crayder - 04.06.2016, 17:37
Re: y_iterate (foreach 3.0) - by vannesenn - 04.06.2016, 19:15
Re: y_iterate (foreach 3.0) - by Crayder - 04.06.2016, 20:02
Re: y_iterate (foreach 3.0) - by vannesenn - 04.06.2016, 21:18
Re: y_iterate (foreach 3.0) - by Crayder - 04.06.2016, 21:48
Re: y_iterate (foreach 3.0) - by PrivateUserName - 08.07.2016, 09:52
Re: y_iterate (foreach 3.0) - by NewFreeroamStunt - 07.09.2017, 10:18
Re: y_iterate (foreach 3.0) - by raydx - 07.09.2017, 10:31
Re: y_iterate (foreach 3.0) - by Misiur - 07.09.2017, 10:44
Re: y_iterate (foreach 3.0) - by wallee - 19.09.2017, 21:17

Forum Jump:


Users browsing this thread: 3 Guest(s)