www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Foreach with byte problems

reply Andrej Mitrovic <none none.none> writes:
So I'd like to print all values storable in a byte in hex representation:

import std.stdio;
void main()
{
    int counter;
    foreach (byte index; byte.min..byte.max)
    {
        if (!(counter % 4))
            writeln();
        
        writef("%#.2x, ", index);
        counter++;
    }
}

If you run this, you'll realize that it doesn't print the final 0x7F. This is
because in a foreach range literal (is that the correct term?), the left side
is inclusive, but the right side isn't. 

Hence this will run the foreach from 0 to 9:
foreach (index; 0..10) { }

So I figured I'd just add a +1 at the end, but then I get an error:
    foreach (byte index; byte.min..byte.max+1)
    {
    }

Error: cannot implicitly convert expression (128) of type int to
byte. 

Of course 128 can't fit in a byte. But how am I supposed to print out the last
value if the right hand side of a range literal is non-inclusive?

This behavior is kind of odd, don't you think?

byte.max is 127, but due to the way foreach works, the last value assigned to
index is 126. If I was allowed to add +1 for RHS, the last value stored to
index would be 127 due to the non-inclusive right side, which is completely
legal. Yet DMD complains that I'm trying to store 128 to index.

I guess DMD first checks if the value on the RHS of the range literal can fit
to a byte before it cuts one off due to the way range literals work. So how do
I work around this?
Feb 25 2011
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 25 Feb 2011 13:52:53 -0500, Andrej Mitrovic <none none.none> wrote:

 So I'd like to print all values storable in a byte in hex representation:

 import std.stdio;
 void main()
 {
     int counter;
     foreach (byte index; byte.min..byte.max)
     {
         if (!(counter % 4))
             writeln();
        writef("%#.2x, ", index);
         counter++;
     }
 }

 If you run this, you'll realize that it doesn't print the final 0x7F.  
 This is because in a foreach range literal (is that the correct term?),  
 the left side is inclusive, but the right side isn't.

 Hence this will run the foreach from 0 to 9:
 foreach (index; 0..10) { }

 So I figured I'd just add a +1 at the end, but then I get an error:
     foreach (byte index; byte.min..byte.max+1)
     {
     }

 Error: cannot implicitly convert expression (128) of type int to
 byte.

 Of course 128 can't fit in a byte. But how am I supposed to print out  
 the last value if the right hand side of a range literal is  
 non-inclusive?

foreach(int index; byte.min..byte.max + 1) The truth is, you don't want to use byte to represent your comparisons, because byte.max + 1 isn't a valid byte. And instead of counter, you can use a formula instead: if((index - byte.min) % 4 == 0) writeln(); -Steve
Feb 25 2011
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Can this be simplified?:

byte[] arr = to!(byte[])(array(iota(byte.min, byte.max+1)));

The +1 turns byte.max into an int that can store 128. So when I call
array on an iota, I'll get back an int[] of [-128, ..., 127]. And I
have to convert that to a byte[].
Feb 25 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 25 Feb 2011 14:15:52 -0500, Andrej Mitrovic  
<andrej.mitrovich gmail.com> wrote:

 Maybe it's best to let D do type infering for me. This works ok:

     foreach (index; byte.min..byte.max+1)
     {
         if((index - byte.min) % 4 == 0)
             writeln();

         writef("%#.2x, ", index);
     }

 I'm fine with that.

 Now, what's wrong with this code:

     auto foo = iota(byte.min, byte.max-1);  // ok
     // foo = [0, 1, .., 124, 125]

     auto bar = iota(byte.min, byte.max);  // fails
     // Errors:
     // D:\DMD\dmd2\windows\bin\..\..\src\phobos\std\range.d(3868):
 Error: cannot implicitly convert expression (cast(int)pastLast - 1) of
 type int to byte
     // D:\DMD\dmd2\windows\bin\..\..\src\phobos\std\range.d(3873):
 Error: cannot implicitly convert expression (cast(int)pastLast + 1) of
 type int to byte
     // D:\DMD\dmd2\windows\bin\..\..\src\phobos\std\range.d(3890):
 Error: cannot implicitly convert expression
 (cast(uint)cast(int)this.pastLast - this.step) of type uint to byte

