Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #143131 > unrolled thread

for ... else blocks

Started by"Rick C. Hodgin" <rick.c.hodgin@gmail.com>
First post2019-04-16 14:57 -0400
Last post2019-04-16 23:32 +0200
Articles 20 on this page of 43 — 9 participants

Back to article view | Back to comp.lang.c


Contents

  for ... else blocks "Rick C. Hodgin" <rick.c.hodgin@gmail.com> - 2019-04-16 14:57 -0400
    Re: for ... else blocks "Rick C. Hodgin" <rick.c.hodgin@gmail.com> - 2019-04-16 15:06 -0400
      Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-16 20:35 +0100
        Re: for ... else blocks "Rick C. Hodgin" <rick.c.hodgin@gmail.com> - 2019-04-16 17:02 -0400
          Re: for ... else blocks David Brown <david.brown@hesbynett.no> - 2019-04-16 23:36 +0200
            Re: for ... else blocks "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-04-17 00:45 +0200
              Re: for ... else blocks Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-04-17 01:06 +0100
                Re: for ... else blocks "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-04-17 04:45 +0200
                  Re: for ... else blocks David Brown <david.brown@hesbynett.no> - 2019-04-17 09:32 +0200
                    Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-17 11:38 +0100
                      Re: for ... else blocks David Brown <david.brown@hesbynett.no> - 2019-04-17 14:05 +0200
                        Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-17 13:57 +0100
                          Re: for ... else blocks David Brown <david.brown@hesbynett.no> - 2019-04-17 15:36 +0200
                            Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-17 15:16 +0100
                              Re: for ... else blocks David Brown <david.brown@hesbynett.no> - 2019-04-17 17:51 +0200
                          Re: for ... else blocks "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-04-17 16:15 +0200
                            Re: for ... else blocks "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-04-17 16:24 +0200
                            Re: for ... else blocks David Brown <david.brown@hesbynett.no> - 2019-04-17 17:55 +0200
                            Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-17 17:44 +0100
                              Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-17 17:48 +0100
                                Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 10:18 -0700
                                  Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 10:32 -0700
                                    Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 10:38 -0700
                                    Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 12:12 -0700
                                      Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 12:25 -0700
                                        Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 12:39 -0700
                                          Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 15:52 -0700
                                            Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 16:50 -0700
                                      Re: for ... else blocks fir <profesor.fir@gmail.com> - 2019-04-17 13:58 -0700
                              Re: for ... else blocks David Brown <david.brown@hesbynett.no> - 2019-04-18 12:21 +0200
                                Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-18 18:54 +0100
                                  Re: for ... else blocks Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-04-18 20:36 +0100
                                    Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-18 21:15 +0100
                                      Re: for ... else blocks Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-04-18 21:46 +0100
                        Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-17 15:05 +0100
                          Re: for ... else blocks "Rick C. Hodgin" <rick.c.hodgin@gmail.com> - 2019-04-17 10:55 -0400
                        Re: for ... else blocks Ike Naar <ike@iceland.freeshell.org> - 2019-04-18 05:57 +0000
                  Re: for ... else blocks Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-04-17 20:43 +0100
    Re: for ... else blocks Chad <cdalten@gmail.com> - 2019-04-16 12:15 -0700
    Re: for ... else blocks Bart <bc@freeuk.com> - 2019-04-16 20:27 +0100
    Re: for ... else blocks Christian Gollwitzer <auriocus@gmx.de> - 2019-04-16 21:34 +0200
      Re: for ... else blocks "Rick C. Hodgin" <rick.c.hodgin@gmail.com> - 2019-04-16 16:54 -0400
      Re: for ... else blocks David Brown <david.brown@hesbynett.no> - 2019-04-16 23:32 +0200

Page 1 of 3  [1] 2 3  Next page →


#143131 — for ... else blocks

From"Rick C. Hodgin" <rick.c.hodgin@gmail.com>
Date2019-04-16 14:57 -0400
Subjectfor ... else blocks
Message-ID<q958g9$rfp$1@dont-email.me>
I'm curious what people's thought are on this idea.  It allows the initial
test on the for {..} block to enter the block, or fall-through to the else
clause.  Subsequent tests on the for {..} block that fail simply exit out
past the last }.

     for (init; test; incr)
     {
         // for code here

     } else {
         // else code here
     }

It's the equivalent of:

     init;
     if (test)
     {
         goto skip_first_iteration;
         for ( ; test; incr)
         {
skip_first_iteration:
             // for code here
         }

     } else {
         // else code here
     }

Seems a natural progression.  One I could use quite often due to the
need to test the conditions outside of the for {..} block today before
entering it with then-known valid data.  If the test for a valid state
could be incorporated into the generic iterative test, and the else
clause added, it would be more natural methinks.

-- 
Rick C. Hodgin

[toc] | [next] | [standalone]


#143133

