代码之家  ›  专栏  ›  技术社区  ›  Chris Tonkinson

std::list和std::map的通用算法?

  •  4
  • Chris Tonkinson  · 技术社区  · 15 年前

    我有一个兴趣班(叫它X)。
    我有一个std::list<x*>(称之为l)。
    我有一个函数(称之为f)。

    f(l)根据检查列表中每个x的内部状态的算法返回l的子集(std::list<x*>)。

    我在应用程序中添加了一个std::map<int,x*>(称之为m),我需要定义f(m)以与f(l)相同的方式操作-也就是说,f(m)必须返回一个std::list<x*>,通过检查映射中每个x的内部状态来确定。

    作为一个自我描述的懒惰的程序员,我立刻发现算法[逻辑上]是相同的,并且每个数据类型(std::list和std::map)都是不可重复的模板。我不想重复使用同一个算法,但我不确定如何继续。

    一种方法是将x*从f(m)(即键值映射中的“值”)中取出来,将它们放入std::list<x*>,然后将处理过程推到f(std::list<x*>)上,然后返回std::list<x*>。我看不出这是唯一的办法。

    我的问题是:我如何在一个地方维护核心算法,但保留对关联容器的序列或值进行迭代的能力?

    谢谢!

    5 回复  |  直到 15 年前
        1
  •  7
  •   Jerry Coffin    15 年前

    首先,除了两者的条件外,其他条件都可以用 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() 你可能想摆脱它,但在我看来,这可能弊大于利。

    无论如何,这将使您的逻辑完全停留在一个地方( F::do_test )它还提供了一个简单、一致的语法,用于创建 list<X *> 或A std::map<int, X *> :

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

    最后请注意: std::list 可能是现存最过度使用的集合。虽然它确实有它的用途,但它们确实非常罕见。 std::vector std::deque 非常 经常更好。

        2
  •  2
  •   Bill Prin    15 年前

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

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

        3
  •  1
  •   Mic    15 年前

    比如:

    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;
    }
    
        4
  •  0
  •   Anon.    15 年前

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

        5
  •  0
  •   Community gview    7 年前

    您可能是对的,您建议的方法不是唯一的解决方案,但它可能是最容易正确编写和理解的。如果你在写产品代码,我肯定会从那里开始。对代码进行概要分析,只有在需要的时候才会变得更漂亮。

    在探索其他选项时,您可以看看 boost::bind . 我收到 this answer 当我试图做类似的事情的时候。我想 std::tr1::bind 基本上是一样的,所以如果你没有增强,你应该能够替换TR1版本。