【问题标题】:How can I return a variable number of containers of various types?如何返回可变数量的各种类型的容器?
【发布时间】:2012-05-01 20:29:16
【问题描述】:

我的数据如下所示:

     token            eps  rank # first line names columns
 Intercept   9.362637e+00     1 # later lines hold data
        A1  -2.395553e-01    30
        G1  -3.864725e-01    50
        T1   1.565497e-01    43
....

不同的文件将具有不同数量的命名列,并且每列中的值类型会因浮点数、整数和字符串而异。

我想编写一个readCols 函数,向它发送列的名称(例如,我可能需要tokenrank 列),它将把指定列中的数据放入适当类型的容器中.

我的问题不在于解析文件,而在于返回包含不同类型的可变数量的容器。例如,我希望将tokenrank 列分别放入vector<string>vector<int> 容器中。这里的问题是我可能想要eps 列(存储在向量中),并且我不想为每种可能的类型组合编写不同的readCols 函数。 (容器的类型对我来说无关紧要。如果我必须只使用vectors,没问题;每个容器包含不同的类型是关键。)

我可能需要一个容纳不同类型的容器来容纳不同类型的容器。看起来Boost.Variant 可能是我想要的解决方案,但我不知道如何告诉解析器我希望每列是哪种类型(我可以制作一个类型名列表之类的东西吗?例如void readCols(string filename, vector<variant<various types of vector>> &data, vector<string> colNames, vector<typename> convertTo))。同样,Boost.Mpl.Vector 可能会解决问题,但我还是不知道如何告诉readCols 每列要如何转换。

我能想到至少两种解决方法:

  1. 使用读入任何容器的模板函数分别读取每一列(container::value_type 允许函数知道如何解析)。我不喜欢这种解决方案,因为文件有时很大(数百万行),因此多次解析它们会花费额外的几分钟(在计算大约需要 30 分钟的程序中,运行时间的百分比可忽略不计;程序将一遍又一遍地跑)。
  2. 将所有列读入字符串容器并在调用上下文而不是解析上下文中重新转换它们。这不会那么糟糕,因为我认为我可以使用std::transformboost::lexical_cast 或 s/t 在一行中进行转换。如果我可以避免2n 的膨胀行,那就太好了(n= 列数,通常为 2 或 3,每列 2 行来声明容器然后转换)。

与完整的通用解决方案相比,第二种解决方法可能需要我付出的努力要少得多;如果是这样的话,我想知道。我想第二种解决方法甚至可能更有效,但我目前主要关心的是易用性。如果我可以编写一个通用的readCols 函数并完成它,那就是我更喜欢的。

【问题讨论】:

  • @jrok 我从来没有,但应该可以。

标签: c++ boost generic-programming boost-mpl boost-variant


【解决方案1】:

当事情变得过于复杂时,我会将问题分解成更小的部分。所以这里有一个建议。

编写一个 CSV 阅读器类,它可以从文件中读取逗号或其他分隔符分隔的值。该类一次读取一行并将该行分成 std​​::string 字段。为了访问字段,您需要实现 getString、getInt、getDouble 等函数来访问字段(按列名或索引)并将它们转换为适当的类型。因此,读者做了一个定义明确的事情并处理有限个基本类型。

然后实现使用 CSV 阅读器的阅读器函数(或类)。这些读取器函数知道列的特定类型以及将它们的值放在哪里——无论是在标量、容器等中。

【讨论】:

  • 读者的想法是有道理的。类似:readerClass reader(file, colNames); while(reader.advance()) { vec1.push_back(getInt("col1")); vec2.push_back(getString("col2")); ... }。主要的缺点是我必须为我读取的每个文件重复这样一个循环;我无法将该样板推入单独的函数中。
  • 你有多少个不同的文件?编写一两个函数后,您应该能够找到共性并将其分解为单独的函数。
  • 为我拥有的每种类型的文件编写不同的函数并不难。难的是编写一个函数来完成所有工作。
  • 当你可以有几个简单的函数时,为什么还要有一个非常复杂的函数?我更喜欢做明确定义的事情的小函数,而不是做很多事情的更大的函数。更容易调试和维护。
  • 我习惯用 perl 编程,其中类型安全不是问题。没有类型安全,这个任务就没有什么不好定义的了。我当然听说过你关于调试和维护的问题。
【解决方案2】:

只要返回值的类型是有限的,例如intdoublestd::string,这样的函数就可以完成这项工作:

using namespace std;
void readCols(string fileName, vector<string> stringCols, 
      vector<string> intCols, vector<string> doubleCols, 
      vector<vector<string> > *stringData, 
      vector<vector<int> > *intData, 
      vector<vector<double> > *doubleData);

(可能足够清楚,但您根据它们的类型列出所需的列名。)

旁观者认为这是否比解决方法更麻烦。

【讨论】:

    猜你喜欢
    • 2015-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 2018-04-12
    相关资源
    最近更新 更多