Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Convenient idiom for handling edge conditions in "for (auto x : collection)" loops?

101 views
Skip to first unread message

K. Frank

unread,
Aug 8, 2015, 7:28:09 PM8/8/15
to
Hello Group!

The new "for" syntax is convenient for iterating
over collections:

for (auto x : collection) // do stuff with x;

But lots of times you want to give slightly special
treatment to the first or last element:

for (unsigned i = 0; i < collection.size(); i++) {
if (i == 0) // do something special;
if (i == 0) continue; // or don't do something
// do stuff with collection[i];
if (i == collection.size() - 1) // do something special;
}

As a concrete (and common) example, suppose you want
to print out a vector in parenthesized, comma-separated
notation:

(1, 2, 3, 4, 5)

Is there a nice idiom for this using "for (auto x : y)"?

You could always just introduce a counter:

unsigned i = 0;
for (auto x : collection) {
// print x
if (++i == collection.size()) break;
// print ", "
}

But by the time you do that, you might as well just
revert to the old-school "for (unsigned i = 0, ...)"
loop.


Thanks for any ideas.


K. Frank

Victor Bazarov

unread,
Aug 8, 2015, 8:22:29 PM8/8/15
to
Not sure how to do the last thing, but the first is easy if you keep a
flag and set it as soon as the operation is done:

bool done1st = false;
for (auto x : v)
{
if (!done1st)
{
// do 1st thing with x
done1st = true;
}
... // do stuff with x
}

Of course, it could be used to not do the first thing as well. For
instance you can put the comma in front of only the second and later
elements:

bool first = true;
for (auto x : v)
{
if (!first)
mystream << ", ";
mystream << x;
first = false;
}

V
--
I do not respond to top-posted replies, please don't ask

Marcel Mueller

unread,
Aug 8, 2015, 8:33:48 PM8/8/15
to
On 09.08.15 01.27, K. Frank wrote:
> As a concrete (and common) example, suppose you want
> to print out a vector in parenthesized, comma-separated
> notation:
>
> (1, 2, 3, 4, 5)

string ret = "(";
for (...)
{ if (ret.size() != 1)
ret = ret.append(",");
ret = ret.append(...); // content
}
ret = ret.append(")");

Of course, this works only when the content cannot be empty. Otherwise
you need a bool first = true;

> Is there a nice idiom for this using "for (auto x : y)"?

I have seen different variants, all of them not that pretty.
(Btw. the ABAP language has a keyword for this common purpose.)

> You could always just introduce a counter:
>
> unsigned i = 0;
> for (auto x : collection) {
> // print x
> if (++i == collection.size()) break;
> // print ", "
> }

The invocation of size() could be slow on some container types. Although
this is uncommon. But size() might not even exist in case of forward
only iterations.

> But by the time you do that, you might as well just
> revert to the old-school "for (unsigned i = 0, ...)"
> loop.

This makes no significant difference.


Marcel

Louis Krupp

unread,
Aug 9, 2015, 1:38:41 AM8/9/15
to
On Sat, 8 Aug 2015 16:27:35 -0700 (PDT), "K. Frank"
<kfran...@gmail.com> wrote:

>Hello Group!
>
>The new "for" syntax is convenient for iterating
>over collections:
>
> for (auto x : collection) // do stuff with x;
>
>But lots of times you want to give slightly special
>treatment to the first or last element:
<snip>
>As a concrete (and common) example, suppose you want
>to print out a vector in parenthesized, comma-separated
>notation:
>
> (1, 2, 3, 4, 5)
>
>Is there a nice idiom for this using "for (auto x : y)"?
>
>You could always just introduce a counter:
>
> unsigned i = 0;
> for (auto x : collection) {
> // print x
> if (++i == collection.size()) break;
> // print ", "
> }
>
>But by the time you do that, you might as well just
>revert to the old-school "for (unsigned i = 0, ...)"
>loop.

An easy way to do the commas would be something like this:

const char* delim = "";
for (auto x : collection) {
print delim;
print x;
delim = ", ";
}

If you want to do something special for the last element in the
collection, and you don't want to compare an index to the collection
size, consider appending a sentinel element to the collection. When
you see it, you know you've hit the end of the stuff you were really
interested in.

Louis

Alf P. Steinbach

