访问算法的 lambda 函数中的迭代器导致我出现分段错误

Posted

技术标签:

【中文标题】访问算法的 lambda 函数中的迭代器导致我出现分段错误【英文标题】:access to iterator in lambda function of an algorithm cause me segmentation fault 【发布时间】:2020-02-28 14:30:52 【问题描述】:

我需要按列对表格进行排序。我的表格由单个向量表示。

示例:

col_name A B C

向量:1 2 3 6 5 4 7 8 9

给我桌子:

A B C

1 6 7
2 5 8
3 4 9

对 B 列进行排序后,我需要获得:

A B C

3 4 9
2 5 8
1 6 7

我的代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

int main()


  std::vector<std::string> vec = "1","8","1","2","3","2","3",
                                  "5","5","2","5","6","5","6",
                                  "9","3","3","4","8","3","9";

  std::vector<std::string> rec = "1","1","8","2","2","3","3",
                                  "2","5","5","5","5","6","6",
                                  "3","9","3","4","3","8","9";

  int size = 7;
  int col_idx = 1;


  for(int i = 0; i<3;++i)
    
      if(i==col_idx)
        continue;

      std::sort(vec.begin() + i*size, vec.begin() + (i+1)*size,
                [col_idx, size, i](std::string& s1,std::string& s2)
                       
                         std::cout << s1 << " "
                                   << s2 << " "
                                   << *(&s1 +(col_idx - i)*size) << " "
                                   << *(&s2 +(col_idx - i)*size) << " "
                                   << (*(&s1 +(col_idx - i)*size) < *(&s2 +(col_idx - i)*size)) << std::endl;
                         return *(&s1 +(col_idx - i)*size) < *(&s2 +(col_idx - i)*size);
                       );
    
  std::sort(vec.begin() + col_idx*size, vec.begin() + (col_idx+1)*size);


assert(vec==res);

我有一个分段错误错误:cout 中只出现第一行。

【问题讨论】:

有什么理由不简单地使用std::vector&lt;std::vector&lt;int&gt; &gt; 使用调试器! 如果您的 std::sort 谓词对于概念上简单的事物(对列进行排序)看起来令人费解,那么请退后一步并重新考虑您的方法(您如何构建数据、选择的数据类型等.) 这是一个示例代码。在实际代码中,这允许一些理论上的加速,并且我无法更改整个代码中的所有方法。 能否请您在代码中包含数字的实际输出和预期输出? 【参考方案1】:

老实说,你的方法在我看来相当复杂。它的大部分复杂性是由于您的代码中有行但它们仅隐式存在。使代码中的内容显式不仅有助于提高可读性,而且使代码更容易编写。

假设您对行使用std::array&lt;std::string,3&gt;,那么您的代码可以像这样轻量级:

#include <vector>
#include <array>
#include <algorithm>
#include <iostream>

int main() 
    using row_t = std::array<std::string,3>;
    std::vector<row_t> vec =  "1","8","1","2","3","2","3","5","5","2","5","6";

    std::sort(vec.begin(),vec.end(),[](const row_t& a, const row_t& b)  return a[2] < b[2]; );

    for (const auto& row : vec) 
        for (const auto& e : row) std::cout << e << " ";
        std::cout << '\n';
    

输出:

1 8 1 
2 3 2 
3 5 5 
2 5 6 

好吧,这可能是解决这个问题的好方法,也许这是我应该做的,但我不能通过 2 个月来更改所有代码......

您可以在问题中更清楚地说明要求。我认为,如果您有 10k 行代码依赖于使用平面向量的这个特定问题,那么当不同的数据结构更合适时,那么您将面临比如何对行进行排序更大的问题。总之……

使用平面std::vector 通常不是一个坏主意。我从你的代码中错过的是

template <int stride>
std::string& access_at(std::vector<std::string>& vec,size_t row,size_t col)  
    return vec[ row * stride + col ]; 

template <int stride>
const std::string& access_at(const std::vector<std::string>& vec,size_t row,size_t col)  
    return vec[ row * stride + col ]; 

这让您可以像这样迭代表:

for (size_t i=0;i < vec.size()/3;++i) 
    for (size_t j=0;j<3;++j) 
        std::cout << access_at<3>(vec,i,j) << " ";
    
    std::cout << '\n';        

接下来我将无耻地窃取和修改this answer的代码。基本思想是对索引向量进行排序,而不是直接对向量进行排序:

using index_t = std::vector<size_t>;
template <int stride>
index_t make_sorted_index(const std::vector<std::string>& values,size_t col) 
    index_t index(values.size() / stride);
    std::iota(index.begin(), index.end(), 0);
    std::sort(index.begin(), 
              index.end(), 
              [&values,&col](size_t a, size_t b)  
                  return access_at<stride>(values,a,col) < access_at<stride>(values,b,col); 
               
    );
    return index;

一旦你有了这个,打印排序表的循环只需要一个小的修改:

for (size_t i=0;i < vec.size()/3;++i) 
    for (size_t j=0;j<3;++j) 
        std::cout << access_at<3>(vec,index[i],j) << " ";
    
    std::cout << '\n';        

把所有东西放在一起:

#include <vector>
#include <numeric>
#include <algorithm>
#include <iostream>

template <int stride>
std::string& access_at(std::vector<std::string>& vec,size_t row,size_t col)  return vec[ row * stride + col ]; 
template <int stride>
const std::string& access_at(const std::vector<std::string>& vec,size_t row,size_t col)  return vec[ row * stride + col ]; 

using index_t = std::vector<size_t>;
template <int stride>
index_t make_sorted_index(const std::vector<std::string>& values,size_t col) 
    index_t index(values.size() / stride);
    std::iota(index.begin(), index.end(), 0);
    std::sort(index.begin(), 
              index.end(), 
              [&values,&col](size_t a, size_t b)  return access_at<stride>(values,a,col) < access_at<stride>(values,b,col);  
    );
    return index;



int main() 
    std::vector<std::string> vec =  "1","8","1","2","3","2","3","5","5","2","5","6";
    for (size_t i=0;i < vec.size()/3;++i) 
        for (size_t j=0;j<3;++j) 
            std::cout << access_at<3>(vec,i,j) << " ";
        
        std::cout << '\n';        
    
    std::cout << '\n';
    auto index = make_sorted_index<3>(vec,1);
    for (size_t i=0;i < vec.size()/3;++i) 
        for (size_t j=0;j<3;++j) 
            std::cout << access_at<3>(vec,index[i],j) << " ";
        
        std::cout << '\n';        
    

有输出:

1 8 1 
2 3 2 
3 5 5 
2 5 6 

2 3 2 
3 5 5 
2 5 6 
1 8 1 

如果你真的需要,我会留给你实际复制向量以获得排序的向量。

PS:在上面的第一个版本中,我按照 C 列排序,最后一部分按照要求按照 B 列排序。

PPS:我还是不明白你的代码。我不明白你为什么在谓词中有std::cout,老实说,我不知道你对sort 的调用应该如何实现你想要的。

【讨论】:

好的,这可能是解决这个问题的好方法,也许这是我应该做的,但我不能通过 2 个月来更改所有代码......

以上是关于访问算法的 lambda 函数中的迭代器导致我出现分段错误的主要内容,如果未能解决你的问题,请参考以下文章

在lambda函数中访问变量名

导致 TypeError 的 Lambda 函数:“int”对象不可迭代

《C++Primer(第5版)》第十章笔记

如何确保函数模板的参数是随机访问迭代器?

day 18 lambda表达式

如何通过迭代器访问向量中的嵌套对?