【问题标题】:Common algorithm for std::list and std::map?std::list 和 std::map 的通用算法?
【发布时间】:2010-12-26 07:18:46
【问题描述】:

我有一个感兴趣的课程(称之为 X)。
我有一个 std::list (称它为 L)。
我有一个函数(称之为 F)。

F(L) 根据检查列表中每个 X 的内部状态的算法返回 L 的子集(一个 std::list)。

我正在向我的应用程序添加一个 std::map (称为 M),我需要定义 F(M) 以与 F(L) 相同的方式运行 - 即也就是说,F(M) 也必须返回一个 std::list,通过检查映射中每个 X 的内部状态来确定。

作为一个自称是懒惰的程序员,我立即看到算法将 [逻辑上] 相同,并且每种数据类型(std::list 和 std::map)都是可迭代的模板。我不想重复使用相同的算法,但我不确定如何继续前进。

一种方法是从 F(M) 中取出 X*(即键值映射中的“值”),将它们放入 std::list,然后下注处理到 F(std::list),传递返回 std::list;回来通过。我看不出这是唯一的方法。

我的问题:我怎样才能在一个地方维护核心算法,但保留迭代序​​列或一对关联容器的值的能力?

谢谢!

【问题讨论】:

    标签: c++ algorithm list stl map


    【解决方案1】:

    首先,除了两者的条件之外,所有的都可以用std::remove_copy_if 完成。尽管名称为remove_copy_if,但不会从原始集合中删除任何内容。我认为如果将其称为filtered_copy 之类的名称,人们会更容易理解它。它将元素从一个集合复制到另一个集合。对于每个元素,它调用一个谓词,当且仅当谓词对该元素返回 false 时,才会复制项目。

    这让您只有一项责任:实现查看每个 X * 的测试函数,并说明是否应该将其排除在您正在制作的副本之外。由于您有一个逻辑要以两种不同的方式应用,因此我会将逻辑封装在类的私有函数中。然后可以通过两种方式将其作为类的operator() 的重载版本提供给外部世界:

    class F { 
        bool do_test(X const *x) const { return x.internal_stuff; }
    public:
        bool operator()(X const *x) const { return do_test(x); }
    
        bool operator()(std::pair<int, X const *> const &p) const { 
            return do_test(p.second);
        }
    };
    

    由于 operator()(X const *) 是对 do_test() 的纯粹复制,您可能希望摆脱它,但 IMO 可能弊大于利。

    无论如何,这会将您的逻辑完全集中在一个地方 (F::do_test)。它还提供了一种简单、一致的语法来创建 list&lt;X *&gt;std::map&lt;int, X *&gt; 的过滤副本:

    std::list<X *> result;   
    std::remove_copy_if(coll.begin(), coll.end(), std:back_inserter(result), F());
    

    最后一点:std::list 可能是现存最过度使用的集合。虽然它确实有它的用途,但它们确实非常罕见。 std::vectorstd::deque 通常非常更好。

    【讨论】:

    • 我喜欢这个,因为函子真的很简洁。与 Mic 和 Anon 的想法相同。但我觉得最优雅。谢谢!
    • @Chris - 我同意,我不知道 remove_copy_if 自己的行为(奇怪的命名),肯定会将它添加到我自己的武器库中:)。
    【解决方案2】:

    怎么样:

    template<typename T>
    struct func {
        std::list<T>& r;
        func(std::list<T>& r_) : r(r_) {}
    
        bool algorithm(const T& t) {
            return t<5; // obviously meant to be replaced :)
        }
    
        void operator()(const T& t) {
            if (algorithm(t)) r.push_back(t);
        }
    
        void operator()(const std::pair<int, T>& t) {
            if (algorithm(t.second)) r.push_back(t.second);
        }
    
    };
    
    template<typename T, typename ForwardIterator>
    std::list<T> subset(ForwardIterator begin, ForwardIterator end) {
        std::list<T> r;
        std::for_each(begin, end, func<T>(r));
        return r;
    }
    

    【讨论】:

      【解决方案3】:

      您可能是对的,您建议的方法不是唯一的解决方案,但它可能是最容易正确编写和理解的方法。如果您正在编写生产代码,我肯定会从那里开始。分析代码并仅在需要时变得更高级。

      在探索其他选项时,您可以查看boost::bind。当我不久前尝试做类似的事情时,我收到了this answer。而且我觉得std::tr1::bind基本是一样的,所以如果你没有Boost应该可以用TR1版本代替。

      【讨论】:

        【解决方案4】:

        编写一个函数,接受两个前向迭代器作为它的参数(开始和结束),然后该函数只测试迭代器的值,如果它通过测试,将其添加到列表中,递增迭代器,并测试它没有' 没有到达终点(如果到达则中断。)

        然后您只需调用该函数并将集合的 begin() 和 end() 迭代器传递给它。

        【讨论】:

        【解决方案5】:

        一种解决方案是将算法从这两个函数中移出,这些函数只需遍历它们的容器,并调用算法函数来确定特定项目是否属于返回列表。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-03-24
          • 2017-08-13
          • 2018-12-16
          • 2019-07-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多