Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #143131 > unrolled thread
| Started by | "Rick C. Hodgin" <rick.c.hodgin@gmail.com> |
|---|---|
| First post | 2019-04-16 14:57 -0400 |
| Last post | 2019-04-16 23:32 +0200 |
| Articles | 20 on this page of 43 — 9 participants |
Back to article view | Back to comp.lang.c
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 →
| From | "Rick C. Hodgin" <rick.c.hodgin@gmail.com> |
|---|---|
| Date | 2019-04-16 14:57 -0400 |
| Subject | for ... 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]
| From | "Rick C. Hodgin" <rick.c.hodgin@gmail.com> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | "Rick C. Hodgin" <rick.c.hodgin@gmail.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2019-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2019-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]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2019-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]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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