c++ 遍历列表并使用每两个元素作为函数参数

bvjveswy  于 2022-12-15  发布在  其他
关注(0)|答案(3)|浏览(139)
#include <iostream>
#include <functional>
#include <list>
#include <iterator>

int reduce (std::list<int> l,std::function<int(int a,int b)> f,int start){
int sum=start;
 for(auto i1=l.begin();i1!=l.end();++i1){
   auto i2=++i1;
     sum+=f((*i1),(*i2));
     ++i2;
 }

return sum;

}

int main(){

  std::list<int> list{11,4,5,12,6,8,9};

  auto a=[](int a,int b){return a+b+1;};

  int start=-12;

  int o=reduce(list,a,start);

  std::cout<<"Output: "<< o<<std::endl;

}

我必须写一个reduce函数,它的第一个参数是一个整数列表,第二个参数是一个函数,它将把容器中的元素减少到一个元素,第三个参数是给定累加的初始值(在这个例子中是-12)。它应该把传递的容器中的元素减少到一个元素,输出应该是50。
当我在for循环中写入cout以查看输出时,迭代器返回了它们应该返回的元素,但为什么我得到了一个inifite循环,是不是因为++i2行将从容器中出来?写这段代码的更好的方法是什么?我怎样才能解决这个问题,使第二个迭代器不会到达容器之外?当涉及到遍历列表容器时,你有什么建议?多谢了

fxnxkyjh

fxnxkyjh1#

我想你误解了要求你完成的任务。这是我的看法

int reduce(std::list<int> l, std::function<int(int a,int b)> f, int start) {
    for (auto it = l.begin(); it != l.end(); ++it) {
        start = f(*it, start);
    }
    return start;
}

这是reduce的正常数学定义

o0lyfsai

o0lyfsai2#

问题是,你在循环中将i1增加了两次,对于一个奇数元素的列表来说,这将跳过end迭代器,所以你的循环超出了结尾,进入了 undefined behavior
你需要一次取一个元素,但是要做两次,如果其中一个是end迭代器,你需要停止。
例如:

auto i1 = begin(l);
while (true)
{
    if (i1 == end(l))
    {
        // The end of an even-numbered list
        break;
    }
    auto value1 = *i1++;

    if (i1 == end(l))
    {
        // The end of an odd-numbered list
        // Here we skip the last element, which is value1
        break;
    }
    auto value2 = *i1++;

    sum += f(value1, value2);
}

我还建议你考虑一下标准库是如何处理泛型容器的,并接受一个迭代器对,这样你就可以使用任何类型的容器了。

laik7k3q

laik7k3q3#

在你的代码中有太多的++。你不需要任何。你还需要决定如何处理一个有奇数个元素的列表,就像你的例子一样。当元素数是奇数时,以两步递增begin将永远不会使迭代器等于end。在下面的代码中,我决定不调用函数,并在下一个迭代器是end时返回。
我认为添加元素只是一个简化的示例,因为对于那个求和,元素以什么顺序添加并不重要,也没有必要成对添加。

#include <iostream>
#include <functional>
#include <list>
#include <iterator>

int reduce(std::list<int> l,std::function<int(int a,int b)> f,int start){
    int sum=start;
    for(auto it=l.begin(); it!=l.end(); std::advance(it,2)) {
        if (std::next(it) == l.end()) return sum;
        sum += f((*it),(*(std::next(it,1))));     
    }
    return sum;
}

int main(){
  std::list<int> list{11,4,5,12,6,8,9};
  auto a = [](int a,int b){
      std::cout << a << " " << b << "\n"; // some poor-mans-debugging
      return a+b+1;
  };
  int start = -12;
  int o = reduce(list,a,start);
  std::cout << "Output: "<< o << std::endl;    
}

Live Demo
std::list迭代器不是随机访问的,因此it +=2不起作用。+=的等价物是std::advance。它使迭代器前进。std::next不修改迭代器,而只返回下一个迭代器。
代码可以做的更好一点,因为目前它增加相同的迭代器两次。虽然这将是在可读性的代价,也许编译器已经优化了这一点。当有疑问的剖析和研究汇编,但现在我不会担心太多。

相关问题