views:

758

answers:

7

The question field is a bit too short to pose my real question. If anyone can recapitulate it better, please feel free.

My real question is this: I'm reading a lot of other people's code in C# these days, and I have noticed that one particular form of iteration is widely spread, (see in code).
My first question is:

Are all these iterations equivalent?

And my second is: why prefer the first? Has it something to do with readibility? Now I don't believe the first form is more readable then the for-form once you get used to it, and readibility is far too much a subjective item in these constructs, of course, what you use the most will seem more readable, but I can assure everyone that the for-form is at least as readable, since it has all in one line, and you can even read the initializing in the construct.

Thus the second question: why is the 3rd form seen much less in code?

        // the 'widespread' construct
        int nr = getNumber();
        while (NotZero(nr))
        { 
            Console.Write(1/nr);
            nr = getNumber();
        }

        // the somewhat shorter form
        int nr;
        while (NotZero(nr = getNumber()))           
            Console.Write(1 / nr);            

        // the for - form
        for (int nr = getNumber(); NotZero(nr); nr = getNumber())
            Console.Write(1 / nr);
+5  A: 

Are all this iterations equivalents?

yes

why prefer the first? Has it sth. to do with readibility?

because you may want to extend the scope of the nr var beyond the while loop?

why is the 3th form seen much less in code?

it is equivalent, same statements! You may prefer the latter because you don't want to extend the scope of the nr variable

dfa
because you may want to extend the scope of the nr var beyond the while loop? --> good point , though in typical uses, you won't
Peter
And it doesn't make sense, since outside of the while loop the nr var will only hold the last value assigned to it --or no value, if the while condition is not met.
foljs
sometimes it makes sense, this is not the case
dfa
I disagree with the last EDIT. In all cases getNumber() is executed just the same amount of times.
David Rodríguez - dribeas
+1, first one with scope issue
Peter
+2  A: 

I think people use the while() loop often because it best represents the way you would visualize the task in your head. I think think there is any performance benefits for using it over any other loop structure.

Nippysaurus
There is no performance benefit in any of the loop structures.
David Rodríguez - dribeas
+5  A: 

The first and third forms you've shown repeat the call to GetNumber. I prefer the second form, although it has the disadvantage of using a side-effect within a condition of course. However I pretty much only do that with a while loop. Usually I don't end up passing the result as an argument though - the common situations I find myself in are:

string line;
while ( (line = reader.ReadLine()) != null)
...

and

int bytesRead;
while ( (bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
...

Both of these are now so idiomatic to me that they don't cause me any problems - and as I say, they allow me to only state each piece of logic once.

If you don't like the variable having too much scope, you can just introduce an extra block:

{
  int bytesRead;
  while ( (bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
  {
     // Body
  }
}

Personally I don't tend to do this - the "too-wide" scope doesn't bother me that much.

I suspect it wouldn't be too hard to write a method to encapsulate all of this. Something like:

ForEach(() => reader.ReadLine(), // Way to obtain a value
        line => line != null,    // Condition
        line =>
{
    // body
};

Mind you, for line reading I have a class which helps:

foreach (string line in new LineReader(file))
{
    // body
}

(It doesn't just work with files - it's pretty flexible.)

Jon Skeet
Jon, that form is te second one (there are 3 examples, not 2) and the strange logics are for the sake of the example. (I was just writing some regexloop : for (theMatch = r.Match(inputString); theMatch.Success; theMatch = theMatch.NextMatch()) but didnot want that in the example
Peter
And I'm using probably that same form the most too, but I rephrase the question for you : "Why use the widespread form over the short form?" Maybe you have given the answer yourself : cause of the introducing of side effects? But you use it anyways, so it's not that bad of a practice i suppose?
Peter
Ah, true - I hadn't seen the second example. Will edit...
Jon Skeet
The problem with the 'while' structure is that the variable gets out of the scope of the loop, even if it is unneeded/unwanted. I'll always prefer keeping variables to the most limited scope that is required.
David Rodríguez - dribeas
@dribeas: I generally prefer a more limited scope too, but I prefer to avoid duplication more. If you want you can always put braces round the "declaration + while loop" so the scope is just that block. I'll edit my answer to demonstrate this.
Jon Skeet
+1 tx, very interesting
Peter
+2  A: 

Here is a random speculation:

When I write C# code, the only two looping constructs I write are while() and foreach(). That is, no one uses 'for' any more, since 'foreach' often works and is often superior. (This is an overgeneralization, but it has a core of truth.) As a result, my brain has to strain to read any 'for' loop because it's unfamiliar.

Brian
Yes, I can see that. Here however a testcondition is involved, so foreach() is another story.
Peter
I disagree, in the sense of the other answer I posted (you can use test conditions with foreach - e.g. TakeWhile).
Brian
If you iterate long string arrays, use for() instead of foreach()
Davorin
I use for when the index is somehow important
Ikke
for comes handy when iterating over two structures that support index. That is, until we get Zip in C#
flq
+3  A: 

I think that the third form (for-loop) is the best of these alternatives, because it puts things into the right scope. On the other hand, having to repeat the call to getNumber() is a bit awkward, too.

Generally, I think that explicit looping is widely overused. High-level languages should provide mapping, filtering, and reducing. When these high level constructs are applicable and available, looping instead is like using goto instead of looping.

If mapping, filtering, or reducing is not applicable, I would perhaps write a little macro for this kind of loop (C# doesn't have those, though, does it?).

Svante
+3  A: 

I offer another alternative

    foreach (var x in InitInfinite(() => GetNumber()).TakeWhile(NotZero))
    {
        Console.WriteLine(1.0/x);
    }

where InitInfinite is a trivial helper function. Whole program:

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static IEnumerable<T> InitInfinite<T>(Func<T> f)
    {
        while (true)
        {
            yield return f();
        }
    }
    static int N = 5;
    static int GetNumber()
    {
        N--;
        return N;
    }
    static bool NotZero(int n) { return n != 0; }
    static void Main(string[] args)
    {
        foreach (var x in InitInfinite(() => GetNumber()).TakeWhile(NotZero))
        {
            Console.WriteLine(1.0/x);
        }
    }
}
Brian
I think Jon and you have sth. similar, but I lack time for now to dive into it, tx all, I think it's very interesting.
Peter
I guess it works, but why do it?
erikkallen
Really? It looks so natural to me, but I have been using F# for more than a year now, so I guess my brain is permanently warped.
Brian
+1 for advanced use of C#
dfa
+1 for interesting
Peter
Indeed Brian, I knew this was your answer before I even saw who answered it. I've not written a for or a while loop in a long time because of F#. Folds and recursion have really taken over my code.
gradbot
+1  A: 

As for why (1) and (2) are "preferred" over (3), my feeling is that most people think of the latter as a way to iterate over a range, using the condition to define the range, rather than continuing to iterate over a block while some condition still holds. The keyword semantics lend themselves to this interpretation and I suspect that, partly because of that, people find that the expressions are most readable in that context. For instance, I would never use (1) or (2) to iterate over a range, though I could.

Between (1) and (2), I'm torn. I used to use (2) (in C) most often due to the compactness, but now (in C#) I generally write (1). I suppose that I've come to value readability over compactness and (1) seems easier to parse quickly and thus more readable to my mind even though I do end up repeating a small amount of logic.

Honestly, I rarely write while statements anymore, typically using foreach -- or LINQ -- in the cases where while statements would previously been used. Come to think of it, I'm not sure I use many for statements, either, except in unit tests where I'm generating some fixed number of a test object.

tvanfosson