[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
#2

Quote:
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.

Can you tell me the substitute function please as it is giving me "using_deprecated_foreach_syntax" error
Reply
#3

Quote:
Originally Posted by hamzajaved780
Посмотреть сообщение
Can you tell me the substitute function please as it is giving me "using_deprecated_foreach_syntax" error
It's written in the thread:
pawn Код:
foreach(new i : Player)
Reply
#4

How to create Single iterator?
Reply
#5

Quote:
Originally Posted by RaeF
Посмотреть сообщение
How to create Single iterator?
pawn Код:
new
    Iterator:gIterator<MAX_SLOTS>;
pawn Код:
Iter_Add(gIterator, value);
pawn Код:
Iter_Remove(gIterator, value);
pawn Код:
foreach (new i : gIterator)
{
    // ...
}
Reply
#6

The main reason why i ask it because i got this error : warning 203: symbol is never used: "Iter_Single@Player_GamePlay"

edit:
@Player_GamePlay is my iterator

Iterator:Player_GamePlay<MAX_PLAYERS>
Reply
#7

Quote:
Originally Posted by RaeF
Посмотреть сообщение
The main reason why i ask it because i got this error : warning 203: symbol is never used: "Iter_Single@Player_GamePlay"

edit:
@Player_GamePlay is my iterator

Iterator:Player_GamePlay<MAX_PLAYERS>
It's like any other variable - declaring it only without using it will give you the warning.
Use it and you'll get rid of it.
Reply
#8

Is there a standalone version of foreach/y_iterate thats up to date?

Edit: I found what i was looking for:
Quote:
Originally Posted by arlindi
Посмотреть сообщение
but the last update was 05/01/12. Is this the most up to date version without using YSI?
Reply
#9

@Psymetrix: That's the latest version of a standalone version (not updated to Misiur's updates though): https://sampforum.blast.hk/showthread.php?tid=570868
Reply
#10

Thanks you, Konstantinos.
Reply
#11

I'm unable to test right now (on my tablet, out for the weekend), but I have a question. Will the following work?
pawn Code:
#define MAX_OBJECT_GROUPS 5

new Iterator:ObjectGroup[MAX_PLAYERS]<MAX_OBJECT_GROUPS, MAX_OBJECTS>;

public OnGameModeInit()
{
    Iter_Init(ObjectGroup); // Because it is an array of iterators still.
    return 1;
}

public OnPlayerConnect(playerid)
{
    Iter_Add(ObjectGroup[playerid]<1>, 5); //Adds object 5 to players 2nd group of objects.
    return 1;
}
This would allow each player to have his own groups of objects. Each group of one player could not contain the same as another group of the same player. Other players would be allowed to have the same object in one of their groups though, and that's perfectly fine for what I need this for.

Answered by Y-Less, issue 35 on github.
Reply
#12

What about adding "Iter_RandomFree" ? It would be a combination of Iter_Random (which returns randomly a used value) and Iter_Free (which returns the first non-used value), it should return a random free value.

Maybe even Iter_CountFree ? A combination of Iter_Count and Iter_Free, which would count the free slots. If it could be in a better way than this:
pawn Code:
printf( "Free: %d", ITERATOR_MAX_SIZE - Iter_Count( ITERATOR_NAME ) );
Reply
#13

Quote:
Originally Posted by IstuntmanI
View Post
What about adding "Iter_RandomFree" ? It would be a combination of Iter_Random (which returns randomly a used value) and Iter_Free (which returns the first non-used value), it should return a random free value.

Maybe even Iter_CountFree ? A combination of Iter_Count and Iter_Free, which would count the free slots. If it could be in a better way than this:
pawn Code:
printf( "Free: %d", ITERATOR_MAX_SIZE - Iter_Count( ITERATOR_NAME ) );
Yes! XD

I had this idea not to long ago, but forgot to suggest it. Then when I went to suggest it I forgot what my idea was! Thanks for refreshing my memory!
Reply
#14

Quote:
Originally Posted by Emmet_
Посмотреть сообщение
pawn Код:
new
    Iterator:gIterator<MAX_SLOTS>;
pawn Код:
Iter_Add(gIterator, value);
pawn Код:
Iter_Remove(gIterator, value);
pawn Код:
foreach (new i : gIterator)
{
    // ...
}
I don't understand how to remove slot with value? What if I have two slots with same value?
Reply
#15

Quote:
Originally Posted by vannesenn
Посмотреть сообщение
I don't understand how to remove slot with value? What if I have two slots with same value?
That's not possible because if a value already exists to the iterator, it is ignored.
You just pass the iterator and the value you want to remove to Iter_Remove function.
Reply
#16

So, only one value can exist in iterator? What's better, Iterator or ArrayList(from INO)? I want put IDs of vehs, houses, bizzes in arrays and replace normal loop with for
Reply
#17

Quote:
Originally Posted by vannesenn
View Post
So, only one value can exist in iterator? What's better, Iterator or ArrayList(from INO)? I want put IDs of vehs, houses, bizzes in arrays and replace normal loop with for
For incremental ID's, like players and vehicles, I'd say it depends on which one you want to use.

I haven't got to test the speed differences between the two yet.
Reply
#18

Please do it :P
Reply
#19

Quote:
Originally Posted by vannesenn
View Post
Please do it :P
Timing "AL - Add"...
Mean = 351.00ns
Timing "AL - Get"...
Mean = 2705.00ns
Timing "YI - Add"...
Mean = 353.00ns
Timing "YI - Get"...
Mean = 315.00ns

y_iterate is faster...

However, bare in mind that the ArrayList include works completely different.
y_iterate can be thought of as an array of on or off values.
ArrayList is a list of integers.

For most cases all you need is on or off values, which y_iterate is great at since it sorts the cases. ArrayList is just an intricate way of handling arrays, a better way.

So as I said before, it's really just your choice for incremental ID's.
Reply
#20

I can't find any tutorial how to include/add/put vehicle ids into iterate.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)