Thread overview
lockstep alternative for StoppingPolicy.longest
Sep 17, 2012
Andrej Mitrovic
Sep 17, 2012
Ali Çehreli
Sep 17, 2012
Andrej Mitrovic
Sep 17, 2012
Ali Çehreli
Sep 17, 2012
Andrej Mitrovic
Sep 17, 2012
Andrej Mitrovic
Sep 17, 2012
Andrej Mitrovic
Sep 17, 2012
Andrej Mitrovic
September 17, 2012
I need to iterate through two arrays and do some special comparisons, but the arrays are not guaranteed to have the same length. lockstep doesn't work with the "longest" policy, e.g.:

int[] a = [1, 2];
int[] b = [1, 2, 3];

foreach (aa, bb; lockstep(a, b, StoppingPolicy.longest))  // throws
{
}

What I would like to have is the ability to set a sentinel value when .empty returns true for one of the arrays, this would enable this policy to work. For example I could have:

foreach (aa, bb; lockstep(a, b, StoppingPolicy.longest, -1))  // sentinel is -1
{
    // if aa or bb doesn't exist it's set to -1
}

or alternatively aa/bb could be pointers, and the sentinel would conveniently be null. Is anything like this in Phobos already?
September 17, 2012
On 09/17/2012 03:00 PM, Andrej Mitrovic wrote:
> I need to iterate through two arrays and do some special comparisons,
> but the arrays are not guaranteed to have the same length. lockstep
> doesn't work with the "longest" policy, e.g.:
>
> int[] a = [1, 2];
> int[] b = [1, 2, 3];
>
> foreach (aa, bb; lockstep(a, b, StoppingPolicy.longest))  // throws
> {
> }
>
> What I would like to have is the ability to set a sentinel value when
> .empty returns true for one of the arrays, this would enable this
> policy to work. For example I could have:
>
> foreach (aa, bb; lockstep(a, b, StoppingPolicy.longest, -1))  // sentinel is -1
> {
>      // if aa or bb doesn't exist it's set to -1
> }
>
> or alternatively aa/bb could be pointers, and the sentinel would
> conveniently be null. Is anything like this in Phobos already?

I think you actually want .shortest, no? lockstep's cousin zip() and until() may help:

import std.stdio;
import std.range;
import std.algorithm;

void main()
{
    int i, j, k, l, m;
    int*[] a = [&i, null, &j];
    int*[] b = [&k, &l, &m];

    foreach (aa, bb;
             zip(StoppingPolicy.shortest, until!(x => x is null)(a), b))
    {
        writefln("%s and %s", aa, bb);
    }
}

Ali
September 17, 2012
On 9/18/12, Ali Çehreli <acehreli@yahoo.com> wrote:
> I think you actually want .shortest, no?

No I want to continue iterating as long as one of the ranges is still not empty. I'm not doing just comparisons, once there's only one range that's not empty I have to do some special checks on its elements. In simple terms:

void main()
{
    enum sentinel = -1;

    // imagine 3rd element missing and lockstep replaces it with -1
    int[] arr1 = [2, 4, -1];
    int[] arr2 = [2, 4, 3];

    bool state;
    foreach (aa, bb; lockstep(arr1, arr2))
    {
        if (aa == sentinel)
        {
            if (aa % 2 == 0)
            {
                state = true;
                break;
            }
        }
        else
        if (bb == sentinel)
        {
            if (bb % 2 == 0)
            {
                state = true;
                break;
            }
        }
        else
        {
            if (aa == bb)
            {
                state = true;
                break;
            }
        }

    }
}

That's not the algorithm I'm using and I'm not dealing with integers but that's just an example. If either range is empty I want to continue doing some special work on the range that isn't empty, otherwise I have to do work on both.
September 17, 2012
On 9/18/12, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:
>     foreach (aa, bb; lockstep(arr1, arr2))
>     {
>         if (aa == sentinel)
>         {
>             if (aa % 2 == 0)

Gah I've messed up the simple example. If aa was a sentinel then it meant I wouldn't check it at all, I'd try to check 'bb' instead.
September 17, 2012
On 09/17/2012 03:51 PM, Andrej Mitrovic wrote:
> On 9/18/12, Ali Çehreli<acehreli@yahoo.com>  wrote:
>> I think you actually want .shortest, no?
>
> No I want to continue iterating as long as one of the ranges is still
> not empty. I'm not doing just comparisons, once there's only one range
> that's not empty I have to do some special checks on its elements. In
> simple terms:
>
> void main()
> {
>      enum sentinel = -1;
>
>      // imagine 3rd element missing and lockstep replaces it with -1
>      int[] arr1 = [2, 4, -1];
>      int[] arr2 = [2, 4, 3];
>
>      bool state;
>      foreach (aa, bb; lockstep(arr1, arr2))
>      {
>          if (aa == sentinel)
>          {
>              if (aa % 2 == 0)
>              {
>                  state = true;
>                  break;
>              }
>          }
>          else
>          if (bb == sentinel)
>          {
>              if (bb % 2 == 0)
>              {
>                  state = true;
>                  break;
>              }
>          }
>          else
>          {
>              if (aa == bb)
>              {
>                  state = true;
>                  break;
>              }
>          }
>
>      }
> }
>
> That's not the algorithm I'm using and I'm not dealing with integers
> but that's just an example. If either range is empty I want to
> continue doing some special work on the range that isn't empty,
> otherwise I have to do work on both.

Then .longest with zip seems to be the way to go. zip() produces the .init value for the short range. Luckily, you are not dealing with int.init so perhaps your type's sentinel is distinguishable:

    foreach (aa, bb; zip(StoppingPolicy.longest, a, b))
    {
        if (aa == aa.init) {
        }
        // etc.

        writefln("%s and %s", aa, bb);
    }

Ali
September 17, 2012
On 9/18/12, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:
> On 9/18/12, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:
>>     foreach (aa, bb; lockstep(arr1, arr2))
>>     {
>>         if (aa == sentinel)
>>         {
>>             if (aa % 2 == 0)
>
> Gah I've messed up the simple example. If aa was a sentinel then it meant I wouldn't check it at all, I'd try to check 'bb' instead.
>

Here we go: http://dpaste.dzfl.pl/dff850fb

Hardcoded but could be made to work with multiple ranges (+ i have way too many runtime checks there). For floating-point this might even work since a sentinel could be NaN, but for ints there's no reasonable sentinel. We could use pointers instead and use NULL when the range is empty.
September 17, 2012
On 9/18/12, Ali Çehreli <acehreli@yahoo.com> wrote:
> Then .longest with zip seems to be the way to go.

Ah ain't that cool. It looks like it works. What does it use for the sentinel, Type.init perhaps?
September 17, 2012
On 9/18/12, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:
> On 9/18/12, Ali Çehreli <acehreli@yahoo.com> wrote:
>> Then .longest with zip seems to be the way to go.
>
> Ah ain't that cool. It looks like it works. What does it use for the sentinel, Type.init perhaps?
>

Yep just tried with floats and returns NaN. Thanks again, Ali! :)