Playing with the Switch and Foreach Statements

    tjansen's picture
    2004
    22
    Feb

    I've been thinking about C's control statements (if, while, switch, for etc) for a little while, and I think there's some room for improvements in the later two...


    switch

    The C# guys have made two interesting modifications to C's switch statement: fall-through
    is only allowed when there's no statement after the case (thus if a statement
    follows a case, there must be a break or similar jump statement).
    And it added the 'goto case' statement that can be used for
    fall-through effects. Here's a C# snippet:

    	void test(int a) {
    		switch (a) {
    		case 0:
    			Console.WriteLine("blabla");
    			break;
    		case 1:
    			Console.Write("More ");
    			goto case 0;
    		case 2:
    		case 3:
    			Console.WriteLine("lalala");
    			break;
    		}
    	}
    

    I think forbidding implicit fall-through is a good idea, it avoids
    a common source of error. But if you forbid it, why is it still necessary
    to write the redundant break statements for each case? Especially long
    switch statements look much better without them:

    	void test(int a) {
    		switch (a) {
    		case 0:
    			Console.WriteLine("blabla");
    		case 1:
    			Console.WriteLine("More");
    			goto case 0;
    		case 2:
    		case 3:
    			Console.WriteLine("lalala");
    		}
    	}
    

    Something that I am missing all the time is a way to describe value
    ranges in a switch statement. The
    constraint syntax from my
    last entry
    offers a neat solution for it:

    	void test(int a) {
    		switch (a) {
    		case <0:
    			System.out.println("a is negative");
    		case 0:
    			System.out.println("a is 0");
    		case >0, <=100:
    			System.out.println("a is between 1 and 100");
    		case <200:
    		case >500, <600:
    			System.out.println("a is >100 and <200, or >500 and <600");
    		default:
    			System.out.println("a has some other value");
    		}
    	}
    

    Allowing comma-separated lists of operator plus constant makes switch much more
    powerful for many purposes.




    foreach

    Over the last years most C-based languages got a foreach statement to
    iterate over collections and arrays, but every language got its own
    syntax. This is what it looks like in C#:

    	int sum = 0;
    	foreach (int i in someArray)
    		sum += i;
    

    ... and ECMAScript (JavaScript):

    	var sum = 0;
    	for (var i in someArray)
    		sum += i;
    

    ... and Java 1.5:

    	int sum = 0;
    	for (int i: someArray)
    		sum += i;
    

    I definitely prefer Java's syntax as 'for' is shorter than 'foreach'
    (without reducing readability) and I don't like making in
    a keyword.

    Unfortunately neither Java nor C# exploit their foreach as a nicer
    alternative for regular for loops. The common C pattern

    	for (int i = 0; i < 100; i++)
    		System.out.println("Bla");
    

    can be expressed with a foreach loop and a special object that
    provides a sequence of integers, similar to
    Python's range() function:

    	for (int i: new Range(100))
    		System.out.println("Bla");
    

    It's looking nicer without the new keyword, as shown here:

    	for (int i: Range(100))
    		System.out.println("Bla");
    

    Using other constructors it would be possible to specify at start value, to
    go backwards or use bigger steps:

    	for (int i: Range(50, 10, -1))
    		System.out.println("i=" + i);
    

    As for loops are pretty common, it may make sense to have a special
    operator for simple ranges, like
    Ruby does and
    Project Alpha proposes:

    	for (int i: 0...99)
    		System.out.println("i=" + i);
    

    The expression 'a...b' is short for '(a<=b)?Range(a, b+1):Range(b, a-1, -1)'.
    Ranges can also be used for other purposes, for instance as function
    arguments. With Range and an overloaded indexer iterating through the
    first ten members of a list would be as easy as:

    	void print10Numbers(List numberList) {
    		for (int i: numberList[0...9])
    			System.out.println("i=" + i);
    	}
    

    Unlike regular methods for slicing collections such as Java's List.subList()
    this syntax gives more flexibility. You could get the first ten numbers in reverse order (numberList[9...0]), skip every second number (numberList[Range(0, numberList.size(), 2)]) and so on.