有条件地逐行读取文件

conditional line-by-line reading from file

现在我有那个代码:

if (minimizator_weighted) {
    while (dataFile >> t >> e >> sigma) { // read data file value by value
        /* some code */
    }
}
else {
    while (dataFile >> t >> e) { // all the same but will not read standard deviation
        /* almost the same code */
    }
}

如您所见,ifelse 流之间的唯一区别是 while 循环的条件。我想知道是否有可能优化该代码段并重新使用代码?如果我能写一些这样的东西就好了:

while ((minimizator_weighted) ? (dataFile >> t >> e >> sigma) : (dataFile >> t >> e)) { ... }

但我不确定这个技巧是否正确...你能给我一些优化建议吗?谢谢!

编辑 这是完整的代码片段

if (minimizator_weighted) {
    while (dataFile >> t >> e >> sigma) { // read data file value by value
        data_set::pt point;
        point.t = t;
        point.e = e;
        point.c_vis = 0.0;
        point.c_invis = 0.0;

        if (std::abs(sigma) <= GSL_SQRT_DBL_MIN) // check for division-by-zero error
            sigma = 1.0;

        point.sigma = sigma;

        set.curve.push_back(point); // store point

        data_numPoints++; // collect some stats
        set.curveAvg += e;
    }   
}   
else {
    while (dataFile >> t >> e) { // all the same but will not read standard deviation
        data_set::pt point;
        point.t = t;
        point.e = e;
        point.c_vis = 0.0;
        point.c_invis = 0.0;

        set.curve.push_back(point);

        data_numPoints++;
        set.curveAvg += e;
    }   
}

添加一个间接级别

bool read_data_line1(istream& dataFile, T& t, E& e, Sig& sigma)
{ return dataFile >> t >> e >> sigma; }

bool read_data_line2(istream& dataFile, T& t, E& e, Sig&)
{ return dataFile >> t >> e; }

auto read_data_line_func = minimizator_weighted ? read_data_line1 : read_data_line2;
while(read_data_line_func(dataFile, t, e, sigma))
{
    data_set::pt point;
    point.t = t;
    point.e = e;
    point.c_vis = 0.0;
    point.c_invis = 0.0;

    if (minimizator_weighted)
    {
      if (std::abs(sigma) <= GSL_SQRT_DBL_MIN) // check for division-by-zero error
        sigma = 1.0;
      point.sigma = sigma;
    }

    set.curve.push_back(point); // store point

    data_numPoints++; // collect some stats
    set.curveAvg += e;
}

我想我会这样表达:

data_set::pt collect_point(std::istream& is, bool minimizator_weighted)
{
    data_set::pt point;
    is >> point.t >> point.e;
    point.c_vis = 0.0;
    point.c_invis = 0.0;
    if (minimizator_weighted) {
        is >> point.sigma;
        point.sigma = std::max(point.sigma, GSL_SQRT_DBL_MIN);
    }
    return point;
}

void test()
{
  int data_numPoints = 0;
  data_set set;

  while (dataFile)
  {
    auto point = collect_point(dataFile, minimizator_weighted);
    auto e = point.e;
    set.curve.push_back(std::move(point)); // store point
    data_numPoints++; // collect some stats
    set.curveAvg += e;
  }
}

您的架构向我们展示了 3 个错误:

  1. sigma 是一些对象未使用的变量
  2. 对象行为由外部变量决定(minimizator_weighted)
  3. 每次访问这些对象之一时都必须使用
  4. minimizator_weighted

这些是基本的设计缺陷,不仅在这里而且在整个程序中都会让你付出代价。不过,修复将需要重新架构。让我向您展示如何做到这一点,您可以决定是继续解决问题还是修复架构。

首先你需要 2 基本类型,第 1st 将与你的 data_set::pt 相匹配已经在没有 sigma 的情况下定义,那么我们将使用 data_set::pt_weighted 扩展它以添加 sigma:

struct pt {
    double t;
    double e;
    double c_vis;
    double c_invis;
};

struct pt_weighted : pt {
    double sigma;
};

现在我们将编写提取运算符,再次从 data_set::pt 的提取运算符开始并扩展 data_set::pt_weighted:

istream& operator>> (istream& lhs, data_set::pt& rhs) {
    rhs.c_vis = 0.0;
    rhs.c_invis = 0.0;

    return lhs >> rhs.t >> rhs.e;
}

istream& operator>> (istream& lhs, data_set::pt_weighted& rhs) {
    lhs >> static_cast<data_set::pt&>(rhs) >> rhs.sigma;

    // check for division-by-zero error
    if(std::abs(rhs.sigma) <= GSL_SQRT_DBL_MIN) rhs.sigma = 1.0;

    return lhs;
}

从这里开始,您需要开始使用模板。首先,您需要将 set.curve 模板化为 data_set::ptdata_set::pt_weighted 的容器,然后您的功能需要更改为:

template <typename T>
void foo() {
    for(T point; dataFile >> point;) {
        set.curve.push_back(point); // store point

        data_numPoints++; // collect some stats
        set.curveAvg += point.e;            
    }
}

如果在运行时之前无法建立 minimizator_weighted,则需要调用 foo,例如:

minimizator_weighted ? foo<data_set::pt_weighted>() : foo<data_set::pt>();