IFTI is interpreting the element type of iota to byte. You need to either explicitly instantiate iota (don't recommend this) or cast one of your args to int. auto bar = iota(cast(int)byte.min, byte.max); a dirty trick you could do is add 0, which should promote the arg to int. -Steve
Feb 25 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 25 Feb 2011 14:34:35 -0500, Andrej Mitrovic  
<andrej.mitrovich gmail.com> wrote:

 Can this be simplified?:

 byte[] arr = to!(byte[])(array(iota(byte.min, byte.max+1)));

 The +1 turns byte.max into an int that can store 128. So when I call
 array on an iota, I'll get back an int[] of [-128, ..., 127]. And I
 have to convert that to a byte[].

This is a good puzzle. You may have to do something with map and casting, but I don't see how it can be done with iota without a cast. The right thing to do would be this: for(byte i = -127; i < byte.max + 1; i++) arr ~= i; which does everything as a byte, but does the comparisons as ints. But I don't know how to make iota do this. -Steve
Feb 25 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 2/25/11, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 a dirty trick you could do is add 0, which should promote the arg to int.

Cool. I've almost used +1-1, LOL! Well, I thought DMD would simply ignore +0 (dead code elimination?), but apparently this is a cool shorthand for casting literals? Nice.
Feb 25 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 25 Feb 2011 14:51:18 -0500, Andrej Mitrovic  
<andrej.mitrovich gmail.com> wrote:

 On 2/25/11, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 a dirty trick you could do is add 0, which should promote the arg to  
 int.

Cool. I've almost used +1-1, LOL! Well, I thought DMD would simply ignore +0 (dead code elimination?), but apparently this is a cool shorthand for casting literals? Nice.

No code is generated to add 0, but it does affect the type. -Steve
Feb 25 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Maybe it's best to let D do type infering for me. This works ok:

    foreach (index; byte.min..byte.max+1)
    {
        if((index - byte.min) % 4 == 0)
            writeln();

        writef("%#.2x, ", index);
    }

I'm fine with that.

Now, what's wrong with this code:

    auto foo = iota(byte.min, byte.max-1);  // ok
    // foo = [0, 1, .., 124, 125]

    auto bar = iota(byte.min, byte.max);  // fails
    // Errors:
    // D:\DMD\dmd2\windows\bin\..\..\src\phobos\std\range.d(3868):
Error: cannot implicitly convert expression (cast(int)pastLast - 1) of
type int to byte
    // D:\DMD\dmd2\windows\bin\..\..\src\phobos\std\range.d(3873):
Error: cannot implicitly convert expression (cast(int)pastLast + 1) of
type int to byte
    // D:\DMD\dmd2\windows\bin\..\..\src\phobos\std\range.d(3890):
Error: cannot implicitly convert expression
(cast(uint)cast(int)this.pastLast - this.step) of type uint to byte
Feb 25 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 02/25/2011 07:52 PM, Andrej Mitrovic wrote:
 So I'd like to print all values storable in a byte in hex representation:

 import std.stdio;
 void main()
 {
      int counter;
      foreach (byte index; byte.min..byte.max)
      {
          if (!(counter % 4))
              writeln();

          writef("%#.2x, ", index);
          counter++;
      }
 }

 If you run this, you'll realize that it doesn't print the final 0x7F. This is
because in a foreach range literal (is that the correct term?), the left side
is inclusive, but the right side isn't.

 Hence this will run the foreach from 0 to 9:
 foreach (index; 0..10) { }

 So I figured I'd just add a +1 at the end, but then I get an error:
      foreach (byte index; byte.min..byte.max+1)
      {
      }

 Error: cannot implicitly convert expression (128) of type int to
 byte.

 Of course 128 can't fit in a byte. But how am I supposed to print out the last
value if the right hand side of a range literal is non-inclusive?

 This behavior is kind of odd, don't you think?

 byte.max is 127, but due to the way foreach works, the last value assigned to
index is 126. If I was allowed to add +1 for RHS, the last value stored to
index would be 127 due to the non-inclusive right side, which is completely
legal. Yet DMD complains that I'm trying to store 128 to index.

 I guess DMD first checks if the value on the RHS of the range literal can fit
to a byte before it cuts one off due to the way range literals work. So how do
I work around this?

lol! One more source of fun in using half-open intervals :-) Denis -- _________________ vita es estrany spir.wikidot.com
Feb 25 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, February 25, 2011 11:37:23 spir wrote:
 On 02/25/2011 07:52 PM, Andrej Mitrovic wrote:
 So I'd like to print all values storable in a byte in hex representation:
 
 import std.stdio;
 void main()
 {
 
      int counter;
      foreach (byte index; byte.min..byte.max)
      {
      
          if (!(counter % 4))
          
              writeln();
          
          writef("%#.2x, ", index);
          counter++;
      
      }
 
 }
 
 If you run this, you'll realize that it doesn't print the final 0x7F.
 This is because in a foreach range literal (is that the correct term?),
 the left side is inclusive, but the right side isn't.
 
 Hence this will run the foreach from 0 to 9:
 foreach (index; 0..10) { }
 
 So I figured I'd just add a +1 at the end, but then I get an error:
      foreach (byte index; byte.min..byte.max+1)
      {
      }
 
 Error: cannot implicitly convert expression (128) of type int to
 byte.
 
 Of course 128 can't fit in a byte. But how am I supposed to print out the
 last value if the right hand side of a range literal is non-inclusive?
 
 This behavior is kind of odd, don't you think?
 
 byte.max is 127, but due to the way foreach works, the last value
 assigned to index is 126. If I was allowed to add +1 for RHS, the last
 value stored to index would be 127 due to the non-inclusive right side,
 which is completely legal. Yet DMD complains that I'm trying to store
 128 to index.
 
 I guess DMD first checks if the value on the RHS of the range literal can
 fit to a byte before it cuts one off due to the way range literals work.
 So how do I work around this?