From"Rick C. Hodgin" <rick.c.hodgin@gmail.com>
Date2019-04-16 15:06 -0400
Message-ID<q9591c$v09$1@dont-email.me>
In reply to#143131
On 4/16/2019 2:57 PM, Rick C. Hodgin wrote:
> I'm curious what people's thought are on this idea.  It allows the initial
> test on the for {..} block to enter the block, or fall-through to the else
> clause.  Subsequent tests on the for {..} block that fail simply exit out
> past the last }.
> 
>      for (init; test; incr)
>      {
>          // for code here
> 
>      } else {
>          // else code here
>      }
> 
> It's the equivalent of:
> 
>      init;
>      if (test)
>      {
>          goto skip_first_iteration;
>          for ( ; test; incr)
>          {
> skip_first_iteration:

Should be "skip_first_test:".

>              // for code here
>          }
> 
>      } else {
>          // else code here
>      }
> 
> Seems a natural progression.  One I could use quite often due to the
> need to test the conditions outside of the for {..} block today before
> entering it with then-known valid data.  If the test for a valid state
> could be incorporated into the generic iterative test, and the else
> clause added, it would be more natural methinks.

Another variant could be:

     for_if (test1; init; test2; incr)
     {
     } else {
     }

test1 is the if (test1) test, and if it fails, goes to the else.  The
test2 is the iterative test which processes like normal.  This one could
be used when test1 and test2 are different, or are not overlapping.

Of course "else if" blocks could also be used, and I think also a new
concept of "else for" block could also be used:

     for (init1; test1; incr1)
     {
         // First for code here

     } else for (init2; test2; incr2) {
         // Second for code here

     } else for (init3; test3; incr3) {
         // Third for code here

     } else {
         // Fallthrough code
     }

Same for:

     for_if (test1_1; init1; test1_2; incr1)
     {
         // First for_if code here

     } else for_if (test1_1; init1; test1_2; incr1) {
         // Second for_if code here

     } else for_if (test1_1; init1; test1_2; incr1) {
         // Third for_if code here

     } else {
         // Fallthrough code
     }

And, of course, for {..} and for_if {..} blocks could be intermixed.

-- 
Rick C. Hodgin

[toc] | [prev] | [next] | [standalone]


#143139

FromBart <bc@freeuk.com>
Date2019-04-16 20:35 +0100
Message-ID<z8qtE.88600$SH1.74138@fx03.am4>
In reply to#143133
On 16/04/2019 20:06, Rick C. Hodgin wrote:

> Another variant could be:
> 
>      for_if (test1; init; test2; incr)
>      {
>      } else {
>      }
> 
> test1 is the if (test1) test, and if it fails, goes to the else.

I thought you wanted 'init' to be executed first? Otherwise there seems 
little point to this, as it is exactly equivalent to:

      if (test1) for (init; test2; incr)
      {
      } else {
      }

which can be done right now. And without encouraging people to abuse 
for-headers even more than they already do. (I know how compulsive it is 
to cram as much in there as possible.)

   The
> test2 is the iterative test which processes like normal.  This one could
> be used when test1 and test2 are different, or are not overlapping.
> 
> Of course "else if" blocks could also be used, and I think also a new
> concept of "else for" block could also be used:
> 
>      for (init1; test1; incr1)
>      {
>          // First for code here
> 
>      } else for (init2; test2; incr2) {
>          // Second for code here
> 
>      } else for (init3; test3; incr3) {
>          // Third for code here
> 
>      } else {
>          // Fallthrough code
>      }


So, if test1 is false to start with it will do try the second for; and 
if test2 is false to start it will try the third for?

Do you have any actual use-cases for this?

[toc] | [prev] | [next] | [standalone]


#143144

From"Rick C. Hodgin" <rick.c.hodgin@gmail.com>
Date2019-04-16 17:02 -0400
Message-ID<q95fqk$94s$1@dont-email.me>
In reply to#143139
On 4/16/2019 3:35 PM, Bart wrote:
> On 16/04/2019 20:06, Rick C. Hodgin wrote:
> 
>> Another variant could be:
>>
>>      for_if (test1; init; test2; incr)
>>      {
>>      } else {
>>      }
>>
>> test1 is the if (test1) test, and if it fails, goes to the else.
> 
> I thought you wanted 'init' to be executed first? Otherwise there seems 
> little point to this, as it is exactly equivalent to:
> 
>       if (test1) for (init; test2; incr)
>       {
>       } else {
>       }

I think you can already do your syntax in C/C++.

     if (test1)
         for (...)
         {
             // for code
         }

     else {
         // else code
     }

> which can be done right now. And without encouraging people to abuse 
> for-headers even more than they already do. (I know how compulsive it is to 
> cram as much in there as possible.)

The purpose of the for_if would be to test the outer condition in a way
that's part of the for block, allowing it to then fall through to an
else clause which could be another for_if or for block, with the second
test being one which falls out of the entire loop if it is entered one
time.

>    The
>> test2 is the iterative test which processes like normal.  This one could
>> be used when test1 and test2 are different, or are not overlapping.
>>
>> Of course "else if" blocks could also be used, and I think also a new
>> concept of "else for" block could also be used:
>>
>>      for (init1; test1; incr1)
>>      {
>>          // First for code here
>>
>>      } else for (init2; test2; incr2) {
>>          // Second for code here
>>
>>      } else for (init3; test3; incr3) {
>>          // Third for code here
>>
>>      } else {
>>          // Fallthrough code
>>      }
> 
> So, if test1 is false to start with it will do try the second for; and if 
> test2 is false to start it will try the third for?

