std :: transform как не вернуть (и не бросить), просто пропустить? - PullRequest
3 голосов
/ 11 октября 2011

Я хочу отобразить отображение содержимого папки (не имея никакой системы папок). Итак, имея 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;
}

Пример кода на основе этого решающего ответа.

Ответы [ 2 ]

5 голосов
/ 11 октября 2011

Поскольку отсутствует transform_if, правильный способ сделать это состоит в том, чтобы сначала скопировать, если пути, имеющие ненулевую длину, get_pertinent_part, в другой контейнер, а затем запустить преобразование в этом новом контейнере.

В качестве альтернативы вы можете написать что-то вроде 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:

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 голосов
/ 11 октября 2011

Вы можете использовать std::accumulate (не от <algorithm>, а от <numeric>), чтобы, накапливать совпадений в ваш вывод.

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

Обратите внимание на использование std::ref (из <functional>).Вы не хотите, чтобы выходной набор перемещался (плюс результат вызова std::accumulate игнорируется, поэтому никакие изменения не будут видны).В качестве альтернативы, если вам не нужна зависимость от std::ref, вы можете использовать указатели.

edit: хех, вернемся к этой идее, возможно, это не лучше, чем использование std::for_each и передача ссылки навыход к конструктору функтора.YMMV.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...