Hacker News new | past | comments | ask | show | jobs | submit login

'goto' is more universal and may make cleaner code even in loops. E.g. to loop backwards without 'goto' one has to resort to an idiom like that:

    for (i = n; i--; )
and I wouldn't say this idiom is that clear. You can get used to it, of course. Another interesting case is printing a list of items with separators. C loop constructs are biased cases of frequently used loops but they do not cover all the possible cases. 'goto' is unbiased.

A similar case are Python loops. There are “Pythonic” ways:

    for item in iterable
        ...
They look sweet but are useful only in some cases. E.g. if you need to modify `iterable`, the Pythonic way won’t work. There are Pythonic workarounds, but they may involve copying a whole iterable, which is hardly a good idea. But there is also a less Pythonic way:

    i = 0; n = len(iterable)
    while i < n:
        ...
This works all the time and serves all possible cases.



> This works all the time and serves all possible cases.

Unless you want the check to be always against the initial length of your iterable, rather than its modified state, you'd have to repeat the `n = len(iterable)` inside the loop, or introduce some syntactic caramel of the walrus kind (though I'm not sure if it would work here).


It is actually much simpler: you change 'n' as well:

    while i < n:
      if shouldDelete(iterable[i]):
        del iterable[i]; n -= 1
      else:
        i += 1


One could use generators to produce a modified iterable without copying e.g. here's genexpr is used to define generator:

     squared = (x*x for x in iterable)
In general, a generator function can be used if necessary:

    def squared(iterable):
        for x in iterable:
            yield x*x


Nothing to important but, just for readers who may be unfamiliar with Python, your last example can be rewritten in a quite idiomatic Pythonic way:

    for i in range(len(iterable)):
        ...


Or, in case you need both `i` and `iterable[i]` within your loop:

    for i, item in enumerate(iterable):
        ...


Yes, all this works. These are Pythonic ways. But see, here the `enumerate` function will allocate a new number and a tuple for the number and the item. Then `i, item` will unpack that tuple and deallocate the tuple memory. All this happens quickly (in a loop it may be essentially the same memory for every tuple) and CPython is well optimized for that, of course, but it is not free.

With an integer cursor (`i`) there are no unique numbers for each item and no temporary tuples. This is slightly cheaper. And also more flexible, which is the main point I'm trying to make. Other loop constructs are meant to be used in certain way and will not work otherwise. Integer cursors are not meant to be used in any special way, you can combine them as you wish. They give the maximum possible flexibility from the beginning. You can loop in any direction, by multiple items or by a variable number of items, over many iterables at once, changing the iterables as you go, and so on.

(A special case in Python are list comprehensions; these are useful because most of that “comprehending” happens inside CPython and thus they are faster.)


There are languages that tried to make these kinds of loops a bit more explicit, like Ada with the reverse keyword, and the whole named loop/block which used with the exit keyword can make for more compact, succinct loops that still read without mental gymnastics.

These kinds of choice don't come alone, but together with the 'step' keyword, the opiniated absence of the 'continue' keyword and the availability or inner/embedded functions/procedures.

For me classic foreach iterators that mask the actual iterator fall apart when I need some kind of 'join' operation (do something specific on each first, last and neither elements of a collection).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: