【问题标题】:conditional line-by-line reading from file有条件地逐行读取文件
【发布时间】:2017-04-28 16:59:16
【问题描述】:

现在我有那个代码:

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 循环的条件。我想知道是否有可能优化该 sn-p 并重用代码?如果我能写一些就太好了:

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

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

编辑 这是完整的代码sn-p

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;
    }   
}

【问题讨论】:

  • 您要求我们判断您是否可以使用“一些代码”代替“几乎相同的代码”。由于我们还没有看到这两个块中的代码,我可以自信地说没有人能够为您提供一个好的答案。
  • 是文件中的 sigma 值吗?如果是这样,您将不得不阅读并有条件地忽略它。
  • @RichardHodges 没有。在第一种情况下(加权)数据文件由每行 3 个 double 值的行组成。在第二种情况下(未加权)数据文件由每行有 2 个double 值的行组成。你可以看到我在阅读这些文件时没有忽略
  • 不,此代码不应合并。我建议因为只有data_set::ptminimizator_weighted 版本使用sigma,所以您创建2 个对象,一个带有sigma 成员,一个没有。为每个编写提取运算符,然后模板化此循环所在的函数,并模板化set.curve,从而正确地简化循环。
  • @JonathanMee 我可以编辑循环体并添加额外的if-statement 来跟踪加权和未加权案例。如果我们正在处理加权案例,我将存储 sigma 值。在其他情况下,我将简单地忽略 sigma 值(就像我的其他代码一样)

标签: c++ while-loop conditional


【解决方案1】:

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

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

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

首先您需要 2 个基本类型,第 1st 将与您已经定义但没有 sigmadata_set::pt 匹配,然后我们将用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>();

【讨论】:

  • 几乎同意你的观点,但是 ew - 那些模板化的函数调用。可能将目标 pt/pt_weighted 作为参考参数或标记,或者可能是 SetType::typename point_type
  • 很好的例子,谢谢!我对 CPP 和面向对象编程有点陌生,所以 C 范式仍然足够强大
  • @RichardHodges 嗯...如果 minimzator_weighted 直到运行时才确定,您将如何处理这个问题?也许我误解了你的建议,但我看不出它们如何帮助我绕过模板化函数调用。
  • @JonathanMee 我想我是说我更喜欢 ADL 而不是明确专门的模板函数。使用 ADL,如果您选择添加新类型,则无需执行精密逻辑手术。
  • @RichardHodges 我觉得我无法从 600 个字符或更少的字符中理解您的观点。我在这里提出了一个问题:stackoverflow.com/q/41128744/2642059,以防你想澄清一下。
【解决方案2】:

我想我会这样表达:

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】:

    添加一个间接级别

    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;
    }
    

    【讨论】:

    • 哇,谢谢,有趣的解决方案!但是为什么我们不能直接使用三元运算符呢?
    • 不能说我是粉丝。将if-statement 放在循环中似乎更糟。
    • @JonathanMee 我把这些担心留给了我机器上的分支预测器。有时你必须务实。
    • @DrobotViktor 可以直接使用三元表达式,只要函数签名匹配即可。但最终结果是一样的(我个人觉得它更具可读性)
    • 你为什么使用auto类型而不是bool
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 2014-06-21
    • 2012-06-13
    • 2016-11-22
    • 1970-01-01
    相关资源
    最近更新 更多