|
A better solution would be a generic "not completed" detector:
for (...)
{
}
onbreak
{
} Which could work with for, foreach, while, etc.
But to be honest, I find it pretty natural to either use a bool or put the loop code in a separate method - which has the advantage of reducing the code size in the original one as well.
I can't see that this is really needed myself, but that's just my opinion.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
That 'onbreak' handles the 'exituntil' part.
It does not handle the 'exitfor' part - the code which is to be executed if you come to the end of the loop.
Besides, the way you define it: The block which contains the iterated code, the top {} pair, is closed and dead when you come to the onbreak {} part. So whichever variables were used in the loop - such as those causing the break condition, are inaccessible to the onbreak {}. I certainly hope that you are not going to suggest that local variables within the for {} block shall be accessible to the onbreak {} block!
Putting the loop code in a separate method helps nothing to handle alternate exit paths without having to set boolean flags. If you meant to include the alternate exit handlers in the method, what is the point of the method? If not: How would you indicate the required exit handling upon return to the caller of that method? How would the exit handlers be able to access the values causing that specific exit action, values local to the loop (function)?
You have another parsing problem in your proposal: The for loop is syntactically completed at the closing }. What follows it, could be any statement. You introduce an onbreak statement, not an onbreak clause of the for statement. The semantics of the for loop is modified by an adjacent statement (i.e. the onbreak) - that is sort of messy.
Obviously, I would like to have the exit clauses available in both for loops, foreach looops etc - the language providing exit clauses has a generalized 'for' control construct that covers both 'for' and 'foreach'). It also allows several exit tests (like the 'until') for while loops, similar to several 'if (cond) break;' - but since the main point is to provide different termination handling under different conditions, the mechanism would be most useful if you could provide arbirarily many 'exituntil' type clauses. I ceratainly would welcome that!
When I first was offered these alternate exit paths, it took me a while to see its real usefulness. But once you have learned to use it, and then move over to a language without it, you all the time tell yourself 'This would just have been so much more elegant if alternate exits were provided!'.
|
|
|
|
|
Member 7989122 wrote: C class languages have some silly {}-requremnents!
They don't.
If anything C has too few by allowing unbracketed one-liners for loops and conditions.
if (Whatever)
DoSomething();
is both a little untidy and slightly dangerous as there's no really visible connection between action and condition. Stuff gets moved or removed and things start to go wrong.
For my money, the compiler should insist on:
if (Whatever)
{
DoSomething();
}
|
|
|
|
|
Or better:
if (Whatever)
{
DoSomething();
}
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
That's just bad indentation!
|
|
|
|
|
I prefer that the { comes together with the indentation; the indentation starts with the {. In your layout, it doesn't: The indentation starts before the {.
Then, I also like the indentation to end with the }. If the } is undented, the undentation comes before the }, which is, in my eyes, similarly inconsistent.
Finally: When you read an if, then the condition, then ... What??? something is missing here! I don't like that, either. If the conditional statement doesn't fit on the same line, it is not empty but contains an announcer: Conditional statement(s) follow below, indented.
So my preferred style is
if (whatever) {
DoSomething();
}
|
|
|
|
|
Let the Holy Wars resume!
If you have an important point to make, don't try to be subtle or clever. Use a pile driver. Hit the point once. Then come back and hit it again. Then hit it a third time - a tremendous whack.
--Winston Churchill
|
|
|
|
|
In Pascal, a statement is a block. Anywhere a block is required, a statement will do, because it is a block.
In C, a statement is not a block. Several productions require a block (e.g. a catch() {}), so you have to create a block out of a single statement by enclosing it in {}.
You may argue: Well, there's the explanation for those {} requirements - they are there to create a block! They are not superfluous.
Yes, if you without any question accept the way C grammar was defined, of course you find it obvious that the {} should be there. So the problem is not in the {} themselves, but in that a statement was not defined as a block, the way it in Pascal. But the end result is the same: In C, you several places need to add {} around a single statement, where Pascal does not require any bracketing constructs.
This holds for several other languages as well - I refer to Pascal because it is the most well known of those.
That bracketing constructs in Pascal are more voluminous (BEGIN - END) than {} is just a small detail related to the parsing of tokens; it is irrelevant to the syntax productions.
Most non-C algorithmic languages don't even need any () bracketing of the condition in 'if' and loop constructs - yet they allow for just as general logical expressions as C does.
The try-catch is another silly bracketing mechanism. Pascal's origins are older than the exception concept, but in other language like CHILL, any block (including a statement, which is a block) is an implict try-block: Between the block and the terminating semicolon, an ON clause may be put (ON corresponds to 'catch'). You need no pre-announcement like 'try', you need no braces just to identify what is being tried - the handler implicitly applies to the block to which it is attached.
The C language was not created by people who were experts on language design, but by programmers who did it as a left hand job inbetween lots of other work. C is that way because... well... that's just how it was done, take it or leave it... On the other hand, Pascal was Designed by a qualified Language Designer, according to a plan and with a well defined goal.
That being said: There are several cases of language designs so academic and theoretically perfect, and without any pragmatic adaptations whatsoever, that they are almost impossible to work with. It is certainly possible to overdo design! You need a teaspoonfull of pragmatism in any design. But C is not a teaspoonfull of pragmatism in a design, it is huge showels of pragmatism without any design at all. That pragmatism overload does not make it useful, it makes a mess. Well, since K&R C, the language has been tightened up in several aspects. C# is the better of the C crowd, and can be tolerated. But it never got rid of those "superfluous" {}, the parenthesising of logical expressions and that super-ugly exception syntax, which is a pity.
|
|
|
|
|
My gut reaction is that I don't like it. It doesn't seem to add any significant benefit, and your proposed syntax just doesn't feel like C# to me.
But, if you want to suggest it to, and discuss it with, the language designers, open an issue on the Roslyn GitHub repo[^].
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Maybe the tokens I used do not have a 'C# feeling' - the C lanugage class is certainly not very fond of keywords - it should rather be some punctuation. So I understand your reaction.
But that is not the main point. The main point is to provide a first class flow control mechanism that allows alternate exit paths from a loop. That I think (well, I know, from practical experience!) is a definite and significant benefit.
So, I would certainly accept other, more "C feeling" tokens, if only the semantics is in place.
|
|
|
|
|
Maybe that could've been an article, eh?
|
|
|
|
|
I am not really into writing articles... (Well, 20-30 years ago when I was young and ambitions, I did write papers and articles, but my ambitions have dwindled away )
If you are serious, not joking: Are you thinking of a CodeProject article, or something else?
Are ideas about language extensions a suitable topic for CP artibles?
|
|
|
|
|
It sounds like you are reinventing the n+1/2 ('n' and a half) times loop. I included that as a construct in a FORTRAN pre-processor in the late 1970s when it looked like
LOOP
statements
EXITIF condition
statements
EXITIF condition
statements
ENDLOOP
with this construct you can not only implement n+1/2 times loops but also implement WHILE loops, e.g.
LOOP
EXITIF NOT condition
statements
ENDLOOP
and UNTIL loops e.g.
LOOP
statements
EXITIF condition
ENDLOOP and FOR loops e.g.
cv = initialvalue
LOOP
EXITIF cv > endvalue
statements
cv = cv + increment
ENDLOOP
For your classic find value in an array search:
found = noofitemsinarray
LOOP
found = found - 1
EXITIF found < 0
EXITIF array(found) = itemtofind
ENDLOOP
|
|
|
|
|
I never knew the 'n and a half times loop', but essentially it is the same idea.
A couple disadvantages with your syntax: The exit statements are spread all over the loop code (if you have several 'EXITIF condition' and their associated statements spread throughout the loop. How do you indicate the end of an EXITCLAUSE?
Second: Several distinct / different EXITIF tests might require identical exit statements. When you directly follow the test with the exit statements, you might end up repeating the same code several times.
But all in all: The goal of this proposal closely resembles my exitfor/exitwhile idea.
|
|
|
|
|
Good lord, that seems complicated. How about:
if (mylist.Contains(refKey)
{
refKey.refcount++;
}
else
{
mylist.Insert(refKey);
refKey.refcount++;
}
since elt.key is the same object in your test if (elt.key == refkey) you can increment refKey's refcount.
Marc
|
|
|
|
|
OK, my point was not to illustrate how to insert an element into a list, but to illustrate a mechanism.
I just picked a very simple example 'off the top of my head' to show different exit handling - and as you point out: For that specific very simple example, there may be simpler ways to do it.
I could easily make a complex 50-line example to show the same mechanism in a composite context, but as a first presentation of the idea, it would clutter up the presentation. So I chose something simpler.
There certainly are cases where you have different exit handlers that can not be replaced by a predefined list method.
|
|
|
|
|
Member 7989122 wrote: OK, my point was not to illustrate how to insert an element into a list, but to illustrate a mechanism.
Ah, sorry. Sometimes I'm too literal.
Marc
|
|
|
|
|
I'll preface this by saying if you really want that syntax today, you could write that part of your program in Nemerle and use its macros to transform the syntax tree to your heart's content. I think that would let you create the kind of loop you're looking for. So you could write the class that performs the looping in Nemerle and just call it from C#/
To answer your question:
One disadvantage that comes to mind is that the change would encourage the use of loops where they're unnecessary, and would also encourage the use of the wrong data structure to solve a given problem. I think that loops with multiple break/exit conditions are usually an antipattern; if a coworker has to modify it (or you have to come back and modify it after 6+ months), the reasons for the conditional exits aren't always clear.
In the example you gave, the loop goes through the entire list every time you want to bump a reference count. If the list is relatively small, it's not a huge problem. If the list becomes large, then the O(n) lookup every time is going to hurt.
Instead, you could do something like:
public class ElementReferenceCounter
{
private readonly Dictionary<string, ListElt> elements;
public Elements()
{
elements = new Dictionary<string, ListElt>();
}
public void AddOrIncrementRefCount(string refKey)
{
if (elements.ContainsKey(refKey))
{
elements[refKey].RefCount++;
}
else
{
elements[refKey] = new ListElt(refKey, 0);
}
}
public void DecrementRefCount(string refKey)
{
if (!elements.ContainsKey(refKey)) return;
if(elements[refKey].RefCount == 1)
{
elements.Remove(refKey);
}
else
{
elements[refKey].RefCount--;
}
}
}
And you get O(1) list element lookup, at the expense of slightly higher memory use. More importantly, the interface is easy to understand. Anyone who works on the code in the future will see a call like
myList.AddOrIncrementRefCount(refkey) and immediately know the code's intent. It's easier to write tests for it this way, too.
In the future, if you found that you need to have your reference counter backed by a HashSet or a List instead of a Dictionary, go ahead and change it. As long as the class interface and behaviour stays the same, any code that uses it will just work without any changes.
I realize all that code is really specific to your example, and what you're proposing would be usable in a much wider variety of scenarios. It just seems that in most of those scenarios, there are probably more testable and maintainable ways to solve the problem.
In summation: what you propose isn't crazy and wouldn't cause major problems for the language, but it would encourage hard to maintain code. That by itself doesn't make this a bad idea...but you did ask for disadvantages, and that's one that came to mind.
|
|
|
|
|
It is relatively easy in C# to write a method, or an Extension Method, that will return whatever data, as well as some indicator (nullable bool, or enum), that contains all the possible conditions you describe: match, no match, error.
You'll find an example of that in a recent QA answer of mine: [^] .
«There is a spectrum, from "clearly desirable behaviour," to "possibly dodgy behavior that still makes some sense," to "clearly undesirable behavior." We try to make the latter into warnings or, better, errors. But stuff that is in the middle category you don’t want to restrict unless there is a clear way to work around it.» Eric Lippert, May 14, 2008
|
|
|
|
|
I disagree with "two alternate loop 'tails' ... another if the loop was left prematurely" -- that can be handled at the test site.
Almost like giving a while an else .
Just for fun, I altered a simple while loop into the following attrocity:
while ( true ) if ( ( ch = sr.Read() ) != -1 )
{
System.Console.WriteLine ( "{0} {1} {2}" , i , ch , (char) ch ) ;
if ( ch == 26 )
{
System.Console.WriteLine ( "Found a char 26 at offset {0}" , i ) ;
break ;
}
i++ ;
}
else
{
System.Console.WriteLine ( "Reached the EOF" ) ;
break ;
}
Just be sure to break out of the else .
Now to attempt:
# define whilst(x) while ( true ) if ( x )
Edit:
# define whilst(x,y) while ( x ) if ( y )
...
int ch ;
int i = 0 ;
whilst ( sr.BaseStream.CanRead , ( ch = sr.Read() ) != -1 )
{
System.Console.WriteLine ( "{0} {1} {2}" , i , ch , (char) ch ) ;
if ( ch == 26 )
{
System.Console.WriteLine ( "Found a char 26 at offset {0}" , i ) ;
sr.BaseStream.Close() ;
}
i++ ;
}
else
{
System.Console.WriteLine ( "Reached the EOF" ) ;
sr.BaseStream.Close() ;
}
modified 28-Sep-16 17:43pm.
|
|
|
|
|
Dictionary ... TryGetValue ?
Reinventing: it's yet another way to get a head
|
|
|
|
|
I don't see how that could provide a programming mechanism for specifying different handling of a loop run to its end vs. premature exit from the loop.
You might of course argue that thanks to the dictionary class, you will never have have a need for handling premature loop exit differently from loop completion, that it always can be handled by a dictionary instance. I must admit that I do not immediately see how.
|
|
|
|
|
Hi All,
Had a bit of an odd thought, some here might be able to shed more light on... I am starting to wonder if the interview process is more to do with 'can we work with this guy' more than 'this guy has X years of Y'. This is based on the fact that tech based skills change so often it can be nearly impossible for anyone to stay at the bleeding edge. Am I right...
|
|
|
|
|
You are probably right - we definitely do interview on the basis of "how do you work when there are things you don't know" rather than "what things do you know" as both IT and our vertical business change so very rapidly.
(In that context a GitHub or a Codeproject article etc. is more useful to us than a CV)
|
|
|
|
|
I must say a couple of places I interviewed at did have a print out of my CP article...
|
|
|
|
|