首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >STL模板和算法的C++实践

STL模板和算法的C++实践
EN

Code Review用户
提问于 2020-08-10 05:56:20
回答 2查看 318关注 0票数 5

我已经实现了以下的程序,为一个练习的STL模板和算法的作业。我所做的就是实现用于打印空文件、非空文件等的代码。我想知道是否有任何方法使代码更加优化。

注:根据本校的要求(必须遵守):

  • 注释“助手函数”的代码不应该修改。这些是empty_check()split()print_filename()
  • 注释为“可以修改”的函数意味着可以修改该函数中的代码。
  • 不应修改int main()
  • 不允许额外的标题。
  • 没有任何新的复杂类型或模板的定义
  • 不使用除辅助函数以外的其他函数。
  • 不使用lambda表达式。
  • 不使用操作符:
    • . (成员访问)
    • -> (通过指针进行成员访问)
    • * (去引用)

  • 不使用显式迭代(forwhiledo while)或选择(ifswitch?:)语句或运算符。
  • 不使用关键字auto
  • 不使用std::coutstd::cerr或执行文本打印的任何其他函数。我将不得不使用提供的助手函数来完成它。
代码语言:javascript
复制
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <functional>
#include <utility>

using name_file = std::string;
using sizeFile = size_t;
using record_in_file = std::pair<name_file, sizeFile>;
using file_datas = std::map<name_file, sizeFile>;

bool empty_check( //helper function
    const record_in_file& dataRecord,
    bool true_if_its_not
)
{
    sizeFile size;
    std::tie(std::ignore, size) = dataRecord;
    bool result = (size == 0);
    if (true_if_its_not)
    {
        result = !result;
    }
    return result;
}

name_file split(const record_in_file& dataRecord) //helper function
{
    name_file name;
    std::tie(name, std::ignore) = dataRecord;
    return name;
}

void print_filename(const name_file& name1) //helper function
{
    std::cout << " * " << name1 << std::endl;
}

void file_names_print(const file_datas& map1) //can modify 
{
    std::vector<name_file> files;
    std::transform(std::begin(map1), std::end(map1), std::back_inserter(files), split);
    std::for_each(std::begin(files), std::end(files), print_filename);
}

size_t files_un_empty_print(const file_datas& map1) //can modify
{
    std::vector<record_in_file> files;
    std::copy_if(std::begin(map1), std::end(map1), std::back_inserter(files),
        std::bind(empty_check, std::placeholders::_1, true));
    std::vector<name_file> file_names;
    std::transform(std::begin(files), std::end(files), std::back_inserter(file_names),
        split);
    std::for_each(std::begin(file_names), std::end(file_names), print_filename);

    return std::count_if(std::begin(map1), std::end(map1),
        std::bind(empty_check, std::placeholders::_1, true));
}

size_t files_empty_print(const file_datas& map1) //can modify
{
    std::vector<record_in_file> files;
    std::copy_if(std::begin(map1), std::end(map1), std::back_inserter(files),
        std::bind(empty_check, std::placeholders::_1, false));

    std::vector<name_file> file_names;
    std::transform(std::begin(files), std::end(files), std::back_inserter(file_names),
        split);

    std::for_each(std::begin(file_names), std::end(file_names), print_filename);

    return std::count_if(std::begin(map1), std::end(map1),
        std::bind(empty_check, std::placeholders::_1, false));
}

std::tuple<file_datas&>  get_param(file_datas& map1) //can modify
{
    return std::forward_as_tuple<file_datas&>(map1);
}

void empty_removal(file_datas& map1) //can modify
{
    std::vector<record_in_file> files;
    std::copy_if(std::begin(map1), std::end(map1), std::back_inserter(files),
        std::bind(empty_check, std::placeholders::_1, true));

    file_datas n_map{ std::begin(files),std::end(files) };

    std::swap(map1, n_map);
}


