January 03, 2008
On Thu, 03 Jan 2008 16:43:52 +0100, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> I'm not super excited about there being such a subtle difference with the
> colon (if you automatically put a colon without thinking about it, then the
> break statement is required, meaning a very subtle bug), but other than
> that, I like the idea.  Why not make it more obvious?:
>
> case (x, y, z) // similar to case x: case y: case z:
> {
> }
> /* else case(a, b, c) ? */
>
> -Steve

I want to add two things:

(1) When using () after case, the {} should be optional, like in an if:

case (1) foo();
case (2, 3)
{
    foo();
    bar();
}

(2) continue should be used for fall through behavior:

case (1) foo();
case (2, 3)
{
    if (x == 3) continue 1;
    bar();
}

We could even get rid of "default" and label the default case as "else" - saving a keyword :)

-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
January 03, 2008
Michiel Helvensteijn wrote:
> But the cases are labels. You fall through labels. To change this for
> switches would be inconsistent.

You're quite right.

> Actually, I don't like switch statement syntax at all, really. I don't think
> they should be labels. They should be { } blocks.

Well, if it's not labels anymore, the implicit break option couln't
break consistency, could it? Anyways, I don't want to go on arguing
for a change I'd rather not see implemented, except as a compromise
for those who don't like the implicit fallthrough... =)

regards, frank

January 04, 2008
Steven Schveighoffer Wrote:

> "Janice Caron" wrote
> > On 1/3/08, Dan wrote:
> >> It's creative, and syntactically coherent.  I like it.  Even if it breaks my old programs, this one makes sense.  : )
> >
> > I don't think it would break any old programs at all
> 
> I haven't done this in any d programs, but in C++ programs, I used to use braces to create a scope in which to declare a variable.  What would happen here:
> 
> case x:
> {
>     int y = 2;
>     ...
> }
> break;
> 
> I'm not super excited about there being such a subtle difference with the colon (if you automatically put a colon without thinking about it, then the break statement is required, meaning a very subtle bug), but other than that, I like the idea.  Why not make it more obvious?:
> 
> case (x, y, z) // similar to case x: case y: case z:
> {
> }
> /* else case(a, b, c) ? */
> 
> -Steve
> 
> 

I tend to agree with Steve.  Perhaps using case in the same manner as if/for/while/switch and the rest is more congruent to other program structures.  I also agree that falling through should be explicit.  I use it alot, but safe and more common behavior is to not do so.

I also like the comma notation; it would probably terse up the giant lexer switch a bit.

Regards,
Dan
January 04, 2008
Dan Lewis wrote:
> I also like the comma notation; it would probably terse up the giant lexer switch a bit.

case a, b, c:

is already valid D.

http://www.digitalmars.com/d/1.0/statement.html#SwitchStatement

-- 
E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
January 04, 2008
On Thu, 03 Jan 2008 10:30:09 -0000, davidl <davidl@126.com> wrote:

> http://pragmatic.oreilly.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html
>
> I think I love the trap no.9
>


i had a go at rolling my own, here it is if anyone's interested:

//public domain
import std.stdio;
import std.variant;
import std.traits;

struct SwitchFrame
{
   Variant     val;
   bool        active;
   bool        _break;
   static SwitchFrame opCall(Variant val, bool active, bool _break)
   {
      SwitchFrame result;
      result.val    = val;
      result.active = active;
      result._break = _break;
      return result;
   }
}

SwitchFrame[] switchStack;

void _continue()  //execute next case after this one
{
   switchStack[$ - 1].active = true;
   switchStack[$ - 1]._break = false;
}

void _rematch()   //execute the next case that matches
{
   switchStack[$ - 1].active = false;
   switchStack[$ - 1]._break = false;
}

void _break()     //don't execute any more cases
{
   switchStack[$ - 1].active = false;
   switchStack[$ - 1]._break = true;
}


struct _range(T)
{
   T min;
   T max;
}

_range!(T) range(T)(T min, T max)
{
   _range!(T) result;
   result.min = min;
   result.max = max;
   return result;
}

struct rangedg(T)
{
   T min;
   T max;
   bool func(T val)
   {
      return min <= val && val <= max;
   }
}

void myswitch(T, U)(T switchVal, U block)
{
   Variant v = switchVal;
   switchStack ~= SwitchFrame(v, false, false);
   block();
   switchStack.length = switchStack.length - 1;
}

