handle
handle

Reputation: 6329

Is there a construct like "iterate iterable if it has elements, else ..."?

Is there a more compact way to iterate an iterable if it has elements, or do something else if it hasn't?

My "problem" is in Python but I'd be interested in other languages as well.

Currently I test the number of elements first:

In [1]: l=[]

In [2]: if l:
   ...:     for e in l:
   ...:         print(e)
   ...: else:
   ...:     print("else")
   ...:
else

The for statement has an else:

In [2]: for e in []:
   ...:     print(e)
   ...: else:
   ...:     print("else")
   ...:
else

but its "suite" (block) is also executed when the iteration is completed:

In [1]: for e in [1,2,3]:
   ...:     print(e)
   ...: else:
   ...:     print("else")
   ...:
1
2
3
else

I realize there is not much to gain here (one line and indent level) but I am curious if something like this exists.

Upvotes: 1

Views: 84

Answers (2)

aka.nice
aka.nice

Reputation: 9402

Since you ask for other languages, here is Squeak/Pharo Smalltalk, since the paradigms are sufficiently close to Python:

you would write something like:

aCollection
    ifEmpty: [Transcript cr; show: 'else']
    ifNotEmpty: [aCollection do: [:each | Transcript cr; show: each printString]].

You could as well create a new method in Collection:

ifNotEmptyDo: aBlock elseDo: elseBlock
    self isEmpty ifTrue: [^elseBlock value].
    ^self do: aBlock

Then use it like this:

aCollection
    ifNotEmptyDo: [:each | Transcript cr; show: each printString]
    elseDo: [Transcript cr; show: 'else'].

Note that isEmpty is defined as ^self size = 0 by default, which is an heresy for possibly lazy or infinite collections... So I propose this new definition:

isEmpty
    self do: [:each |^false].
    ^true

Just to see that testing emptyness or performing a loop is more or less the same task ;)

Conclusion, in Smalltalk there is a very low level of syntactic sugar.
The block closures enable differed, conditional, or repeated evaluation.
Thus all the constructs like branches and loops and combinations of these are just ordinary messages taking block closures as parameters and you can add your own construct at will.
This facility is often abused with overkill selectors for a very small added value, so the need for new constructs should allways be put in balance with the maintenance cost (more methods to implement, test, and learn for a newcomer, etc...). Let's remember: "Small is beautiful".

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1123350

Most of the time you just test for length and exit early:

if not l:
    # empty case
    return  # or raise exception

At any rate, because iteration will not take place on an empty list, the following is also sufficient, no else required:

if not l:
    # empty case
for i in l:
    # only ever executed if there are any elements

The for ... else suite is only useful for loops that exit early; the else suite is executed for all loops that completed. If a break was used the else is skipped. This makes it unsuitable for detecting empty sequences, as iteration on those completes successfully regardless of the number of elements in the absence of a break statement.

Upvotes: 4

Related Questions