unread,
Aug 9, 2015, 2:40:58 AM8/9/15
to
On 09-Aug-15 1:27 AM, K. Frank wrote:
>
> The new "for" syntax is convenient for iterating
> over collections:
>
> for (auto x : collection) // do stuff with x;
>
> But lots of times you want to give slightly special
> treatment to the first or last element:
>
> for (unsigned i = 0; i < collection.size(); i++) {
> if (i == 0) // do something special;
> if (i == 0) continue; // or don't do something
> // do stuff with collection[i];
> if (i == collection.size() - 1) // do something special;
> }

The Python language has a nice idiom for this, namely passing the range
you want to an `enumerate` function that produces an (index, value) pair
for each value from that range. Defined as a continuation it's pretty
simple,

def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1

The Boost library provides similar functionality for C++, although with
a silly pipe-syntax (it's a bit over-engineered), and hidden so well
that I couldn't find it now by simple googling. It's probably in the
vicinity of Boost ranges. But exactly where, that's the question.

However, I was able to find a similar utility that I just wrote up for
an answer to some forum question, apparently in comp.lang.c++ (this
group) and evidently this year:

<code file="Indexer.hpp">
#pragma once
// Copyright (c) 2015 Alf P. Steinbach

#include <iterator> // std::iterator_traits
#include <stddef.h> // ptrdiff_t
#include <type_traits> // std::remove_reference
#include <utility> // std::pair, std::declval, std::begin,
std::end, std::move

namespace progrock{ namespace cppx{
using std::begin;
using std::declval;
using std::end;
using std::iterator;
using std::iterator_traits;
using std::move;
using std::pair;
using std::random_access_iterator_tag;
using std::remove_reference;

using Index = ptrdiff_t;

template< class Underlying_iter_tp, class Index_tp = Index >
class Indexer
{
public:
using Underlying_iter = Underlying_iter_tp;
using Index = Index_tp;

using Item = typename remove_reference<decltype((
*declval<Underlying_iter>() ))>::type;

struct Numbered_item
: pair<Index, Item*>
{
using Pair = pair<Index, Item*>;
using Pair::pair; // Constructors.
using Pair::first;
using Pair::second;

auto index() const -> Index { return first; }
auto value() -> Item& { return *second; }
auto value() const -> Item const& { return *second; }
};

private:
Underlying_iter under_begin_;
Underlying_iter under_end_;

class Iter
: public iterator<
typename
iterator_traits<Underlying_iter>::iterator_category,
Numbered_item,
typename iterator_traits<Underlying_iter>::difference_type,
typename iterator_traits<Underlying_iter>::pointer,
typename iterator_traits<Underlying_iter>::reference
>
{
friend class Indexer;
private:
Underlying_iter it_;
Numbered_item item_;

Iter( Underlying_iter it )
: it_( move( it ) )
, item_( 0, &*it ) // TODO: Check validity
{}

public:
friend
auto operator<( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ < b.it_); }

friend
auto operator<=( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ <= b.it_); }

friend
auto operator==( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ == b.it_); }

friend
auto operator>=( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ >= b.it_); }

friend
auto operator>( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ > b.it_); }

friend
auto operator!=( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ != b.it_); }

auto operator++()
-> Iter&
{
++it_;
++item_.first; item_.second = &*it_;
return *this;
}

auto operator++( int )
-> Iter
{
Iter original = *this;
++*this;
return original;
}

auto operator*()
-> Numbered_item const&
{ return item_; }
};

public:
auto begin() -> Iter { return Iter( under_begin_ ); }
auto end() -> Iter { return Iter( under_end_ ); }

Indexer( Underlying_iter const begin_iter, Underlying_iter
const end_iter )
: under_begin_( begin_iter )
, under_end_( end_iter )
{}
};

template< class Collection_tp >
auto indexed( Collection_tp& c )
-> Indexer<decltype( begin( c ) )>
{ return Indexer<decltype( begin( c ) )>( begin( c ), end( c ) ); }
}} // namespace progrock::cppx
</code>

I'm pretty sure that this has not been tested for anything but the
simplest possible example, in the original question, whatever it was.

And I would usully not define explicitly all those comparison operators,
but instead inherit a template that generates them.

So, it's more like example proof-of-concept code than something to use
directly as-is, but I think you may be able to at least at first just
use it directly.


