I am stuck at solving Accelerated C++ exercise 8-5 and I don't want to miss a single exercise in this book.
Accelerated C++ Exercise 8-5 is as follows:
Reimplement the
gen_sentence
andxref
functions from Chapter 7 to use output iterators rather than putting their entire output in one data structure. Test these new versions by writing programs that attach the output iterator directly to the standard output, and by storing the results inlist <string>
andmap<string, vector<int> >
, respectively.
To understand scope of this question and current knowledge in this part of the book - this exercise is part of chapter about generic function templates and iterator usage in templates. Previous exercise was to implement simple versions of <algorithm>
library functions, such as equal, find, copy, remove_copy_if
etc.
If I understand correctly, I need to modify xref
function so it:
map<string, vector<int> >
I tried to pass map iterator as back_inserter()
, .begin()
, .end()
to this function, but was not able to compile it. Answer here explains why.
xref function as in Chapter 7:
// find all the lines that refer to each word in the input
map<string, vector<int> >
xref(istream& in,
vector<string> find_words(const string&) = split)
{
string line;
int line_number = 0;
map<string, vector<int> > ret;
// read the next line
while (getline(in, line)) {
++line_number;
// break the input line into words
vector<string> words = find_words(line);
// remember that each word occurs on the current line
for (vector<string>::const_iterator it = words.begin();
it != words.end(); ++it)
ret[*it].push_back(line_number);
}
return ret;
}
Split implementation:
vector<string> split(const string& s)
{
vector<string> ret;
typedef string::size_type string_size;
string_size i = 0;
// invariant: we have processed characters `['original value of `i', `i)'
while (i != s.size()) {
// ignore leading blanks
// invariant: characters in range `['original `i', current `i)' are all spaces
while (i != s.size() && isspace(s[i]))
++i;
// find end of next word
string_size j = i;
// invariant: none of the characters in range `['original `j', current `j)' is a space
while (j != s.size() && !isspace(s[j]))
++j;
// if we found some nonwhitespace characters
if (i != j) {
// copy from `s' starting at `i' and taking `j' `\-' `i' chars
ret.push_back(s.substr(i, j - i));
i = j;
}
}
return ret;
}
Please help to understand what am i missing.
I found more details on the exercise, here: https://stackoverflow.com/questions/5608092/accelerated-c-exercise-8-5-wording-help:
template <class Out> void gen_sentence( const Grammar& g, string s, Out& out )
USAGE:
std::ostream_iterator<string> out_str (std::cout, " "); gen_sentence( g, "<sentence>", out_str );
template <class Out, class In> void xref( In& in, Out& out, vector<string> find_words( const string& ) = split )
USAGE:
std::ostream_iterator<string> out_str (std::cout, " "); xref( cin, out_str, find_url ) ;
Frankly, I have to come to the conclusion that that question is ill-posed, specifically where they specified the new interface for xref
: xref should result in a map. However, using output iterators would imply using std::inserter(map, map.end())
in this case. While you can write a compiling version of the code, this will not do what you expect since map::insert
will simply ignore any insertions with duplicated keys.
If the goal of xref is only to link the words to the line number of their first appearance this would still be ok, but I have a feeling that the author of the exercise simply missed this subtler point :)
Here is the code anyways (note that I invented a silly implementation for split
, because it was both missing and required):
#include <map>
#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <iterator>
std::vector<std::string> split(const std::string& str)
{
std::istringstream iss(str);
std::vector<std::string> result;
std::copy(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter(result));
return result;
}
// find all the lines that refer to each word in the input
template <typename OutIt>
OutIt xref(std::istream& in,
OutIt out,
std::vector<std::string> find_words(const std::string&) = split)
{
std::string line;
int line_number = 0;
// read the next line
while (getline(in, line)) {
++line_number;
// break the input line into words
std::vector<std::string> words = find_words(line);
// remember that each word occurs on the current line
for (std::vector<std::string>::const_iterator it = words.begin();
it != words.end(); ++it)
*out++ = std::make_pair(*it, line_number);
}
return out;
}
int main(int argc, const char *argv[])
{
std::map<std::string, int> index;
std::ifstream file("/tmp/test.cpp");
xref(file, std::inserter(index, index.end()));
#if __GXX_EXPERIMENTAL_CXX0X__
for(auto& entry: index)
std::cout << entry.first << " first found on line " << entry.second << std::endl;
#else
for(std::map<std::string, int>::const_iterator it = index.begin();
it != index.end();
++it)
{
std::cout << it->first << " first found on line " << it->second << std::endl;
}
#endif
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With