在C++向量中对每个元素调用函数

63
在 C++ 中,是否有一种方法可以在不使用循环遍历整个向量的情况下对向量的每个元素调用一个函数?类似于 Python 中的“map”。
8个回答

75

已经有几个回答提到了 std::for_each

虽然这些回答回应了你提出的问题,但我想补充一下,至少在我的经验中,std::for_each 是标准算法中最不实用的算法之一。

我更频繁地使用(举一个例子)std::transform,它基本上是 a[i] = f(b[i]);result[i] = f(a[i], b[i]);,而不是 std::for_each。许多人经常使用 std::for_each 打印集合的元素;为此,std::copystd::ostream_iterator 作为目标的结合效果更好。


8
这是我在寻找的答案,“transform”更接近Python中的“map”而不是“for_each”,因为“map”会生成一个输出列表,而“for_each”调用一个函数但会丢弃输出。 - Steve Jessop
为什么不直接使用基于范围的for循环(const auto& item : myCollection)呢?如果您在类或头文件/源文件中有另一个函数,或者使用lambda表达式,这样可以减少代码量。 - KulaGGin
@KulaGGin:主要是因为问题指定了:“……不使用循环运行所有向量元素?”诚然,这可能不是最合理的要求,但是当问题明确要求不使用循环时建议使用循环并不能真正回答问题。 - Jerry Coffin
抱歉,我的错,我错过了那个。我确实看了标题并浏览了文本。 - KulaGGin
请注意:std::transform 不保证按顺序应用...要按顺序将函数应用于序列或应用修改序列元素的函数,请使用 std::for_each - starriet

67

可以使用:std::for_each

#include <algorithm> //std::for_each

void foo(int a) {
    std::cout << a << "\n";
}

std::vector<int> v;

...

std::for_each(v.begin(), v.end(), &foo);

1
使用这种方法(而不是循环),人们是否可以期望获得速度上的提升? - abcd
2
@dbliss,for_each()的实现很可能只是一个循环,而且由于它使用了模板,所以不应该有任何区别。然而,随着C++17的推出,他们可能会引入并行处理,这对你来说完全透明,而且很可能会更快。 - Alexis Wilke
std::for_each(v.begin(), v.end(), &foo); 或者 std::for_each(v.begin(), v.end(), foo); - sanjivgupta

31

不使用lambda表达式,而是使用基于范围的for循环:for(int& n: nums) n++;。更少的代码 - 更容易理解。 - KulaGGin

10

OP提到了Python中的map函数。

实际上,这个Python函数将一个函数应用于列表(或可迭代对象)的每个元素,并返回一个收集所有结果的列表(或可迭代对象)。

换句话说,它做的事情类似于这样:

def f( x ) : 
   """ a function that computes something with x"""
   # code here 
   return y 

input = [ x1, x2, x3, ... ]
output = map( func, input )  

# output is  now [ f(x1), f(x2), f(x3), ...] 

因此,与Python的map最接近的C++标准库等效物实际上是std::transform(来自<algorithm>头文件)。
以下是示例用法:
#include <vector>
#include <algorithm> 
using namespace std;

double f( int x ) { 
   // a function that computes the square of x divided by 2.0 
   return x * x / 2.0 ;
}

int main( ) {
  vector<int> input{ 1, 5, 10 , 20};
  vector<double> output;
  output.resize( input.size() ); // unfortunately this is necessary

  std::transform( input.begin(), input.end(), output.begin(), f );

  // output now contains  { f(1), f(5), f(10), f(20) }
  //                     = { 0.5, 12.5,  50.0, 200.0 } 
  return 0;
}   

我认为这应该是最佳答案。 - gordon_freeman

10

如果你使用的是C++11,有一个更简短的方法:基于范围的for循环。它的目的正是这个。

std::vector<int> v {1,2,3,4,5};

for (int element : v)
    std::cout << element; //prints 12345

当适用时,您也可以对它应用引用和const,或者在类型很长时使用auto。

std::vector<std::vector<int>> v {{1,2,3},{4,5,6}};

for (const auto &vec : v)
{
    for (int element : vec)
        cout << element;

    cout << '\n';
} 

输出:

123
456

这种语法在vs2011中尚未得到支持,例如我的副本就不支持。但是+1。 - ervinbosenbacher
@xebo,啊,这太糟糕了,因为在我看来,这是C++11最好的特性之一。 - chris
对于任何未来的访问者,范围for循环是在Visual Studio 2012中添加的。 - chris
这个回答如何解决问题?在你的代码中,你是如何将一个函数应用于 std::vector 的每个元素的?我只看到了迭代。此外,你的第一个示例复制了元素,而第二个示例通过 const & 传递它们,因此任何修改都是针对副本进行的或根本无法进行修改。 - Alex Bitek
2
@BadDesign,从技术上讲,输出就是应用函数 operator<<(std::cout, element)。我发帖的主要目的是让原帖作者(以及任何未来的访问者)了解这种语法。不想循环的原因并没有说明,可能只是不想把它全部输入。for_each 中也有循环。第二个示例只是一个示例。它表明您可以随意选择使用其中的任何一个,这使人们可以自由地更改它。另外,谁说你需要修改元素才能在其上调用函数呢? - chris

7
使用for_each
// for_each example
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

void myfunction (int i) {
  cout << " " << i;
}

struct myclass {
  void operator() (int i) {cout << " " << i;}
} myobject;

int main () {
  vector<int> myvector;
  myvector.push_back(10);
  myvector.push_back(20);
  myvector.push_back(30);

  cout << "myvector contains:";
  for_each (myvector.begin(), myvector.end(), myfunction);

  // or:
  cout << "\nmyvector contains:";
  for_each (myvector.begin(), myvector.end(), myobject);

  cout << endl;

  return 0;
}

2
您可以使用std::for_each,它接受一对迭代器和一个函数或函数对象。

2

我想分享一下,如果有人喜欢的话,可以使用std::ranges中的等效函数来替代for_eachtransform

std::vector<int> v;

std::ranges::for_each(v,[](const auto& n) {});

const auto squared = v | std::views::transform([](const auto& n) { return n*2; });

在godbolt上运行:https://godbolt.org/z/zYME6b


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