> As a concrete (and common) example, suppose you want
> to print out a vector in parenthesized, comma-separated
> notation:
>
> (1, 2, 3, 4, 5)
>
> Is there a nice idiom for this using "for (auto x : y)"?

<code>
#include <p/cppx/collections/Indexer.hpp>
using namespace progrock;

#include <iostream>
#include <vector>
using namespace std;

auto main() -> int
{
using cppx::indexed;

vector<int> const v = {1, 2, 3, 4, 5};
wcout << "(";
for( auto item : indexed( v ) )
{
wcout << (item.index() > 0? ", " : "") << item.value();
}
wcout << ")" << endl;
}
</code>

<snip>

Cheers & hth.,

- Alf

--
Using Thunderbird as Usenet client, Eternal September as NNTP server.

Jorgen Grahn

unread,
Aug 9, 2015, 6:38:17 PM8/9/15
to
On Sat, 2015-08-08, K. Frank wrote:
> Hello Group!
>
> The new "for" syntax is convenient for iterating
> over collections:
>
> for (auto x : collection) // do stuff with x;
>
> But lots of times you want to give slightly special
> treatment to the first or last element:
...

I noted this just like you did, but eventually concluded that "ok, so
I can't use the new syntax in that special case. The simplification
comes at a price."

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

K. Frank

unread,
Aug 10, 2015, 9:21:27 AM8/10/15
to
Hello All!

Thanks for everyone's thoughtful comments.

On Sunday, August 9, 2015 at 6:38:17 PM UTC-4, Jorgen Grahn wrote:
> On Sat, 2015-08-08, K. Frank wrote:
> > Hello Group!
> >
> > The new "for" syntax is convenient for iterating
> > over collections:
> >
> > for (auto x : collection) // do stuff with x;
> >
> > But lots of times you want to give slightly special
> > treatment to the first or last element:
> ...
> I noted this just like you did, but eventually concluded that "ok, so
> I can't use the new syntax in that special case. The simplification
> comes at a price."

Yes, I'm basically coming to the same conclusion. But
it seems that the "special case" where I can't use the
new syntax (without paying the extra price) is for me
more often the case than not.

It's certainly trivial to do something special before
or after you iterate over the container. A boolean
flag, a la Victor, works for the first iteration and
seems conceptually lighter-weight than a counter (but
really isn't much different than introducing an integral
counter). But to do something special for the last
iteration does seem to require a counter. I suppose
one could somehow test somehow for the last element's
location in the collection, but this seems unwieldy,
potentially expensive, and not generally reliable.

(I don't think Louis's suggestion of testing for a
sentinel value at the end of the collection really
works. If I want to do something special for the
last substantive item in the collection -- and the
sentinel value is just an add-on placeholder -- I
won't find out that I've hit the sentinel value until
after I've iterated over -- and processed -- the last
"real" item -- the next-to-last nominal item -- in the
collection.)

I like Alf's suggestion of a syntax that gives you an
index value ("for free," syntactically) as well as the
collection item. I wouldn't add it as a home-brew facility
in my own code, but if it became common practice, I would
probably use it. It might be a little tough justifying adding
it to the language proper -- the old-school "for (i = ...)"
syntax does get the job done.

> /Jorgen


Thanks all for some good ideas.


K. Frank

Jorgen Grahn

unread,
Aug 10, 2015, 9:51:41 AM8/10/15
to
On Mon, 2015-08-10, K. Frank wrote:
> Hello All!
>
> Thanks for everyone's thoughtful comments.
>
> On Sunday, August 9, 2015 at 6:38:17 PM UTC-4, Jorgen Grahn wrote:
>> On Sat, 2015-08-08, K. Frank wrote:
>> > Hello Group!
>> >
>> > The new "for" syntax is convenient for iterating
>> > over collections:
>> >
>> > for (auto x : collection) // do stuff with x;
>> >
>> > But lots of times you want to give slightly special
>> > treatment to the first or last element:
>> ...
>> I noted this just like you did, but eventually concluded that "ok, so
>> I can't use the new syntax in that special case. The simplification
>> comes at a price."
>
> Yes, I'm basically coming to the same conclusion. But
> it seems that the "special case" where I can't use the
> new syntax (without paying the extra price) is for me
> more often the case than not.

