|
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...
|
|
|
|
|
glennPattonInThePUB wrote: I must say a couple of places I interviewed at did have a print out of my CP article...
Was is placed under a big banner that said "don't be this guy"?
|
|
|
|
|
I was starting to wonder
|
|
|
|
|
IMO, yes, and no.
Yes, in that recruiting is expensive, so most large companies recruit for the long term. As such, they want to see that you (a) are a decent guy to work with, (b) know something about the field at present, and (c) can master new technologies as they come along. No, in that the recruiters use a filter of 'X years of Y' in order to separate the sheep from the goats.
There is a fundamental mismatch between a large companies' actual requirements and those that the recruiters apply, which leads to much frustration on both sides.
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
|
|
|
|
|
Quote: There is a fundamental mismatch between a large companies' actual requirements and those that the recruiters apply, which leads to much frustration on both sides. Sigh |
Tell me about!
|
|
|
|
|
Interview processes, IMHO, should probably find answers to following questions not in any particular order:
- We have to do work X now. Can this person do it with standards that we follow or like to achieve?
- Once X is over, we need to work on Y and might also get Z to do. Will this person be useful then?
- Does this person look like he can adapt to our work culture?
- Is it probable that this person will be around as long as we would want him to be?
- Does this person look like he is willing to learn new things or rather open to change and criticism?
- He claims to be having X years of experience. Do we see expected maturity levels at this experience?*
However, many a times I have seen a job paired with years of experience. That is sometimes incorrect pairing. A person with any amount of experience can be
- Incompetent
- Rigid in the way he works
- Madly in love with technology he has worked with, hence not open for newer things.
*Technologies may have changed but I believe certain qualities only come with experience.
"It is easy to decipher extraterrestrial signals after deciphering Javascript and VB6 themselves.", ISanti[ ^]
|
|
|
|
|
Quote: Does this person look like he can adapt to our work culture? I tend to view this as can we sit down and have a with them or will a horrible argument be the result... but I'm not managment.
|
|
|
|
|