It would be used in cases where part of the test1 would be overlapping.

     if (x)
     {
         for (init; x && x->member != whatever; iter)
         {
             // Process

             // Move through the pointer chain
             x = x->next;
         }
     }

In this case, you only want to execute the init code if x is valid to
start out with.

> Do you have any actual use-cases for this?

I came across a need for it today in my code, which is why / how it
occurred to me.

-- 
Rick C. Hodgin

[toc] | [prev] | [next] | [standalone]


#143147

FromDavid Brown <david.brown@hesbynett.no>
Date2019-04-16 23:36 +0200
Message-ID<q95hsl$kuq$1@dont-email.me>
In reply to#143144
On 16/04/2019 23:02, Rick C. Hodgin wrote:
> On 4/16/2019 3:35 PM, Bart wrote:

>> Do you have any actual use-cases for this?
> 
> I came across a need for it today in my code, which is why / how it
> occurred to me.
> 

I think you'll need to show some concrete examples in order to show the 
point.

At the moment, I'm left thinking that you are constructing complicated 
syntax for the programmer to write things that a compiler can handle 
already using simple C syntax and some optimisations.

[toc] | [prev] | [next] | [standalone]


#143154

From"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Date2019-04-17 00:45 +0200
Message-ID<q95ltu$c8r$1@dont-email.me>
In reply to#143147
On 16.04.2019 23:36, David Brown wrote:
> On 16/04/2019 23:02, Rick C. Hodgin wrote:
>> On 4/16/2019 3:35 PM, Bart wrote:
> 
>>> Do you have any actual use-cases for this?
>>
>> I came across a need for it today in my code, which is why / how it
>> occurred to me.
>>
> 
> I think you'll need to show some concrete examples in order to show the 
> point.
> 
> At the moment, I'm left thinking that you are constructing complicated 
> syntax for the programmer to write things that a compiler can handle 
> already using simple C syntax and some optimisations.

I don't see Rick's postings, but I imagine what he wants is a construct 
for this (note: I think it indicates the lambda should be a named func):

     const bool early_loop_exit = invoke( [&]() -> bool
     {
         for( INIT; COND; UPDATE )
         {
             if( SOMETHING ) { return true; }
         }
         return false;
     } );

     if( not early_loop_exit )
     {
         // The logical "else" part.
     }

It can also be expressed with all code inline in current func with 
exceptions. The `return false` then corresponds to throwing of an 
exception. I believe that's how the construct works in CPython.

The exception based logic would look more clean but potentially carries 
some execution overhead for the "else" case.


Cheers!,

- Alf

[toc] | [prev] | [next] | [standalone]


#143156

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2019-04-17 01:06 +0100
Message-ID<87y3494hru.fsf@bsb.me.uk>
In reply to#143154
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> writes:

> I don't see Rick's postings, but I imagine what he wants is a
> construct for this (note: I think it indicates the lambda should be a
> named func):
>
>     const bool early_loop_exit = invoke( [&]() -> bool
>     {
>         for( INIT; COND; UPDATE )
>         {
>             if( SOMETHING ) { return true; }
>         }
>         return false;
>     } );
>
>     if( not early_loop_exit )
>     {
>         // The logical "else" part.
>     }

That looks like the Python meaning, not the one RCH was suggesting.  I
think RCH's intent can be written as:

  bool loop_body_executed = false;
  for (INIT; COND; UPDATE, loop_body_executed = true) {
     // ...
  }
  if (!loop_body_executed) {
    // The logical "else" part.
  }

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#143158

From"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Date2019-04-17 04:45 +0200
Message-ID<q963vs$ck8$1@dont-email.me>
In reply to#143156
On 17.04.2019 02:06, Ben Bacarisse wrote:
> "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> writes:
> 
>> I don't see Rick's postings, but I imagine what he wants is a
>> construct for this (note: I think it indicates the lambda should be a
>> named func):
>>
>>      const bool early_loop_exit = invoke( [&]() -> bool
>>      {
>>          for( INIT; COND; UPDATE )
>>          {
>>              if( SOMETHING ) { return true; }
>>          }
>>          return false;
>>      } );
>>
>>      if( not early_loop_exit )
>>      {
>>          // The logical "else" part.
>>      }
> 
> That looks like the Python meaning, not the one RCH was suggesting.  I
> think RCH's intent can be written as:
> 
>    bool loop_body_executed = false;
>    for (INIT; COND; UPDATE, loop_body_executed = true) {
>       // ...
>    }
>    if (!loop_body_executed) {
>      // The logical "else" part.
>    }
> 

Thanks. That seems to indicate that there are at least two about equally 
convincing but exactly opposite notions of what `else` should mean if it 
were introduced as an extension to `for`...

   ---

Anyway I find your example interesting because it's easier to read, and 
therefore grok, than my example that, more ideally, constrains the scope 
for modification of the variable.

And I think that's due to two reasons:

• That your example is shorter, mainly because of formatting.
• That C++, from its C heritage, supports the changing-variables style 
of coding, and does not so much support expression based coding.