That's how it felt for me too (when I went through my hobby projects
and applied C++11 features like this one, as an exercise) but I think
in my case it was an illusion.

I /hope/ it is an illusion, because otherwise ranged-for is flawed.
It should be something that's frequently useful ...

And why would it be, given how useful it is in other languages like
shell script, Perl and Python?

> [...] the old-school "for (i = ...)"
> syntax does get the job done.

Yes, and with 'auto', std::begin() and so on, a lot of the ugliness
goes away.

Scott Lurndal

unread,
Aug 10, 2015, 4:22:00 PM8/10/15
to
r...@zedat.fu-berlin.de (Stefan Ram) writes:
>r...@zedat.fu-berlin.de (Stefan Ram) writes:
>>One also might consider having an optional lambda
>>that is to be called for the special case of an
>>empty container.
>
> Here is a first implementation:
>
>#include <iostream>
>#include <ostream>
>#include <functional> // ::std::function
>#include <vector>
>
>template< typename C >
>bool foreach
>( C const container,
> ::std::function< bool( typename C::value_type, bool, bool )> body )
>{ auto const b = ::std::cbegin( container );
> auto const e = ::std::cend( container );
> bool first = true;
> bool last = false;
> auto next = b;
> for( auto i = b; i != e; i = next )
> { if( next != e )++next;
> if( next == e )last = true;
> if( !body( *i, first, last ))return false;
> first = false; }
> return true; }
>
>int main()
>{ foreach
> ( ::std::vector< int >{ 4, 5, 6, 7 },
> []( int const i, bool const isfirst, bool const islast )
> { ::std::cout <<( isfirst ? "" : ", " )<< i <<( islast ? ".\n" : "" );
> return true; } ); }
>
> . This prints:
>
>4, 5, 6, 7.
>
> . However, having to write
>
>[]( int const i, bool const isfirst, bool const islast )
>
> in the client of »foreach« is rather cumbersome!
>

Man, that is unnecessarly complicated for such a simple
problem. Just use the interators directly and dump the
unreadable auto/lamda parts.

Richard Damon

unread,
Aug 10, 2015, 10:48:43 PM8/10/15
to
One way to process the last item differently is have the loop provide
the value for the NEXT loop, process the item from the last loop
(skipping if this is the first time), and then moving the value to the
variable to remember it for the next loop. You then process the last
item after the loop. Not sure if this is cleaner than a numeric loop,
but sometimes you don't really know you are on the last item until after
you have gotten it (like detecting the end of file after it).

Gert-Jan de Vos

unread,
Aug 11, 2015, 6:59:51 AM8/11/15
to
How about this:

void f(const vector<T>& vec)
{
for (auto& e : vec)
{
if (&e == vec.front())
HandleFirst(e);
else if (&e == vec.back())
HandleLast(e);
else
HandleCenter(e);
}
}

Juha Nieminen

unread,
Aug 11, 2015, 10:01:25 AM8/11/15
to
Gert-Jan de Vos <gert-ja...@onsneteindhoven.nl> wrote:
> void f(const vector<T>& vec)
> {
> for (auto& e : vec)
> {
> if (&e == vec.front())

You are comparing a pointer to a value of type T.

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Gert-Jan de Vos

unread,
Aug 11, 2015, 11:22:41 AM8/11/15
to
On Tuesday, 11 August 2015 16:01:25 UTC+2, Juha Nieminen wrote:
> Gert-Jan de Vos wrote:
> > void f(const vector<T>& vec)
> > {
> > for (auto& e : vec)
> > {
> > if (&e == vec.front())
>
> You are comparing a pointer to a value of type T.

Indeed, I meant this:

void f(const vector<T>& vec)
{
for (auto& e : vec)
{
if (&e == &vec.front())
HandleFirst(e);
else if (&e == &vec.back())
HandleLast(e);
else
HandleCenter(e);
}
}

Gareth Owen

unread,
Aug 15, 2015, 7:50:49 AM8/15/15
to
sc...@slp53.sl.home (Scott Lurndal) writes:

> Man, that is unnecessarly complicated for such a simple problem. Just
> use the interators directly and dump the unreadable auto/lamda parts.

That's not Stefan's style. His code is the poster child for how to
turn idioms into incomprensibility.
0 new messages