July 22, 2013
> how about s[i - 1..n]?
> 
> You going to go throw some ifs around the statement that uses that? Use a ternary if? So I'm forced to use a longer more verbose method, and also introduce bugs, because the most obvious, simplest, and logical solution, s[max(0, i-1)..n] won't work.

-1 as index is illegal anyways, so this code will fail even with signed indices
July 22, 2013
On Mon, 22 Jul 2013 04:47:34 +0100, JS <js.mdnq@gmail.com> wrote:

> Doing simple stuff like
>
> for(int i = 0; i < s.length - 1; i++) fails catastrophically if s is empty. To make right one has to reduce performance by writing extra checks.
>
> There seems to be no real good reason why size_t is unsigned... Surely one doesn't require too many strings larger than 2^63 bits on an x64 os...
>
> I running into a lot of trouble because of the way D deals with implicit casting of between signed and unsigned.
>
> please don't tell me to use foreach... isn't not a panacea.

I have always found the whole size is an unsigned int thing annoying too.  In C/C++ I simply cast strlen() and co to 'int' because in all but very specific and well known cases this is entirely sufficient, and it avoids the underflow issue entirely.

If we were to design the perfect type for representing a size or length it would hold the maximum value of an unsigned int, but would not undeflow to max unsigned int, instead it would truncate.

This type would have to be built on top of the existing primitives and would therefore be less performant, which is a shame and likely the reason it doesn't already exist.

R

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
July 22, 2013
On Monday, 22 July 2013 at 11:56:35 UTC, Regan Heath wrote:
> If we were to design the perfect type for representing a size or length it would hold the maximum value of an unsigned int, but would not undeflow to max unsigned int, instead it would truncate.
>
> This type would have to be built on top of the existing primitives and would therefore be less performant, which is a shame and likely the reason it doesn't already exist.
>
> R

ARM directly supports saturating arithmetic, so this approach could be both practical and fast. However, x86 only supports it in SIMD, so it's not ideal.
July 22, 2013
On 7/22/13, JS <js.mdnq@gmail.com> wrote:
> foreach doesn't allow you to modify the index to skip over elements.

It does:

-----
import std.stdio;

void main()
{
    int[] x = [1, 2, 3, 4, 5];
    foreach (ref i; 0 .. 5)
    {
        writeln(x[i]);
        ++i;
    }
}
-----

Writes:
1
3
5
July 22, 2013
On Monday, 22 July 2013 at 12:51:31 UTC, Andrej Mitrovic wrote:
> On 7/22/13, JS <js.mdnq@gmail.com> wrote:
>> foreach doesn't allow you to modify the index to skip over
>> elements.
>
> It does:
>
> -----
> import std.stdio;
>
> void main()
> {
>     int[] x = [1, 2, 3, 4, 5];
>     foreach (ref i; 0 .. 5)
>     {
>         writeln(x[i]);
>         ++i;
>     }
> }
> -----
>
> Writes:
> 1
> 3
> 5

Cool... This should make life easier! Thanks.
July 22, 2013
On Monday, 22 July 2013 at 12:51:31 UTC, Andrej Mitrovic wrote:
> On 7/22/13, JS <js.mdnq@gmail.com> wrote:
>> foreach doesn't allow you to modify the index to skip over
>> elements.
>
> It does:
>
> -----
> import std.stdio;
>
> void main()
> {
>     int[] x = [1, 2, 3, 4, 5];
>     foreach (ref i; 0 .. 5)
>     {
>         writeln(x[i]);
>         ++i;
>     }
> }
> -----
>
> Writes:
> 1
> 3
> 5

99% sure that's unspecified behavior. I wouldn't rely on anything like that.
July 22, 2013
On 07/22/2013 08:04 AM, monarch_dodra wrote:

> On Monday, 22 July 2013 at 12:51:31 UTC, Andrej Mitrovic wrote:
>> On 7/22/13, JS <js.mdnq@gmail.com> wrote:
>>> foreach doesn't allow you to modify the index to skip over
>>> elements.
>>
>> It does:
>>
>> -----
>> import std.stdio;
>>
>> void main()
>> {
>>     int[] x = [1, 2, 3, 4, 5];
>>     foreach (ref i; 0 .. 5)
>>     {
>>         writeln(x[i]);
>>         ++i;
>>     }
>> }
>> -----
>>
>> Writes:
>> 1
>> 3
>> 5
>
> 99% sure that's unspecified behavior. I wouldn't rely on anything like
> that.

Two more solutions one with foreach one without any explicit looping:

import std.stdio;
import std.range;

void main()
{
    int[] x = [1, 2, 3, 4, 5];

    foreach (i; iota(0, 5, 2))
    {
        writeln(x[i]);
        ++i;
    }

    writeln(x.indexed(iota(0, x.length, 2)));
}

Ali

July 22, 2013
On Monday, 22 July 2013 at 15:04:25 UTC, monarch_dodra wrote:
> On Monday, 22 July 2013 at 12:51:31 UTC, Andrej Mitrovic wrote:
>> On 7/22/13, JS <js.mdnq@gmail.com> wrote:
>>> foreach doesn't allow you to modify the index to skip over
>>> elements.
>>
>> It does:
>>
>> -----
>> import std.stdio;
>>
>> void main()
>> {
>>    int[] x = [1, 2, 3, 4, 5];
>>    foreach (ref i; 0 .. 5)
>>    {
>>        writeln(x[i]);
>>        ++i;
>>    }
>> }
>> -----
>>
>> Writes:
>> 1
>> 3
>> 5
>
> 99% sure that's unspecified behavior. I wouldn't rely on anything like that.