lol! One more source of fun in using half-open intervals :-)

In the general case, having the first element of an interval be inclusive and the last one exclusive is perfect. That's how it works with iterators in C++. That's essentially how it works with array indices, since they start with 0 and their length is one greater than the last index. It's just plain nice and makes checking end conditions cleaner. However, it is true that in this particular case, it's annoying. Still, in the general case, I do believe that it's definitely the right behavior. - Jonathan M Davis
Feb 25 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 2/25/11, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 However, it is true that in this particular case, it's annoying. Still, in
 the
 general case, I do believe that it's definitely the right behavior.

 - Jonathan M Davis

I do agree it's the right thing to do, and I wouldn't want to change it. But I'd like the compiler to be smart in this case and see that there's no way 128 will ever be stored into a byte. It's not some crucial piece of code that I need working. I just ran into it by accident really.
Feb 25 2011
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 02/25/2011 10:52 AM, Andrej Mitrovic wrote:
 So I'd like to print all values storable in a byte in hex representation:

 import std.stdio;
 void main()
 {
      int counter;
      foreach (byte index; byte.min..byte.max)
      {
          if (!(counter % 4))
              writeln();

          writef("%#.2x, ", index);
          counter++;
      }
 }

 If you run this, you'll realize that it doesn't print the final 0x7F. This is
because in a foreach range literal (is that the correct term?), the left side
is inclusive, but the right side isn't.

I've seen the same "problem" with enums. import std.stdio; enum E { x, y, z } void main() { foreach (e; E.min .. E.max) { writeln(e); } } The value 2 is excluded. Of course it's rare to use enums like that in a foreach loop as their values are not always continuous. I found out the better solution before sending this message: foreach (e; __traits(allMembers, E)) { writeln(e); } The difference is, the type of 'e' is string above. Finally, the following produces integer values: foreach (e; __traits(allMembers, E)) { writeln(to!E(e)); } Ok, good... :) Ali
Feb 25 2011