【问题标题】:Accelerated C++ exercise 8-5 solution not clear加速C++练习8-5解法不清楚
【发布时间】:2011-12-18 13:08:38
【问题描述】:

我一直在解决加速 C++ 练习 8-5,我不想错过本书中的任何一个练习。

加速C++练习8-5如下:

重新实现第 7 章中的 gen_sentencexref 函数以使用 输出迭代器,而不是将它们的全部输出放在一个数据中 结构体。通过编写附加的程序来测试这些新版本 将迭代器直接输出到标准输出,并通过存储 结果分别为list <string>map<string, vector<int> >

为了理解这个问题的范围和本书这一部分的当前知识——这个练习是关于通用函数模板和模板中迭代器使用的章节的一部分。之前的练习是实现<algorithm> 库函数的简单版本,例如equal, find, copy, remove_copy_if 等。

如果我理解正确的话,我需要修改xref函数这样它:

  • 使用输出迭代器
  • 将结果存储在map<string, vector<int> >

我尝试将映射迭代器作为back_inserter().begin().end() 传递给此函数,但无法编译它。回答 here 解释原因。

第 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;
}

拆分实现:

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;
}

请帮助了解我缺少什么。

【问题讨论】:

    标签: c++ accelerated-c++


    【解决方案1】:

    我在这里找到了有关练习的更多详细信息: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 )
    

    用法:

    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   )
    

    用法:

    std::ostream_iterator<string> out_str (std::cout, " "); 
    xref(   cin, out_str, find_url   ) ; 
    

    坦率地说,我不得不得出结论,这个问题是不恰当的,特别是他们为xref 指定了新接口的地方:外部参照应该会生成一个地图。但是,在这种情况下,使用输出迭代器意味着使用 std::inserter(map, map.end())。虽然您可以编写代码的编译版本,但这不会达到您的预期,因为map::insert 将简单地忽略任何带有重复键的插入。

    如果外部参照的目标只是将单词链接到它们第一次出现的行号,这仍然可以,但我感觉练习的作者只是错过了这个微妙之处点:)

    这是代码(请注意,我为split 发明了一个愚蠢的实现,因为它既缺失又需要):

    #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;
    }
    

    【讨论】:

    • 感谢您的回答。很抱歉没有发布split 实现。我会把它添加到我的问题中。情况是我不熟悉make_pair函数。稍作搜索发现make_pair仅在书末的图书馆摘要中提及,所以我仍然不清楚作者试图通过这个练习来技术。
    • make_pair 是一个模板方法。如果你有一些类型,比如:std::pair&lt;std::string, int&gt;,它更容易使用:make_pair("hello", 5); 而不是:std::pair&lt;std::string, int&gt;("hello", int),它可以推断出你想要制作什么样的配对。
    • 还有两个笔记。 1) 外部参照函数最初返回的map&lt;string, vector&lt;int&gt; &gt; 包含字符串和 int 向量,每行单词都出现在。 (答案中的附加代码仅打印第一次找到的行字) 2)我应该能够将输出迭代器直接附加到标准输出。要么我不知道该怎么做,要么解决方案应该以不同的方式实施?
    • @user1104456:您应该只期望将输出迭代器附加到标准输出以调用gen_sentence。我假设您对那部分不感兴趣,因为 (a) 它完全从您的问题 (b) 中丢失了它对于输出迭代器来说是微不足道的(它是在我看来,构成挑战的地图
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-10
    • 2021-09-21
    • 1970-01-01
    • 2013-02-28
    • 2017-12-06
    • 2017-07-12
    相关资源
    最近更新 更多