void mycase(T...)(T args)
{
   SwitchFrame* frame = &switchStack[$ - 1];
   if (frame._break) return;
   if (!frame.active)
   {
      foreach (i, arg; args[0 .. $-1])
      {
         if (typeid(T[i]) == frame.val.type && arg == frame.val.get!(T[i])) goto success;

         static if (is (typeof(*arg) U == function))
         {
            //writefln("function");
            if (arg(frame.val.get!(U))) goto success;
         }
         static if (is (T[i] U == delegate))
         {
            //writefln("delegate");
            if (arg(frame.val.get!(ParameterTypeTuple!(U)))) goto success;
         }
         static if (is (T[i] U == _range!(V), V))
         {
            V switchVal1 = frame.val.get!(V);
            if (arg.min <= switchVal1 && switchVal1 <= arg.max) goto success;
         }

      }
   }
   else
   {
success:
      _break();// <- change this to change the default action
      args[$-1]();
   }
}

bool always(int a)
{
   return true;
}

void main()
{
   myswitch(24,
   {
      mycase(10, 11, 12,                 { writefln("10, 11, 12"); _continue; });
      mycase(24,                         { writefln("24"        ); _rematch;  });
      mycase(range(3, 25),               { writefln("range"     ); _rematch;  });
      mycase((int a) { return a > 40; }, { writefln(">40"       ); _rematch;  });
      mycase(&always,                    { writefln("always"    ); _rematch;  });
   });
   _range!(int) ra = {2,3};
}
January 04, 2008
BC wrote:
> On Thu, 03 Jan 2008 10:30:09 -0000, davidl <davidl@126.com> wrote:
> 
>> http://pragmatic.oreilly.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html 
>>
>>
>> I think I love the trap no.9
>>
> 
> 
> i had a go at rolling my own, here it is if anyone's interested:
> 
> //public domain
> import std.stdio;
> import std.variant;
> import std.traits;
> 
> struct SwitchFrame
> {
>    Variant     val;
>    bool        active;
>    bool        _break;
>    static SwitchFrame opCall(Variant val, bool active, bool _break)
>    {
>       SwitchFrame result;
>       result.val    = val;
>       result.active = active;
>       result._break = _break;
>       return result;
>    }
> }
> 
> SwitchFrame[] switchStack;
> 
> void _continue()  //execute next case after this one
> {
>    switchStack[$ - 1].active = true;
>    switchStack[$ - 1]._break = false;
> }
> 
> void _rematch()   //execute the next case that matches
> {
>    switchStack[$ - 1].active = false;
>    switchStack[$ - 1]._break = false;
> }
> 
> void _break()     //don't execute any more cases
> {
>    switchStack[$ - 1].active = false;
>    switchStack[$ - 1]._break = true;
> }
> 
> 
> struct _range(T)
> {
>    T min;
>    T max;
> }
> 
> _range!(T) range(T)(T min, T max)
> {
>    _range!(T) result;
>    result.min = min;
>    result.max = max;
>    return result;
> }
> 
> struct rangedg(T)
> {
>    T min;
>    T max;
>    bool func(T val)
>    {
>       return min <= val && val <= max;
>    }
> }
> 
> void myswitch(T, U)(T switchVal, U block)
> {
>    Variant v = switchVal;
>    switchStack ~= SwitchFrame(v, false, false);
>    block();
>    switchStack.length = switchStack.length - 1;
> }
> 
> void mycase(T...)(T args)
> {
>    SwitchFrame* frame = &switchStack[$ - 1];
>    if (frame._break) return;
>    if (!frame.active)
>    {
>       foreach (i, arg; args[0 .. $-1])
>       {
>          if (typeid(T[i]) == frame.val.type && arg == frame.val.get!(T[i])) goto success;
> 
>          static if (is (typeof(*arg) U == function))
>          {
>             //writefln("function");
>             if (arg(frame.val.get!(U))) goto success;
>          }
>          static if (is (T[i] U == delegate))
>          {
>             //writefln("delegate");
>             if (arg(frame.val.get!(ParameterTypeTuple!(U)))) goto success;
>          }
>          static if (is (T[i] U == _range!(V), V))
>          {
>             V switchVal1 = frame.val.get!(V);
>             if (arg.min <= switchVal1 && switchVal1 <= arg.max) goto success;
>          }
> 
>       }
>    }
>    else
>    {
> success:
>       _break();// <- change this to change the default action
>       args[$-1]();
>    }
> }
> 
> bool always(int a)
> {
>    return true;
> }
> 
> void main()
> {
>    myswitch(24,
>    {
>       mycase(10, 11, 12,                 { writefln("10, 11, 12"); _continue; });
>       mycase(24,                         { writefln("24"        ); _rematch;  });
>       mycase(range(3, 25),               { writefln("range"     ); _rematch;  });
>       mycase((int a) { return a > 40; }, { writefln(">40"       ); _rematch;  });
>       mycase(&always,                    { writefln("always"    ); _rematch;  });
>    });
>    _range!(int) ra = {2,3};
> }

