SA-MP Forums

Go Back   SA-MP Forums > SA-MP Scripting and Plugins > Scripting Help > Tutorials

Reply
 
Thread Tools Display Modes
Old 12/04/2018, 12:23 PM   #1
Y_Less
Spam Machine
 
Y_Less's Avatar
 
Join Date: Jun 2008
Location: 629 - git.io/Y
Posts: 14,723
Reputation: 3073
Default yield

Introduction


yield is a new keyword that basically means “return some data, but later come back to EXACTLY this spot in the code”. This is appearing in more and more languages, and now PAWN has it as well. As a very brief example, this is invalid code. The compiler will give a warning, since the second print can never be run:

Code:

Func()
{
    print("a");
    return 42;
    print("b");
}
On the other hand, this code is valid, because after doing the return, code execution actually comes back in to the middle of the function:

Code:

Func()
{
    print("a");
    yield return 42;
    print("b");
}
Iterators

Currently the only place this actually works is in special iterators. If you are not familiar with basic special iterators, read this: http://forum.sa-mp.com/showthread.php?t=570937

A regular special iterator may look like this:

Code:

iterfunc stock Powers(&iterstate, cur, base)
{
    if (cur)
    {
        return
            iterstate = base * cur,
            _:(iterstate > cur) * iterstate;
    }
    return 1;
}

#define [email protected] iterstate(0, 0)
And called with:

Code:

foreach (new i : Powers(2))
{
    // 1, 2, 4, 8, 16, ...
}
foreach calls that function once every loop, each time passing in the current value of i and any other required state for the function. This gets a bit confusing, so we can replace this all with yield, and keep all the iterator state purely within the function:

Code:

iterfunc stock Powers(base)
{
    for (new cur = 1; cur > 0; cur *= base)
        yield return cur;
}

#define [email protected] iteryield
A few things to notice:
  1. We declare a local variable (cur) and keep using it, despite repeatedly leaving the function. This variable lives within the function's closure. You could call this function twice in nested loops with no problems.
  2. There is no return at the end of the function. You could have one, and that value would be part of the foreach loop, but one isn't required (this would be a compiler warning with regular returns). If the function ends without a yield, the loop instantly ends.
  3. Because loop ends are determined purely by the iterator function ending, not by a special value as in other iterators, yield iterators can use EVERY number.
  4. You can end a yield function early by using return alone as normal, or by using yield break;.
  5. The define has changed to iter - this just tells foreach which type of code to use for this iterator.
  6. This specific loop ends when the number overflows, but that's not special to this code.

Example

The example for using this code is identical to the old version:

Code:

foreach (new i : Powers(2))
{
    // 1, 2, 4, 8, 16, ...
}
So for each iteration of that loop, just a little bit of the iterator function is run.

Every Value

Standard special iterator functions end when they return a certain value - by default this is cellmin, but it can be changed. However, this means that there is always at least one value that can’t be part of the loop ever. Maybe that’s not a problem, but yield iterators don’t have that problem:

Code:

iterfunc stock Number()
{
    new x = cellmin;
    do
    {
        yield return x;
        ++x;
    }
    while (x != cellmin);
}

#define [email protected] iteryield

foreach (new i : Number())
{
    printf("%!d(MISSING)", i);
}
That code will print every valid 32-bit number (so I suggest you don’t actually run it).

Variants

yield iterators can contain any code, as with normal function, and can “yield” as many things as desired.

Looping over this will get every number three times in a row:

Code:

iterfunc stock NumberThrice()
{
    new x = cellmin;
    do
    {
        yield return x;
        yield return x;
        yield return x;
        ++x;
    }
    while (x != cellmin);
}

#define [email protected] iteryield
A Range function (y_iterate actually already has one of these, not written like this as it is an old-style standard special iterator, but this is a good example anyway):

Code:

iterfunc stock Range(start, end, step = 1)
{
    if (step < 0)
    {
        while (end < start)
        {
            yield return start;
            start += step;
        }
    }
    else
    {
        while (start < end)
        {
            yield return start;
            start += step;
        }
    }
}

#define [email protected] iteryield
Return the numbers from 0-20. This example is not at all the easiest way of doing this, but shows a number of other techniques:

Code:

iterfunc stock XToY(x, y)
{
    while (x != y)
        yield return x++;
}

#define [email protected] iteryield

iterfunc stock ZeroToTwenty()
{
    yield return 0;
    foreach (new i : XToY(1, 10))
        yield return i;
    yield return 10;
    yield return 11;
    yield return 12;
    yield return 13;
    for (new i = 0; i != 3; ++i)
        yield return i + 14;
    yield return 17;
    for (new i = 0; i != 100; ++i)
    {
        if (i == 3)
            yield break;
        else
            yield return i + 18;
    }
    yield return 101;
}

#define [email protected] iteryield
Notes:

  1. Because of yield break;, the higher numbers will never be passed to foreach.
  2. This has a call to another yield iterator. Any valid code can be used in these functions (even recursion).
  3. Some returns are in loops, some aren't. It doesn't matter.
  4. The inner yield function uses post-increment on the yield return line. This will also work correctly and pass the old value to the loop.

Last edited by Y_Less; 22/05/2018 at 11:42 AM.
Y_Less is offline   Reply With Quote
Old 05/05/2018, 07:33 AM   #2
Evocator
Huge Clucker
 
Join Date: Nov 2013
Posts: 481
Reputation: 153
Default Re: yield

How did I even miss this. This is perfect. Thank you.
Evocator is offline   Reply With Quote
Old 10/05/2018, 12:52 PM   #3
FireCat
High-roller
 
FireCat's Avatar
 
Join Date: Jul 2010
Posts: 2,404
Reputation: 596
Default Re: yield

Very handy to have someone explain the concept of yield.

It doesn't just apply in that situation, but also other examples like breaking down complex UX designs to build easy-to-read and maintainable code.

Very good explanation IMHO for newbies!
Congratulations.
FireCat is offline   Reply With Quote
Old 20/05/2018, 12:08 PM   #4
eXtr1kE
Big Clucker
 
Join Date: Dec 2009
Posts: 114
Reputation: 0
Default Re: yield

I really like how you use ASM to improve PAWN as language. Keep goin'
__________________
Contact : [email protected] (ONLINE OF MSN)
eXtr1kE is offline   Reply With Quote
Old 22/05/2018, 04:55 AM   #5
Crystallize
High-roller
 
Join Date: Aug 2013
Posts: 1,573
Reputation: 308
Default Re: yield

I don’t get the idea how can this be useful in samp? Or it’s just for pawn general coding?
Eitherway thanks.
__________________
SAMP BATTLE GROUNDS
87.98.241.207:7780
Crystallize is offline   Reply With Quote
Old 22/05/2018, 11:27 AM   #6
Y_Less
Spam Machine
 
Y_Less's Avatar
 
Join Date: Jun 2008
Location: 629 - git.io/Y
Posts: 14,723
Reputation: 3073
Default Re: yield

It's an extension to pawn, you can use it however you want to write modes. In theory you can use nothing but callbacks and "if" to write a mode, it just isn't easy.
Y_Less is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
What kind of server/gamemode would yield most success if it was started today? Markus1337 Server Support 3 16/01/2015 07:29 AM


All times are GMT. The time now is 04:37 AM.


Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.