In some other contexts the shorter formatting tends to produce what in 
my eyes is a gray wall of compact code, so I'm wary of adopting it. It's 
nice for books and articles, though, where vertical space is a limited 
resource. But on the third and gripping hand I may be too old to change 
formatting preferences again...

   ---

Going back even further historically, C's parent language BCPL had a 
construct that one can think of as “arithmetic blocks”, like a curly 
braces compound statement that could return a value.

And expressing your example with an imaginary such construct it's not 
significantly more to read, and then properly constrains the scope where 
the code can change or influence the value of the boolean:

     const bool loop_body_executed = BCPL_BLOCK {
         for (INIT; COND; UPDATE) {
              if( SOMETHING ) { return true; }
         }
         return false;
     };
     if( not loop_body_executed ) {
         // The logical "else" part.
     }


   ---

As it happens it's easy to define a macro like BCPL_BLOCK:

     #define BCPL_BLOCK   ~[&]()

... where the `~` operator is defined as e.g.

     namespace lambda_support
     {
         using std::declval;

         template< class Func
             , class Enabled_ = decltype( declval<Func>()() )
             >
         inline auto operator~( Func const& f )
             -> decltype(( f() ))
         { return f(); }
     }  // namespace lambda_support

One just needs a `using` statement for it. Hence the need for SFINAE to 
ensure that this definition only kicks in, that it's only found, when 
`~` is applied to something callable without arguments. It should 
probably be even more constrained than what I managed above.

So, that notation is not hypothetical. It's also easy to provide 
bindings for the lambda, e.g. using the `%` operator. It would be (or 
was when I tried it out, some years ago, then named `$invoked_with` 
instead of BCPL_BLOCK) a rather ugly hack that didn't entirely prevent 
incorrect use, but if one could get language and/or standard library 
support then it could be safe, because the standard library is allowed 
to use magic. I'd prefer a core language prefix invocation operator.


Cheers!,

- Alf

[toc] | [prev] | [next] | [standalone]


#143162

FromDavid Brown <david.brown@hesbynett.no>
Date2019-04-17 09:32 +0200
Message-ID<q96kqg$l34$1@dont-email.me>
In reply to#143158
On 17/04/2019 04:45, Alf P. Steinbach wrote:
> On 17.04.2019 02:06, Ben Bacarisse wrote:
>> "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> writes:
>>
>>> I don't see Rick's postings, but I imagine what he wants is a
>>> construct for this (note: I think it indicates the lambda should be a
>>> named func):
>>>
>>>      const bool early_loop_exit = invoke( [&]() -> bool
>>>      {
>>>          for( INIT; COND; UPDATE )
>>>          {
>>>              if( SOMETHING ) { return true; }
>>>          }
>>>          return false;
>>>      } );
>>>
>>>      if( not early_loop_exit )
>>>      {
>>>          // The logical "else" part.
>>>      }
>>
>> That looks like the Python meaning, not the one RCH was suggesting.  I
>> think RCH's intent can be written as:
>>
>>    bool loop_body_executed = false;
>>    for (INIT; COND; UPDATE, loop_body_executed = true) {
>>       // ...
>>    }
>>    if (!loop_body_executed) {
>>      // The logical "else" part.
>>    }
>>
> 
> Thanks. That seems to indicate that there are at least two about equally
> convincing but exactly opposite notions of what `else` should mean if it
> were introduced as an extension to `for`...
> 