Neat, but too many braces. :-(((({(}

--bb
January 04, 2008
On 1/4/08, BC <notmi_emayl_adreznot@hotmail.com.remove.not> wrote:
> void main()
> {
>     myswitch(24,
>     {
>        mycase(10, 11, 12,                 { writefln("10, 11, 12");
> _continue; });
>        mycase(24,                         { writefln("24"        );
> _rematch;  });
>        mycase(range(3, 25),               { writefln("range"     );
> _rematch;  });
>        mycase((int a) { return a > 40; }, { writefln(">40"       );
> _rematch;  });
>        mycase(&always,                    { writefln("always"    );
> _rematch;  });
>     });
>     _range!(int) ra = {2,3};
> }

A mighty and noble effort - but I can't help but feel that the following is more readable:

    if (x==10 || x==11 || x==12) writefln("10,11,12");
    if (x==24) writefln(24);
    if (x>=3 && x<=25) writefln("range");
    writefln("always");

The way I see it, the advantage of "switch/case" over "if" is that the compiler might be able to find cool ways to optimize the code (e.g binary search, sparse array lookup, table lookup, whatever). If the compiler can't do that, well then, what's the point of using it at all when "if" is perfectly expressive already?
January 05, 2008
On Fri, 04 Jan 2008 17:26:54 -0000, Janice Caron <caron800@googlemail.com> wrote:

> On 1/4/08, BC <notmi_emayl_adreznot@hotmail.com.remove.not> wrote:
>> void main()
>> {
>>     myswitch(24,
>>     {
>>        mycase(10, 11, 12,                 { writefln("10, 11, 12");
>> _continue; });
>>        mycase(24,                         { writefln("24"        );
>> _rematch;  });
>>        mycase(range(3, 25),               { writefln("range"     );
>> _rematch;  });
>>        mycase((int a) { return a > 40; }, { writefln(">40"       );
>> _rematch;  });
>>        mycase(&always,                    { writefln("always"    );
>> _rematch;  });
>>     });
>>     _range!(int) ra = {2,3};
>> }
>
> A mighty and noble effort - but I can't help but feel that the
> following is more readable:
>
>     if (x==10 || x==11 || x==12) writefln("10,11,12");
>     if (x==24) writefln(24);
>     if (x>=3 && x<=25) writefln("range");
>     writefln("always");
>
Good point.

> The way I see it, the advantage of "switch/case" over "if" is that the
> compiler might be able to find cool ways to optimize the code (e.g
> binary search, sparse array lookup, table lookup, whatever). If the
> compiler can't do that, well then, what's the point of using it at all
> when "if" is perfectly expressive already?

Do you know if the compilers actually do this?
January 05, 2008
On 1/5/08, BC <notmi_emayl_adreznot@hotmail.com.remove.not> wrote:
> > The way I see it, the advantage of "switch/case" over "if" is that the compiler might be able to find cool ways to optimize the code (e.g binary search, sparse array lookup, table lookup, whatever). If the compiler can't do that, well then, what's the point of using it at all when "if" is perfectly expressive already?
>
> Do you know if the compilers actually do this?

Me? Hell no! I don't write compilers. But Walter may be able to answer that question for D.

I do know the answer for at least one ancient compiler of old. I know that back in the days of AmigaDOS, the Amiga compiler used a choice of two strategies. It would create a simple O(1) lookup table if all the values were contiguous, or nearly contiguous, or an O(log(N)) binary search otherwise. But that was then and this is now and things are bound to be different.
January 05, 2008
BC wrote:
> On Fri, 04 Jan 2008 17:26:54 -0000, Janice Caron <caron800@googlemail.com> wrote:
>> The way I see it, the advantage of "switch/case" over "if" is that the
>> compiler might be able to find cool ways to optimize the code (e.g
>> binary search, sparse array lookup, table lookup, whatever). If the
>> compiler can't do that, well then, what's the point of using it at all
>> when "if" is perfectly expressive already?
> 
> Do you know if the compilers actually do this?

I've heard that javac uses table lookups for switch statements.