Skip to content
Why is a raven like a writing desk?

Thoughts both confusing and enlightening.

Why is a raven like a writing desk?

Thoughts both confusing and enlightening.

Exercising Ranges (part 5)

elbeno, 1 July, 20151 July, 2015

(Start at the beginning of the series if you want more context.)

Generalising iota

To pretty-print a reversible polynomial, we need to provide for reversibility of iota. So what is iota? It makes a range by repeatedly incrementing a value. What if instead of increment, we use an arbitrary function? We could do this with generate_n:

template 
std::string to_string_reverse(Rng&& r)
{
  auto s = ranges::size(r);
  auto powers = ranges::view::generate_n(
      [s] () mutable { return --s; }, s);
  return detail::to_string(
      ranges::view::zip(ranges::view::reverse(std::forward(r)), 
                        powers));
}

But this isn’t quite as clean as it could be: the lambda mutates state. An alternative is found in the Haskell Prelude:

iterate :: (a -> a) -> a -> [a] 

iterate applies a function repeatedly to an argument, feeding the result of the last iteration to the input of the next one. It’s very similar to generate, and we can write both iterate and iterate_n. The view takes a function and an initial value, and produces the sequence:

[latex] x, f(x), f(f(x)), f(f(f(x))), … [/latex]

Here’s the C++ for the skeleton of iterate_view:

template
struct iterate_view
  : view_facade, infinite>
{
private:
  friend struct range_access;
  using result_t = concepts::Function::result_t;
  semiregular_t gen_;
  semiregular_t val_;
  ...
  explicit iterate_view(G g, T&& t)
    : gen_(std::move(g)), val_(std::move(t))
  {}
  ...
};

The cursor is easily written just like generate‘s cursor: next() caches a new value, and current() returns it. Like generate, the view can only be traversed once, so the cached value is kept in the view itself rather than the iterator. And there is no end cursor.

struct cursor
{
  iterate_view *view_;
  ...
  result_t current() const
  {
    return view_->val_;
  }
  void next()
  {
    view_->next();
  }
};
void next()
{
  val_ = gen_(val_);
}

For iterate_n, the approach is almost identical, except that next() decrements a count, and size() is also provided. Using iterate_n, we can generate a decreasing sequence and use it in the reverse print function for a polynomial, which we know is a SizedRange.

template 
std::string to_string_reverse(Rng&& r)
{
  auto s = ranges::size(r);
  auto powers = ranges::view::iterate_n(
      [] (auto i) { return i - 1; }, s-1, s);
  return detail::to_string(
      ranges::view::zip(ranges::view::reverse(std::forward(r)), 
                        powers));
}

As for iterate itself, it’s potentially useful in lots more places. The obvious use cases (and test cases) are mathematical in nature, for instance my test case using the Collatz conjecture (aka Ulam conjecture, 3n+1 conjecture) sequence starting with 27:

int collatz(int n)
{
  if (n & 1) return 3*n+1;
  return n>>1;
}

DEF_TEST(Collatz27, Iterate)
{
  auto m = view::iterate(collatz, 27)
    | view::take_while([] (int n) { return n != 1; });
  EXPECT(ranges::find(m, 9232) != ranges::end(m));
  return true;
}

But there are also plenty of applications in other code, where we are updating the state of something based on its previous state: iterate separates the loop code and allows us to write a clean function that simply produces a new state from an old state. This is useful for games and simulations of all kinds. So iterate is a useful addition to the range views.

Now we can pretty-print polynomials properly, in the next part I’m going to take on the trickier subject of how to multiply them.

C++ Programming

Post navigation

Previous post
Next post

Related Posts

C++ Reflection: Another Monad

4 March, 20264 March, 2026

“Discovery consists of seeing what everybody has seen, and thinking what nobody has thought.” — Albert Szent-Györgi, 1937 Nobel Laureate for Medicine I’m sure others have thought this, but they’re certainly not saying much about it. Barry’s recent blog post “Behold the power of meta::substitute” got so close to saying…

Read More

Exploring Pascal’s Triangle

19 August, 2007

Pascal’s Triangle (henceforth known as PT). You know – that thing you learned in maths. The Wikipedia entry, like most mathematical Wikipedia entries, reads (if your mathematical background is anything like mine) like “here’s a few things you might vaguely remember from school, oh and of course gleep = glorp”….

Read More

Exercising Ranges (part 1)

1 July, 20151 July, 2015

The idea For some time since attending C++Now, I have been meaning to get around to playing with Eric Niebler’s range library. Eric presented ranges in a worked example as one of the keynotes at C++Now – a prior version of the talk that he gave at the Northwest C++…

Read More

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

©2026 Why is a raven like a writing desk? | WordPress Theme by SuperbThemes