Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accelerated C++ exercise 8-5 solution not clear

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 and xref 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 in list <string> and map<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:

  • Use output iterator
  • Store results in 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.

like image 965
user1104456 Avatar asked Dec 18 '11 13:12

user1104456


1 Answers

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;
}
like image 71
sehe Avatar answered Oct 21 '22 16:10

sehe