【问题标题】:Is there a simple way of refactoring this code?有没有一种简单的方法来重构这段代码?
【发布时间】:2022-11-10 01:34:01
【问题描述】:

我有一个具有非常相似的重复代码的函数。我喜欢重构它,但不想要任何复杂的映射代码。

该代码基本上过滤掉表中的列。我通过让比较语句具有简单类型使这个示例变得简单,但真正的比较可能更复杂。

我希望可能有一些模板或 lambda 技术可以做到这一点。

vector<MyRecord*>& MyDb::Find(bool* field1, std::string * field2, int* field3)
{
    std::vector<MyRecord*>::iterator iter;
    filterList_.clear();
    std::copy(list_.begin(), list_.end(), back_inserter(filterList_));

    if (field1)
    {
        iter = filterList_.begin();
        while (iter != filterList_.end())
        {
            MyRecord* rec = *iter;
            if (rec->field1 != *field1)
            {
                filterList_.erase(iter);
                continue;
            }
            iter++;
        }
    }

    if (field2)
    {
        iter = filterList_.begin();
        while (iter != filterList_.end())
        {
            MyRecord* rec = *iter;

            if (rec->field2 != *field2)
            {
                filterList_.erase(iter);
                continue;
            }
            iter++;
        }
    }

    if (field3)
    {
        iter = filterList_.begin();
        while (iter != filterList_.end())
        {
            MyRecord* rec = *iter;

            if (rec->field3 != *field3)
            {
                filterList_.erase(iter);
                continue;
            }
            iter++;
        }
    }
    return filterList_;
}

更新: 以防万一有人好奇,这是我的最终代码。再次感谢大家。很容易理解和维护。

vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3)
{
    auto compare = [&](MyRecord* rec) {
        bool add = true;
        if (field1 && rec->field1 != *field1) {
            add = false;
        }
        if (field2 && rec->field2 != *field2) {
            add = false;
        }
        if (field3 && rec->field3 != *field3) {
            add = false;
        }
        return add;
    };

    filterList_.clear();

    std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_), compare);
    return filterList_;
}

【问题讨论】:

  • if ((field1 &amp;&amp; rec-&gt;field1 != *field1)) || (field2 &amp;&amp; rec-&gt;field2 != *field2) || (field3 &amp;&amp; rec-&gt;field3 != *field3) { ...}。预先删除if (field1) 和其他检查,运行循环一次,同时检查所有三个字段。
  • 只是建议-而不是创建完整副本然后删除元素,我只会将原始列表中需要的元素复制到过滤后的元素中
  • @IgorTandetnik 就像我说的那样,我使这个示例变得简单,实际代码比这多列和不同的数据类型。我想避免有一个巨大的 if 语句。无论如何,我可能最终会这样做。
  • 如果模板/lambda 失败,您总是可以求助于使用宏
  • 我相信是这样。所有列都是可选的。

标签: c++ algorithm templates refactoring c++-standard-library


【解决方案1】:

您可以使用std::copy_if(因为您已经/将要复制一份)

vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3){
  filterList_.clear();
  std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_),[&](MyRecord* rec){
    // whatever condition you want.
    return field3 && rec->field3 != *field3;
  });
  return filterList_;
}

【讨论】:

    【解决方案2】:

    有没有一种简单的方法来重构这段代码?

    据我了解您的算法/意图,使用 std::erase_if () 您可以替换整个 while 循环,如下所示 (Demo code):

    #include <vector> // std::erase_if
    
    std::vector<MyRecord*> // return by copy as filterList_ is local to function scope
    Find(bool* field1 = nullptr, std::string* field2 = nullptr, int* field3 = nullptr)
    {
        std::vector<MyRecord*> filterList_{ list_ }; // copy of original
        const auto erased = std::erase_if(filterList_, [=](MyRecord* record) { 
            return record 
                && ((field1 && record->field1 != *field1)
                || (field2 && record->field2 != *field2)
                || (field3 && record->field3 != *field3));
            }
        );
        return filterList_;
    }
    

    如果不支持 C++20,或者您可以使用 erase–remove idiom,这实际上是在 std::erase_if 的底层发生的。

    【讨论】:

    • 感谢 JeJo 和其他所有人。我认为您的答案是与我正在寻找的最相关的答案。我会做一点改变。我将使用 find_if 并添加到列表中而不是删除。
    • @tadpole 我认为另一个答案是您想要的更多,因为您使用的是副本。
    猜你喜欢
    • 2018-07-27
    • 1970-01-01
    • 2012-06-16
    • 1970-01-01
    • 1970-01-01
    • 2019-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多