Of course it is specified behavior.

ForeachStatement:
    Foreach (ForeachTypeList ; Aggregate) NoScopeNonEmptyStatement

Foreach:
    foreach
    foreach_reverse

ForeachTypeList:
    ForeachType
    ForeachType , ForeachTypeList

ForeachType:
    refopt BasicType Declarator
    refopt Identifier

Aggregate:
    Expression

This is an example of unspecified behavior:

import std.stdio;

void main()
{
    int[] x = [1, 2, 3, 4, 5];
    foreach (ref i; 0 .. 5)
    {
        __limit1631--;
        writeln(x[i]);
    }
}
July 22, 2013
On Monday, 22 July 2013 at 15:39:11 UTC, Maxim Fomin wrote:
> On Monday, 22 July 2013 at 15:04:25 UTC, monarch_dodra wrote:
>> On Monday, 22 July 2013 at 12:51:31 UTC, Andrej Mitrovic wrote:
>>> On 7/22/13, JS <js.mdnq@gmail.com> wrote:
>>>> foreach doesn't allow you to modify the index to skip over
>>>> elements.
>>>
>>> It does:
>>>
>>> -----
>>> import std.stdio;
>>>
>>> void main()
>>> {
>>>   int[] x = [1, 2, 3, 4, 5];
>>>   foreach (ref i; 0 .. 5)
>>>   {
>>>       writeln(x[i]);
>>>       ++i;
>>>   }
>>> }
>>> -----
>>>
>>> Writes:
>>> 1
>>> 3
>>> 5
>>
>> 99% sure that's unspecified behavior. I wouldn't rely on anything like that.
>
> Of course it is specified behavior.
>
> ForeachStatement:
>     Foreach (ForeachTypeList ; Aggregate) NoScopeNonEmptyStatement
>
> Foreach:
>     foreach
>     foreach_reverse
>
> ForeachTypeList:
>     ForeachType
>     ForeachType , ForeachTypeList
>
> ForeachType:
>     refopt BasicType Declarator
>     refopt Identifier
>
> Aggregate:
>     Expression

So... you are saying that if the grammar allows it, then the behavior is specified?

All I see, is you iterating over references to the elements of an aggregate. The final behavior really depends on how said aggregate is implemented. If anything, if the behavior *was* defined, then I'd simply argue the behavior is wrong: I don't see why changing the values of the elements of the aggregate should change the amount of elements you iterate on at all. Also:

//----
    int[] x = [1, 2, 3, 4, 5];
    foreach (ref i; iota(0, 5))
    {
        writeln(x[i]);
        ++i;
    }
//----

This also compiles, but I used a different aggregate, yet represents the same thing. Because it is implemented differently, I get a completely different result. Unless I'm mistaken, when a result depends on the implementation, and the implementation doesn't state what the result is, then that's what unspecified behavior is. (unspecified, not undefined).

> This is an example of unspecified behavior:
>
> import std.stdio;
>
> void main()
> {
>     int[] x = [1, 2, 3, 4, 5];
>     foreach (ref i; 0 .. 5)
>     {
>         __limit1631--;
>         writeln(x[i]);
>     }
> }

What is "__limit1631" ? Doesn't compile for me.
July 22, 2013
On Monday, 22 July 2013 at 15:51:45 UTC, monarch_dodra wrote:
> So... you are saying that if the grammar allows it, then the behavior is specified?
>

You may argue that although grammar does allows it, the feature is semantically not defined. However here it is known what "ref int i" means, to be more precise what you can do with objects marked with ref attribute.

> All I see, is you iterating over references to the elements of an aggregate. The final behavior really depends on how said aggregate is implemented. If anything, if the behavior *was* defined, then I'd simply argue the behavior is wrong: I don't see why changing the values of the elements of the aggregate should change the amount of elements you iterate on at all. Also:
>
> //----
>     int[] x = [1, 2, 3, 4, 5];
>     foreach (ref i; iota(0, 5))
>     {
>         writeln(x[i]);
>         ++i;
>     }
> //----
>
> This also compiles, but I used a different aggregate, yet represents the same thing. Because it is implemented differently, I get a completely different result. Unless I'm mistaken, when a result depends on the implementation, and the implementation doesn't state what the result is, then that's what unspecified behavior is. (unspecified, not undefined).

This is different because in 0..5 ref int maps directly to variable modified, but in iota() it maps to value returned by .front property function and since it doesn't return by ref, refness is wiped out. Behavior is defined in both cases.

>> This is an example of unspecified behavior:
>>
>> <...>
>
> What is "__limit1631" ? Doesn't compile for me.

This one may http://dpaste.dzfl.pl/3faf27ba

extern(C) int printf (const char*, ...);

void main()
{
    int[] x = [1, 2, 3, 4, 5];
    foreach (ref i; 0 .. 5)
    {
        __limit6--; // or 5  depending on dmd version
        printf("%d\n", x[i]);
    }
}

1
2
3

This is example of unspecified behavior (better undefined) due to playing with __identifiers and how dmd bug can make D code looks strange.