And that would indicate that neither is a good idea, and "for else"
should be avoided.  Both cases can be easily and clearly handled by a
bool flag, which a good compiler can often eliminate entirely (and it's
a cheap solution even if the compiler can't remove it).

>   ---
> 
> Anyway I find your example interesting because it's easier to read, and
> therefore grok, than my example that, more ideally, constrains the scope
> for modification of the variable.
> 
> And I think that's due to two reasons:
> 
> • That your example is shorter, mainly because of formatting.
> • That C++, from its C heritage, supports the changing-variables style
> of coding, and does not so much support expression based coding.
> 

I think the principle of avoiding changing variables is good, but it can
be taken too far.  Many factors contribute to code clarity - obsessing
over any one of them is likely to give you worse (i.e., less legible)
code in the end.

> In some other contexts the shorter formatting tends to produce what in
> my eyes is a gray wall of compact code, so I'm wary of adopting it. It's
> nice for books and articles, though, where vertical space is a limited
> resource. But on the third and gripping hand I may be too old to change
> formatting preferences again...
> 
>   ---
> 
> Going back even further historically, C's parent language BCPL had a
> construct that one can think of as “arithmetic blocks”, like a curly
> braces compound statement that could return a value.
> 
> And expressing your example with an imaginary such construct it's not
> significantly more to read, and then properly constrains the scope where
> the code can change or influence the value of the boolean:
> 
>     const bool loop_body_executed = BCPL_BLOCK {
>         for (INIT; COND; UPDATE) {
>              if( SOMETHING ) { return true; }
>         }
>         return false;
>     };
>     if( not loop_body_executed ) {
>         // The logical "else" part.
>     }
> 
> 
>   ---
> 
> As it happens it's easy to define a macro like BCPL_BLOCK:
> 
>     #define BCPL_BLOCK   ~[&]()
> 
> ... where the `~` operator is defined as e.g.
> 
>     namespace lambda_support
>     {
>         using std::declval;
> 
>         template< class Func
>             , class Enabled_ = decltype( declval<Func>()() )
>             >
>         inline auto operator~( Func const& f )
>             -> decltype(( f() ))
>         { return f(); }
>     }  // namespace lambda_support
> 
> One just needs a `using` statement for it. Hence the need for SFINAE to
> ensure that this definition only kicks in, that it's only found, when
> `~` is applied to something callable without arguments. It should
> probably be even more constrained than what I managed above.
> 

You do like to come up with some convoluted code, don't you?  I found
that one interesting, however.  Your next challenge is to use concepts
to remove the "Enabled" bit.


> So, that notation is not hypothetical. It's also easy to provide
> bindings for the lambda, e.g. using the `%` operator. It would be (or
> was when I tried it out, some years ago, then named `$invoked_with`
> instead of BCPL_BLOCK) a rather ugly hack that didn't entirely prevent
> incorrect use, but if one could get language and/or standard library
> support then it could be safe, because the standard library is allowed
> to use magic. I'd prefer a core language prefix invocation operator.
> 

[toc] | [prev] | [next] | [standalone]


#143164

FromBart <bc@freeuk.com>
Date2019-04-17 11:38 +0100
Message-ID<kmDtE.71610$da5.23289@fx13.am4>
In reply to#143162
On 17/04/2019 08:32, David Brown wrote:

>> Thanks. That seems to indicate that there are at least two about equally
>> convincing but exactly opposite notions of what `else` should mean if it
>> were introduced as an extension to `for`...

Its use in Python is not hard to understand.

Its use as described in the OP is harder (RCH's ideas for features 
always are).

> And that would indicate that neither is a good idea, and "for else"
> should be avoided.  Both cases can be easily and clearly handled by a
> bool flag, which a good compiler can often eliminate entirely (and it's
> a cheap solution even if the compiler can't remove it).

The idea is to avoid the extra logic. Here's a typical use in one of my 
languages (real example):

     for sw to optionnames.len do
         if eqstring(name,optionnames[sw]) then
             do_option(sw,value)
             exit
         fi
     else
         println "Unknown option:",name
         stop 1
     od

This for a language at the level of C (I'm posting from that group). A 
higher level might just say 'sw in optionnames', but since many won't 
have eliminated for-loops completely, there is likely still to be a use 
for for-else.

The alternative as you say is to use a flag:

     optionno:=0
     for sw ...
         ...
         optionno:=sw
         ...
     od
     if optionno then
         do_option ...
     else
         println ...
     fi

I must have written a thousand such loops; the for-else feature provides 
a welcome respite, as well as a self-contained solution in one statement.

The flag version introduces a new variable that demands an extra, wider 
scope, something at odds with your preference for narrower block scopes.

[toc] | [prev] | [next] | [standalone]


#143168

FromDavid Brown <david.brown@hesbynett.no>
Date2019-04-17 14:05 +0200
Message-ID<q974qd$a1$1@dont-email.me>
In reply to#143164
On 17/04/2019 12:38, Bart wrote:
> On 17/04/2019 08:32, David Brown wrote:
> 
>>> Thanks. That seems to indicate that there are at least two about equally
>>> convincing but exactly opposite notions of what `else` should mean if it
>>> were introduced as an extension to `for`...
> 
> Its use in Python is not hard to understand.

That is debatable.  But I agree it is easier (and more useful) than
Rick's suggestion.

> 
> Its use as described in the OP is harder (RCH's ideas for features
> always are).
> 
>> And that would indicate that neither is a good idea, and "for else"
>> should be avoided.  Both cases can be easily and clearly handled by a
>> bool flag, which a good compiler can often eliminate entirely (and it's
>> a cheap solution even if the compiler can't remove it).
> 
> The idea is to avoid the extra logic. Here's a typical use in one of my
> languages (real example):
> 
>     for sw to optionnames.len do
>         if eqstring(name,optionnames[sw]) then
>             do_option(sw,value)
>             exit
>         fi
>     else
>         println "Unknown option:",name
>         stop 1
>     od
> 

	bool found = false;
	int sw;
	for (sw = 0; sw < len; sw++) {
		if (strcmp(name, options[sw]) == 0) {
			found = true;
			break;
		}
	}
	if (found) {
		do_option(sw, value);
	} else {
		printf("Unknown option: %s\r\n", name);
		stop(1);
	}

The "for else" construct is not necessary, and the bool flag solution
will be just as efficient.  Structuring the code as "try to find the
match, then handle the result" is arguably neater (and by "arguably", I
don't mean it is /definitely/ neater).  It also makes it easier to
refactor the code if you have a different way of searching, perhaps as a
separate function.


> This for a language at the level of C (I'm posting from that group). A
> higher level might just say 'sw in optionnames', but since many won't
> have eliminated for-loops completely, there is likely still to be a use
> for for-else.
> 
> The alternative as you say is to use a flag:
> 
>     optionno:=0
>     for sw ...
>         ...
>         optionno:=sw
>         ...
>     od
>     if optionno then
>         do_option ...
>     else
>         println ...
>     fi
> 
> I must have written a thousand such loops; the for-else feature provides
> a welcome respite, as well as a self-contained solution in one statement.

A smaller and more compact solution could be nice - but that would, as
you say, require a higher level language.

> 
> The flag version introduces a new variable that demands an extra, wider
> scope, something at odds with your preference for narrower block scopes.

I have a preference for narrower scopes, all else being equal.  But if
the data is needed over a wider range, then a wider scope is the answer.

[toc] | [prev] | [next] | [standalone]


#143170

FromBart <bc@freeuk.com>
Date2019-04-17 13:57 +0100
Message-ID<AoFtE.111551$sX.36190@fx31.am4>
In reply to#143168
On 17/04/2019 13:05, David Brown wrote:
> On 17/04/2019 12:38, Bart wrote:
>> On 17/04/2019 08:32, David Brown wrote:
>>
>>>> Thanks. That seems to indicate that there are at least two about equally
>>>> convincing but exactly opposite notions of what `else` should mean if it
>>>> were introduced as an extension to `for`...
>>
>> Its use in Python is not hard to understand.
> 
> That is debatable.

The controversy is with the use of the 'else' keyword. The idea of 
having code executed only on normal loop termination, not so much.

> 	if (found) {
> 		do_option(sw, value);
> 	} else {
> 		printf("Unknown option: %s\r\n", name);
> 		stop(1);
> 	}
> 
> The "for else" construct is not necessary, and the bool flag solution
> will be just as efficient.

So why not:

  	if (found) {
  		do_option(sw, value);
  	}
         if (!found) {
  		printf("Unknown option: %s\r\n", name);
		exit(1);
  	}

We don't really need 'if else' either. (I'm sure I didn't have that in 
Fortran years ago.)


> A smaller and more compact solution could be nice - but that would, as
> you say, require a higher level language.

By higher level I mean something like:

     do_option(name in optionnames, value)

'in' would return 1..len or 0, so do_option would have to deal with a 
option code of 0, to keep it simple.

Higher level code tends not to have so many explicit loops, but so long 
as for-loops /are/ still used, then an 'else' clause containing 
dedicated code for a normal termination rather than a break will still 
be useful.

[toc] | [prev] | [next] | [standalone]


#143171

FromDavid Brown <david.brown@hesbynett.no>
Date2019-04-17 15:36 +0200
Message-ID<q97a4r$35u$1@dont-email.me>
In reply to#143170
On 17/04/2019 14:57, Bart wrote:
> On 17/04/2019 13:05, David Brown wrote:
>> On 17/04/2019 12:38, Bart wrote:
>>> On 17/04/2019 08:32, David Brown wrote:
>>>
>>>>> Thanks. That seems to indicate that there are at least two about
>>>>> equally
>>>>> convincing but exactly opposite notions of what `else` should mean
>>>>> if it
>>>>> were introduced as an extension to `for`...
>>>
>>> Its use in Python is not hard to understand.
>>
>> That is debatable.
> 
> The controversy is with the use of the 'else' keyword. The idea of
> having code executed only on normal loop termination, not so much.

It is the use of the "else" keyword that you said was not hard to
understand in Python, and I said it was debatable how hard that is.  The
poster that first mentioned said that the "else" keyboard in Python
"for" loops is more often a subject of confusion than a useful coding
technique.

Clearly I don't disagree that sometimes it is useful to execute
different code when a loop is terminated early.

> 
>>     if (found) {
>>         do_option(sw, value);
>>     } else {
>>         printf("Unknown option: %s\r\n", name);
>>         stop(1);
>>     }
>>
>> The "for else" construct is not necessary, and the bool flag solution
>> will be just as efficient.
> 
> So why not:
> 
>      if (found) {
>          do_option(sw, value);
>      }
>         if (!found) {
>          printf("Unknown option: %s\r\n", name);
>         exit(1);
>      }
> 
> We don't really need 'if else' either. (I'm sure I didn't have that in
> Fortran years ago.)
> 

Sometimes that is a preferable arrangement.  But this is a straw man
(whataboutism, if you prefer) - we have "if ... else ..." in C.  A key
difference is that "if ... else ..." is extremely useful, and extremely
common in most imperative languages.  A "for ... else ..." construct is
only occasionally useful - as for all constructs, that usefulness must
be weighed against the additional learning and the potential for
confusion.  For "if ... else ...", the balance is clearly on the
"useful" side.  For "for ... else ...", the balance would appear to be
on the "confusing" side.

(Before you bring in more whataboutisms, C already has constructs that
are more often confusing than useful.  There is no need to mention them
or list them.)

> 
>> A smaller and more compact solution could be nice - but that would, as
>> you say, require a higher level language.
> 
> By higher level I mean something like:
> 
>     do_option(name in optionnames, value)
> 
> 'in' would return 1..len or 0, so do_option would have to deal with a
> option code of 0, to keep it simple.
> 

That is an odd construct - I would not expect to see it, or immediately
know what was meant.  I would rather expect something like:

	if (name in optionnames) {
		do_option(optionnames[name], value);
	} else {
		print "Unknown option"
	}

or

	try {
		do_option(optionnames[name], value);
	} except on key_error {
		print "Unknown option"
	}

(I am making up high-level syntax as I go here.)

		

> Higher level code tends not to have so many explicit loops, but so long
> as for-loops /are/ still used, then an 'else' clause containing
> dedicated code for a normal termination rather than a break will still
> be useful.
> 

I don't disagree that a "for ... else ..." clause could be useful.  But
I am not convinced that it is useful enough to overcome potential
confusion - I think its use would be rare, and alternative simple and
clear solutions are easily available.

[toc] | [prev] | [next] | [standalone]


#143174

FromBart <bc@freeuk.com>
Date2019-04-17 15:16 +0100
Message-ID<tzGtE.434583$%SF.74131@fx15.am4>
In reply to#143171
On 17/04/2019 14:36, David Brown wrote:
> On 17/04/2019 14:57, Bart wrote:

>> By higher level I mean something like:
>>
>>      do_option(name in optionnames, value)
>>
>> 'in' would return 1..len or 0, so do_option would have to deal with a
>> option code of 0, to keep it simple.
>>
> 
> That is an odd construct - I would not expect to see it, or immediately
> know what was meant.

Python has "in" but just returns True or False, which is not very 
helpful. My version returns the index where a occurs in b, although most 
useful for 1-based lists.

   I would rather expect something like:
> 
> 	if (name in optionnames) {
> 		do_option(optionnames[name], value);

You're doing a double lookup, one with "in", and one with [], which 
latter would require optionnames to be a dict. It gets messy.


> I don't disagree that a "for ... else ..." clause could be useful.  But
> I am not convinced that it is useful enough to overcome potential
> confusion - I think its use would be rare, and alternative simple and
> clear solutions are easily available.

It's rare partly because languages don't have it!

Although I already said it would have problems being added to C because 
of the ambiguity of 'else'.

[toc] | [prev] | [next] | [standalone]


#143177

FromDavid Brown <david.brown@hesbynett.no>
Date2019-04-17 17:51 +0200
Message-ID<q97i2u$p2b$1@dont-email.me>
In reply to#143174
On 17/04/2019 16:16, Bart wrote:
> On 17/04/2019 14:36, David Brown wrote:
>> On 17/04/2019 14:57, Bart wrote:
> 
>>> By higher level I mean something like:
>>>
>>>      do_option(name in optionnames, value)
>>>
>>> 'in' would return 1..len or 0, so do_option would have to deal with a
>>> option code of 0, to keep it simple.
>>>
>>
>> That is an odd construct - I would not expect to see it, or immediately
>> know what was meant.
> 
> Python has "in" but just returns True or False, which is not very
> helpful. My version returns the index where a occurs in b, although most
> useful for 1-based lists.
> 
>   I would rather expect something like:
>>
>>     if (name in optionnames) {
>>         do_option(optionnames[name], value);
> 
> You're doing a double lookup, one with "in", and one with [], which
> latter would require optionnames to be a dict. It gets messy.
> 

Depending on the data structure, the double lookup would often be fast
and simple.  In a compiled language with an optimising compiler,
especially in a language which emphasises immutable data (as most modern
languages do), this could be optimised.

However, this is just sample code in an imaginary language - not
something we need to figure out in detail.

> 
>> I don't disagree that a "for ... else ..." clause could be useful.  But
>> I am not convinced that it is useful enough to overcome potential
>> confusion - I think its use would be rare, and alternative simple and
>> clear solutions are easily available.
> 
> It's rare partly because languages don't have it!
> 

Mostly it is rare because you don't usually need it.

> Although I already said it would have problems being added to C because
> of the ambiguity of 'else'.
> 

[toc] | [prev] | [next] | [standalone]


#143173

From"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Date2019-04-17 16:15 +0200
Message-ID<q97ce9$i93$1@dont-email.me>
In reply to#143170
On 17.04.2019 14:57, Bart wrote:
> [snip] 
> By higher level I mean something like:
> 
>      do_option(name in optionnames, value)
> 
> 'in' would return 1..len or 0, so do_option would have to deal with a 
> option code of 0, to keep it simple.

That places responsibility for dealing with an unknown option down in 
`do_option`, via an `if` or `switch` or something.

I'd rather have that logic up front:

     if( const int id = 1 + my::index_of( name, optionnames ) ) {
         app::do_option( id, value );
     } else {
         my::fail( ""s + __func__ + " - unknown option '" + name + "'" );
     }

> Higher level code tends not to have so many explicit loops, but so long 
> as for-loops /are/ still used, then an 'else' clause containing 
> dedicated code for a normal termination rather than a break will still 
> be useful.

Consider also

     [](){
         for( const& string item: optionnames ) {
             if( item == name ) {
                 app::do_option( 1 + &item - &optionnames[0], value );
                 return;
             }
         }
         my::fail( ""s + __func__ + " - unknown option '" + name + "'" );
     }();

`return` is a nice statement; here it returns to the point after the 
last semicolon.

The perhaps currently cryptic JavaScript-like start and end of that code 
is a pattern that one might as well get used to.


Cheers!,

- Alf

[toc] | [prev] | [next] | [standalone]


#143175

From"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Date2019-04-17 16:24 +0200
Message-ID<q97cuq$kqc$1@dont-email.me>
In reply to#143173
On 17.04.2019 16:15, Alf P. Steinbach wrote:
> On 17.04.2019 14:57, Bart wrote:
>> [snip] By higher level I mean something like:
>>
>>      do_option(name in optionnames, value)
>>
>> 'in' would return 1..len or 0, so do_option would have to deal with a 
>> option code of 0, to keep it simple.
> 
> That places responsibility for dealing with an unknown option down in 
> `do_option`, via an `if` or `switch` or something.
> 
> I'd rather have that logic up front:
> 
>      if( const int id = 1 + my::index_of( name, optionnames ) ) {
>          app::do_option( id, value );
>      } else {
>          my::fail( ""s + __func__ + " - unknown option '" + name + "'" );
>      }
> 
>> Higher level code tends not to have so many explicit loops, but so 
>> long as for-loops /are/ still used, then an 'else' clause containing 
>> dedicated code for a normal termination rather than a break will still 
>> be useful.
> 
> Consider also
> 
>      [](){
>          for( const& string item: optionnames ) {
>              if( item == name ) {
>                  app::do_option( 1 + &item - &optionnames[0], value );
>                  return;
>              }
>          }
>          my::fail( ""s + __func__ + " - unknown option '" + name + "'" );
>      }();
> 
> `return` is a nice statement; here it returns to the point after the 
> last semicolon.
> 
> The perhaps currently cryptic JavaScript-like start and end of that code 
> is a pattern that one might as well get used to.

Oh, sorry, for the start I meant

     [&](){

capturing reference to the variables.

And also failed to mention, as before in this thread, the multi-line 
lambda indicates to me that it should probably be a named function.


Cheers!,

- Alf

[toc] | [prev] | [next] | [standalone]


#143178

FromDavid Brown <david.brown@hesbynett.no>
Date2019-04-17 17:55 +0200
Message-ID<q97i9q$qeg$1@dont-email.me>
In reply to#143173
On 17/04/2019 16:15, Alf P. Steinbach wrote:
> On 17.04.2019 14:57, Bart wrote:
>> [snip] By higher level I mean something like:
>>
>>      do_option(name in optionnames, value)
>>
>> 'in' would return 1..len or 0, so do_option would have to deal with a
>> option code of 0, to keep it simple.
> 
> That places responsibility for dealing with an unknown option down in
> `do_option`, via an `if` or `switch` or something.
> 
> I'd rather have that logic up front:
> 
>     if( const int id = 1 + my::index_of( name, optionnames ) ) {
>         app::do_option( id, value );
>     } else {
>         my::fail( ""s + __func__ + " - unknown option '" + name + "'" );
>     }
> 

Maybe a std::optional would be a nicer choice here, rather than having
to manipulate the index number?

[toc] | [prev] | [next] | [standalone]


#143179

FromBart <bc@freeuk.com>
Date2019-04-17 17:44 +0100
Message-ID<QJItE.182616$a%2.84036@fx30.am4>
In reply to#143173
On 17/04/2019 15:15, Alf P. Steinbach wrote:
> On 17.04.2019 14:57, Bart wrote:
>> [snip] By higher level I mean something like:
>>
>>      do_option(name in optionnames, value)
>>
>> 'in' would return 1..len or 0, so do_option would have to deal with a 
>> option code of 0, to keep it simple.
> 
> That places responsibility for dealing with an unknown option down in 
> `do_option`, via an `if` or `switch` or something.

Yeah, I forgot about the error message. Then do_option can just return 
the option number (or a status), and it becomes necessary to wrap withe 
do_option call with an 'if'.

Dealing with option 0 inside do_option is not an issue (in my code it 
means adding one line 'when 0 then' to ignore that option number).

 > Consider also
 >
 >      [](){
 >          for( const& string item: optionnames ) {
 >              if( item == name ) {
 >                  app::do_option( 1 + &item - &optionnames[0], value );
 >                  return;
 >              }
 >          }
 >          my::fail( ""s + __func__ + " - unknown option '" + name + "'" );
 >      }();
 >
 > `return` is a nice statement; here it returns to the point after the
 > last semicolon.

So, defining an anonymous function, immediately calling it, and using 
'return' to provide the alternate exit path from the loop that is required.

Clever, but the kind of cleverness that obscures the mundane task that 
we are actually trying to do [in this case].

As a reminder, here's my original for-else example in a bigger context:

   https://pastebin.com/Yv6xQg92

(This processes command-line options and filenames for a C compiler, but 
the same pattern is used in several programs.)

This bit doesn't need to be fast, and it doesn't need to be clever.

[toc] | [prev] | [next] | [standalone]


#143180

FromBart <bc@freeuk.com>
Date2019-04-17 17:48 +0100
Message-ID<ANItE.69625$lc3.14309@fx02.am4>
In reply to#143179
On 17/04/2019 17:44, Bart wrote:

> As a reminder, here's my original for-else example in a bigger context:
> 
>    https://pastebin.com/Yv6xQg92

(That guest paste doesn't seem to work. Try:

  https://pastebin.com/raw/bNUNjk4K)

[toc] | [prev] | [next] | [standalone]


Page 1 of 3  [1] 2 3  Next page →

Back to top | Article view | comp.lang.c


csiph-web