【问题标题】:std::transform how to not return (and not throw), just skip?std::transform 如何不返回(也不抛出),只是跳过?
【发布时间】:2011-12-04 23:09:57
【问题描述】:

我想显示文件夹内容显示(没有任何文件夹系统)所以有一个 std::set<string_file_names>std::strings 在它和一些给定的路径到某个目录,我们想像在普通 fs 中搜索文件夹内容。所以有一套:

    set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");

然后搜索字符串"bin/obj/Debug/",我们想要得到 3 个项目 - 文件夹,2 个文件:

CloudServerPrototype/
vc100.idb
vc100.pdb

但我们也得到一个空行。如果没有找到项目,如何获取它以及如何抛出错误?

完整代码:

#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <iterator>

using namespace std;

struct get_pertinent_part
{
    const std::string given_string;

    get_pertinent_part(const std::string& s)
        :given_string(s)
    {
    }

    std::string operator()(const std::string& s)
    {
        std::string::size_type first = 0;

        if (s.find(given_string) == 0)
        {
            first = given_string.length();
        }
        else
        {
            return "";
        }

        std::string::size_type count = std::string::npos;
        std::string::size_type pos = s.find_last_of("/");
        if (pos != std::string::npos && pos > first)
        {
            count = pos + 1 - first;
        }

        return s.substr(first, count);
    }
};

void directory_listning_without_directories_demo()
{
    set<string> output;
    set<string> demo_set;

    demo_set.insert("file1");
    demo_set.insert("file2");
    demo_set.insert("folder/file1");
    demo_set.insert("folder/file2");
    demo_set.insert("folder/folder/file1");
    demo_set.insert("folder/folder/file2");
    demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
    demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
    demo_set.insert("bin/obj/Debug/vc100.idb");
    demo_set.insert("bin/obj/Debug/vc100.pdb");


    std::transform(demo_set.begin(),
        demo_set.end(),
        std::inserter(output, output.end()),
        get_pertinent_part("bin/obj/Debug/"));

    std::copy(output.begin(),
        output.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"));
}

int main()
{
    directory_listning_without_directories_demo();
    cin.get();
    return 0;
}

基于this grate answer的代码示例。

【问题讨论】:

  • output.erase(""); 怎么样?
  • 嘿!我认识that code
  • 为什么不先用filter再transform?

标签: c++ string search set transform


【解决方案1】:

由于没有 transform_if,正确的方法是首先将具有非零长度 get_pertinent_part 结果的路径复制_if 到另一个容器中,然后在该新容器上运行转换。

或者,您可以编写类似 transform_if 的东西,将转换函数的结果与谓词进行比较。这是我未经测试的镜头:

template <class InIt, class OutIt, class UnaryFunction, class Predicate>
void transform_if_value(InIt first, InIt last, OutIt out, UnaryFunction fn, Predicate pred)
{
    for ( ; first != last; ++ first)
    {
        auto val = fn(*first);
        if (pred(val))
            *out++ = val;
    }
}

然后你可以像这样使用它

transform_if_value(
    demo_set.begin(),
    demo_set.end(),
    std::inserter(output, output.begin()),
    get_pertinent_part("bin/obj/Debug/"),
    [](const std::string& s) {return !s.empty();});

要获得更酷的语法,请查看 boost 的 Range 适配器:

boost::copy(
    demo_set | boost::adaptors::transformed(get_pertinent_part("bin/obj/Debug/"))
             | boost::adaptors::filtered([](const std::string& s) {return !s.empty();}),
    std::inserter(output, output.begin()));

【讨论】:

    【解决方案2】:

    您可以使用std::accumulate(不是来自&lt;algorithm&gt;,而是来自&lt;numeric&gt;)来累积匹配到您的输出中。

    std::set<string> const&
    operator()(std::set<string> const& out, std::string const& candidate) const
    {
        if(/* same logic to check if candidate matches */) {
            out.insert(/* insert processed/transformed path */);
        }
        return out;
    }
    

    调用为:

    std::accumulate(
        demo_set.begin(), demo_set.end()
        , std::ref(output)
        , get_pertinent_path("bin/obj/Debug/") );
    

    注意std::ref 的使用(来自&lt;functional&gt;)。您不希望输出集被移动(加上对std::accumulate 的调用结果被忽略,因此不会看到任何更改)。或者,如果您不想依赖 std::ref,您可以使用指针。

    编辑:嘿,重新审视这个想法,这可以说并不比使用std::for_each 并将对输出的引用传递给函子的构造函数更好。 YMMV。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-24
      • 2014-01-04
      • 1970-01-01
      • 1970-01-01
      • 2019-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多