2

I'm new to C++ (coming from C and Java) and wanted to write a simple Sudoku-Solver as an exercise. I was thinking about using iterators to iterate over the rows/columns/blocks of the Sudoku field and read this c++Reference page.

Now I have two questions:

  • To me it seems like there exists two kinds of iterators Input-(read only)/Output(write only)-iterators and there are different kinds of Input-Iterators (Contiguous/RandomAccess/etc). If I want to iterate over the Sudoku fields I want to be able to read and write elements. What iterator should my iterator extend?

  • I actually think that I need two iterators one to iterate over the different rows/columns/blocks and one to iterate over the elements over the elements in these rows/columns/blocks. What is a common naming convention to use here? I was thinking about RowIterator to iterate over the rows and RowElementIterator to iterate over the contained elements inside an object pointed to by RowIterator.

I was also hoping for a good introducing resource into C++ iterators.

  • Here is a good reference for you: http://en.cppreference.com/w/cpp/iterator – NathanOliver Aug 25 '16 at 12:04
  • Iterators are a generalized concept, to access elements in a container. I doubt that iterators are a natural way to access the elements of a Sudoku grid. – IInspectable Aug 25 '16 at 12:04
  • There is no way to extend an iterator (besides manually duplicating and adding behavior). You can inherit from an iterator, but that is not the same and has a lot of drawbacks. There is actually no reason to inherit anything. You just write an iterator from scratch. Iterators that allow both reading and writing are the norm, only streams need trickyness such as read- or write-only iterators. That said you may just want to use a simple `int` index instead. – nwp Aug 25 '16 at 12:11
  • @NathanOliver This page is included in my question... –  Aug 25 '16 at 12:12
  • @nwp I want a convenient way to iterate over an Sudoku block and it can get pretty ugly with `int` indices. For example, when the first Sudoku block has the indices `{ 0, 1, 2, 9, 10, 11, 18, 19, 20 }`. –  Aug 25 '16 at 12:15
  • Well then just make a `struct` that has a `private` `int` and an overloaded `operator++()` that sets the `int` to the correct value and return the `int` on `const operator *()` and you are pretty much done. – nwp Aug 25 '16 at 12:19
  • An iterator can be both input iterator (i.e. readable) and output iterator (i.e. writable), in which case we can call it a _mutable_ input iterator. Is cppreference unclear about that? – cpplearner Aug 25 '16 at 13:00
  • writing your own iterator has a bit of boilerplate to it. if you wanna cheat a little, you can use [this](http://coliru.stacked-crooked.com/a/d9986e48c70e54b7). To reduce the boilerplate there is also [boost iterator facade](http://www.boost.org/doc/libs/1_54_0/libs/iterator/doc/iterator_facade.html) – sp2danny Aug 25 '16 at 13:11

2 Answers2

1

The iterator structure is different from what you think. RandomAccess iterators can be capable of both input and output. Even the lowly ForwardIterators are capable of that.

Also, these aren't real base classes. Instead, they are iterator categories. That means the "InputIterator" category fully contains the "RandomAccess" category. Not every iterator allows write access and/or random access, so the "OutputIterator" and "RandomAccess" categories are orthogonal.

In your case, all your iterators can be RandomAccess; there's no real reason to restrict them. There's no obvious naming convention, or a real reason for names. You'd typically not use those names anyway now that we have for (auto& row : board) { for (auto& cell : row) { ... } ... }

Community
  • 1
  • 1
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Thank you for your answer. Since I would have three different iterators my loops would probably look like this: `for (auto& row : board.GetRows()) { ... }`, `for (auto& row : board.GetColumns()) { ... }` and `for (auto& row : board.GetBlocks()) { ... }`, right? –  Aug 25 '16 at 12:20
  • `RandomAccess iterators are capable of both input and output. ` That's not always true. For example, `std::vector::iterator` is a random access iterator and output iterator, while `std::vector::const_iterator` is a random access iterator but not an output iterator. – cpplearner Aug 25 '16 at 13:03
  • @cpplearner: Correct, updated answer to make it more explicit. – MSalters Aug 25 '16 at 13:10
0

I might be wrong, but I doubt iterators will help you much in solving Sudoku.

Suppose you'll be writing your iterators (line, col, block) as RandomAccess ones - to allow "addressing by range operator" rather than just by increment only. Being an abstract substitute for a "current position in the linear container", minimalistically-valid random access iterators won't provide the "raw index". This become a bit off-putting if you compare the use of iterators with the direct use of position indexes:

"Navigation by row/col iterators" - if you get to a position by iterating first-by-row then by col-inside-the-row, you will need an extra step to get the "switch the row but keep the column" done. In code:

// go by row first, then by col inside the row
auto elemPos=board.row_iterator()[row].col_iterator()[col];
// now, keep the col, but switch the row
auto newPos=board.getColIterator(elemPos)[newRow];
//                ^ this is an extra operation 
//                  to perform the switch from row first
//                  to column first navigation

Compare the above with the "addressing by indexes" - you have the row/col position, feel free to increment/decrement any of the two as you need

auto elem=board[row][col];
auto newElem=board[newRow][col];

Yes, I get it: pet projects for learning purposes have value, but... I think there may be heaps of other areas where addressing by iterators make much more sense then 2D grids.

Adrian Colomitchi
  • 3,974
  • 1
  • 14
  • 23