int main()
{
    file_datas map = {
        {"readme.txt", 2000},
        {"main.exe", 10000},
        {"save.bak", 0},
        {"library.dll", 1243},
        {"0.res", 121100},
        {"1.res", 121100},
        {"2.res", 115600},
        {"errors.log", 0}
    };

    std::cout << "Files:" << std::endl;
    file_names_print(map);
    std::cout << std::endl;

    std::cout << "Files that are not empty:" << std::endl;
    size_t Count_of_unemptyFiles = files_un_empty_print(map);
    std::cout
        << " There are "
        << Count_of_unemptyFiles
        << " non-empty files.\n"
        << std::endl;

    std::cout << "Files that are empty:" << std::endl;
    size_t Count_of_emptyFiles = files_empty_print(map);
    std::cout
        << " There are "
        << Count_of_emptyFiles
        << " empty files.\n"
        << std::endl;

    std::cout << "Files after removing the empty ones:" << std::endl;
    auto parameters = get_param(map);
    std::apply(empty_removal, parameters);
    file_names_print(map);
}
```
代码语言:javascript
复制
EN

回答 2

Code Review用户

回答已采纳

发布于 2020-08-10 19:45:48

避免创建临时向量

您应该避免创建不必要的临时向量。例如,在file_names_print()中,可以使用嵌套的std::bind()来避免向量files

代码语言:javascript
复制
void file_names_print(const file_datas& map1) //can modify 
{
    std::for_each(std::begin(map1), std::end(map1),
                  std::bind(print_filename, std::bind(split, std::placeholders::_1)));
}

这可以用所有的std::transform() + std::for_each()组合来完成。考虑到您的限制,我看不出如何避免std::copy_if的临时向量。

避免不必要的计数

在使用std::copy_if()的情况下,不再需要调用std::count_if()来计数原始输入中的匹配元素,只需获得临时向量的std::size()即可。例如:

代码语言:javascript
复制
size_t files_un_empty_print(const file_datas& map1) //can modify
{
    std::vector<record_in_file> files;
    std::copy_if(std::begin(map1), std::end(map1), std::back_inserter(files), std::bind(empty_check, std::placeholders::_1, true));
    std::for_each(std::begin(files), std::end(files), std::bind(print_filename, std::bind(split, std::placeholders::_1)));

    return std::size(files);
}

从容器

中删除元素

如果您可以使用C++20,那么只需编写:

代码语言:javascript
复制
void empty_removal(file_datas& map1) //can modify
{
    std::erase_if(map1, std::bind(empty_check, std::placeholders::_1, true));
}

如果您不能使用C++20,那么典型的方法是使用for-loop对匹配的元素调用erase()。当然,你被限制不能这样做,然后你确实必须复制。

直接复制到std::map

empty_removal()中,首先创建文件的std::vector,然后将其转换为映射。可以通过创建一个空的std::map并插入以下内容来避免这种情况:

代码语言:javascript
复制
void empty_removal(file_datas& map1) //can modify
{
    file_datas files;
    std::copy_if(std::begin(map1), std::end(map1),
                std::inserter(files, std::end(files)),
                std::bind(empty_check, std::placeholders::_1, true));

    std::swap(map1, files);
}

关于这些限制的

这可能是一种强迫您使用STL算法的方法,但是有几个缺点。不幸的是,由于您必须将多个迭代器传递给STL算法,并且永远不会将容器作为返回值,因此很难组合多个STL算法。然后,您必须使用中间副本,这是非常低效的。然后,射程基础for循环播放通常更清晰、更高效。在现实世界中,您将能够使用C++提供的所有工具,并选择最适合该任务的工具。您可能会将for-loops、算法和lambda结合在一起。

票数 3
EN

Code Review用户

发布于 2020-08-10 20:07:04

很抱歉,我的评论有点简短,但是,我今天的时间有限。我的评论不像G.Sliepen的那么技术性,而是更多地关注代码风格。

更喜欢一致的名称

查看列表后的前两行,包括:

代码语言:javascript
复制
using name_file = std::string;
using sizeFile = size_t;
...

选择一种风格(two_wordstwoWordsTwoWords等)在你的代码中坚持使用它。

选择提高代码可读性和遵循正确语法

的名称

代码语言:javascript
复制
size_t files_un_empty_print(const file_datas& map1);  // what does "files_un_empty" mean?
void file_names_print(const file_datas& map1);        // what is "map_1"?
bool empty_check(
    const record_in_file& dataRecord,
    bool true_if_its_not // "is_empty" is concise and better communicates the intent
);
void empty_removal(file_datas& map1);  // "remove" or "delete_empty_files" are clearer

关于正确语法的第二点,我认为这可能是无关紧要的挑剔之处,但是,如果您在选择名称时可以使用适当的英语语法,请这样做:

代码语言:javascript
复制
using name_file = std::string;  // "file_name" is a more natural and familiar way to say the same thing
using file_datas = std::map<name_file, sizeFile>;  // the word "data" is plural

您的代码中可能有其他示例,但这应该说明这一点。

使用声明

您创建了一个别名:

代码语言:javascript
复制
using sizeFile = size_t;

然后返回到使用size_t

代码语言:javascript
复制
size_t files_un_empty_print(const file_datas& map1);
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/247705

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档