“map”函数在STL中的名称是什么?

50

我希望能够像这样写一些东西:

char f(char);
vector<char> bar;
vector<char> foo = map(f, bar);

transform 函数似乎很相似,但它不会自动生成结果集的大小。


我觉得有人太过沉迷于Perl了。我曾经试图在C++中实现类似的东西,但很快就变得非常低效:与C++不同,Perl具有高级优化器,它知道数组、哈希或字符串是什么,并相应地优化对它们的操作。 - Dummy00001
STL有std::map作为集合。你可以在任何其他命名空间中拥有自己的map - MSalters
2
@Dummy: map 函数存在于 Perl、Python 和 Lisp 中,它是一个方便的高级构造,用于简单描述常见操作。 - Paul Nathan
7
这句话的意思是,重要的不是知道STL中map操作的具体名称,而是要知道如何在STL中进行map操作的搜索方法。 :-) - Paul Nathan
现在你要找的是“范围”,它存在于外部库中,并最终会成为语言标准的一部分。 - Kuba hasn't forgotten Monica
5个回答

52

您可以在<iterator>中使用std::back_inserter,不过在前面提供大小更有效率。例如:

string str = "hello world!", result;
transform(str.begin(), str.end(), back_inserter(result), ::toupper);
// result == "HELLO WORLD!"

17
+1 -- 您可以使用std::vector<t>::reserve来消除效率惩罚。 - Billy ONeal

28

在C++11标准生效之前,就有人问过这个问题... 现在我们有了std::transform()作为函数式编程“map”操作的(丑陋的)等价物。以下是如何使用:

auto f(char) -> char; // or if you like: char f(char)
vector<char> bar;
vector<char> foo;
// ... initialize bar somehow ...
std::transform(bar.begin(), bar.end(), std::back_inserter(foo), f);

4
为了使这个工作正常运行,您需要以下观察结果:
  1. 为了使赋值操作高效,map 函数不应该执行任务。相反,它应该将其参数保存在临时对象中(在您的情况下,这将是 class map::result<char(*)(char), vector<char> > 类的实例)。
  2. 这个 map::result 临时对象应该有一个 template <typename T> operator T 转换函数。
  3. map::result 被分配给一个 std::vector<char> 时,这个转换函数是唯一可行的。
  4. 在转换操作符 class map::result<char(*)(char), vector<char> >::operator vector<char> 中,您有输入和返回类型以及映射函数。此时,您可以有效地转换输入。

<edit>

代码

template<typename CONT, typename FUNC>
class mapresult {
    CONT const& in;
    FUNC f;
public:
    template<typename RESULT> RESULT to() const
    {
        RESULT out;
        for (auto const& e : in) { out.push_back(f(e)); }
        return out;
    }
    template<typename RESULT> operator RESULT() const
    {
        return this->to<RESULT>();
    }
    mapresult(CONT const& in, FUNC f) : in(in), f(std::move(f)) { }
};

template<typename CONT, typename FUNC>
auto map(CONT const& in, FUNC f) -> mapresult<CONT, FUNC>
{
    return mapresult<CONT, FUNC>(in, f);
}

使用方式:

using namespace std;
char foo(char c) { return c | ('A' ^ 'a'); }
std::string in = "Test";

int main(int argc, char* argv[])
{
    string out = map(in, &foo);
    cout << out << endl;

    char replace = 'e';
    cout << map(in, [replace](char c){return c == replace ? '?' : c; }).to<string>();
}

1
@JohannesGerer:map函数不知道目标容器。将映射输出存储到std::deque<char>中,只是为了发现它实际上需要在std::vector<char>中使用,这样做是低效的。 - MSalters
一个明显的改进是在调用.reserve(in.size())时使用SFINAE。主要问题是将结果赋值给一个带有重载赋值运算符的类。 - MSalters

3
您可以使用类似以下方式来模仿上面的地图语法。
template<typename T, typename A>
T map(A(*f)(A), T & container) {
    T output;
    std::transform(container.begin(), container.end(), std::back_inserter(output), f);
    return output;
}

1

std::transform函数可以完成任务,但在某些情况下性能不佳。我建议使用while循环并提前保留大小。该函数可以轻松地更改为与字符串或任何可映射的内容一起使用。

template<typename T, typename C>
std::vector<T> map(const std::vector<C> &array, auto iteratee) {
  int index = -1;
  int length = array.size();
  std::vector<T> v(length);
  while(++index < length) {
    v[index] = iteratee(array[index], index);
  }
  return v;
}

调用函数,其中array是您想要映射的std::vector。

auto result = map<int, int>(array, [](int elem, int index) {
  return elem + 10;
});

使用std::transform在1亿个元素上运行map花费了约6.15秒

而while循环版本只花费了约3.90秒


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接