LINUX.ORG.RU

Flux — C++20 библиотека алгоритмов с другой моделью итераций

 , ,


4

3

Это header-only (~405 KB) C++20 библиотека в духе C++20 Ranges, Python IterTools, итераторов Rust и других, и предоставляет набор функций, в целом эквивалентный C++20 Ranges, но использует немного другую модель итерации, основанную на курсорах, а не итераторах.
Курсоры Flux - это обобщение индексов массивов, в то время как итераторы STL - обобщение указателей массивов.
Возможности:

  • большой выбор алгоритмов и адаптеров последовательностей для создания мощных (?) и эффективных конвейеров данных;
  • более высокая безопасность по сравнению со стандартными итераторами;
  • более простое использование в распространённых случаях, особенно при определении собственных последовательностей и адаптеров;
  • более эффективное выполнение некоторых распространённых операций;
  • совместимость с существующими стандартными библиотечными типами и концептами.

Документация: https://tristanbrindle.com/flux/index.html
Код: https://github.com/tcbrindle/flux
Лицензия: Boost 1.0.
Пример:

constexpr auto result = flux::ints()                        // 0,1,2,3,...
                         .filter(flux::pred::even)          // 0,2,4,6,...
                         .map([](int i) { return i * 2; })  // 0,4,8,12,...
                         .take(3)                           // 0,4,8
                         .sum();                            // 12

static_assert(result == 12);

Он же в Compiler Explorer: https://flux.godbolt.org/z/KKcEbYnTx.


Проект от автора библиотеки NanoRange – C++20 Ranges для C++17.

★★★★★

Последнее исправление: dataman (всего исправлений: 1)
Ответ на: комментарий от Beewek

Переводчик не знает, что переводит :-)

intelfx ★★★★★
()

Надо будет поковырять. А то от плюсовых итераторов у меня «боль в моя дырка задница».

WatchCat ★★★★★
()
Ответ на: комментарий от Beewek

А почему «мощных» под вопросом?

Мне не нравится буквальный перевод слова powerful в этом контексте.
Предлагайте варианты! :)

dataman ★★★★★
() автор топика
Ответ на: комментарий от dataman

Часто вижу «мощные алгоритмы/библиотеки/программы». Что это такое? :)

Три разные вещи. А в ОП — четвёртая.

intelfx ★★★★★
()
Ответ на: комментарий от dataman

Да нормальный перевод. Вот я, например, говорю, что QString из QtCore по сравнению с std::string — мощные (по возможностям).

hobbit ★★★★★
()
Ответ на: комментарий от intelfx

Выразительный

Есть в каком-то словаре?
Например, в словаре недавно умершего Апресяна такого варианта нет.

dataman ★★★★★
() автор топика
Ответ на: комментарий от dataman

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

На русский в контексте айти слова stream и thread переводятся одинаково. Или вот sunny и solar – на русском вообще никак не различимы. И таких примеров полно.

wandrien ★★★
()
Ответ на: комментарий от wandrien

Да, нужно будет как-то абстрагироваться от ватта или лошадиной силы. :)

dataman ★★★★★
() автор топика
Ответ на: комментарий от dataman

По тем же причинам, почему в русском любят использовать «сильный» вместо более специфичного прилагательного.

wandrien ★★★
()

Мне понравился ответ интервьюируемого на канале «C++ Russia».

Типо: «Вы какой версией C++ пользуетесь?»

Ответ: «Да я не знаю. Эндцать лет тому назад прочёл я книжку по C++, и с тех пор фигачу по ней. А версия? Да фиг её знает, что за версия то была!» 😁

sparkie ★★★★★
()
Ответ на: комментарий от sparkie

Значит это максимально некомпетентный ответ. Там между диалектами разница как между разными языками.

C++ 20 лет назад – это вообще ни о чем.

wandrien ★★★
()
Ответ на: комментарий от dataman

Как-бы expressive – это только одна ось измерений: больше буковок или меньше. Тогда как powerful может включать себя сразу несколько таких осей: expressiveness, composibility и др.

Кстати, подумалось, что можно использовать и термин «продвинутых» в данном контексте.

eao197 ★★★★★
()
auto find_first_digit = [](std::string_view line) -> int {
    while (!line.empty()) {
        for (auto const& [str, value] : digits_map) {
            if (line.starts_with(str)) {
                return value;
            }
        }
        line.remove_prefix(1);
    }
    return 0;
};

ой, а почему запиливший это чудо латентный растоман сам не пользуется своим флюксом? Смешно просто.

Lrrr ★★★★★
()
Ответ на: комментарий от wandrien

...это максимально некомпетентный ответ.

С чего бы это?

Если чувака в его работе всё устраивает и большего точно не нужно...

sparkie ★★★★★
()
Ответ на: комментарий от sparkie

Если чувака в его работе всё устраивает и большего точно не нужно…

Чувака может устраивать работа единственным сантехником на деревне, потому что других просто нет, но компетентным сантехником он от этого не станет.

wandrien ★★★
()
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от theNamelessOne

нет конечно, у него там буквально парой строчек ниже

auto part2 = [](std::string_view const input) -> int {
    return flux::split_string(input, '\n')
        .map([](std::string_view const line) -> int {
            return 10 * find_first_digit(line) + find_last_digit(line);
        })
        .sum();
};

очевидно что find_first_digit ему пришлось отлаживать, а для отладки это дерьмище не подходит вообще никак. То есть с его помощью можно реализовывать только простейшую логику, для которой и одного for хватит. Ну и зачем оно тогда вообще нужно?

Lrrr ★★★★★
()
Ответ на: комментарий от Lrrr

Так точно. Это ровно то, о чем я говорил неоднократно:

Цепочечные вызовы только выглядят fancy, когда ты с ними впервые познакомился. А поотлаживав реальный код и поматерившись, ты переписываешь их на скучные циклы, которые дружелюбны к отладке.

wandrien ★★★
()
Ответ на: комментарий от Lrrr

Хотя конкретно в этом случае,

очевидно что

не очевидно.

Возможно, просто первое что в голову пришло, то и написал. Запутанной логики в коде нет, чтобы его долго отлаживать.

wandrien ★★★
()

Курсоры Flux - это обобщение индексов массивов, в то время как итераторы STL - обобщение указателей массивов.

индекс в массиве становится невалидным(индекс указывает не на тот элемент), если массив модифицировать перед этим индексом - удалить или вставить элемент.

указателю же такие модификации жизни не портят. то есть второе - более общий формализм, чем первое

alysnix ★★★
()
Ответ на: комментарий от dataman

Почему-то представился качок, работающий в печатном издании.

wandrien ★★★
()

А можно какой-нибудь пример, где эта декларативщина сильно удобнее итеративщины с for-циклом. Просто вот этот пример высосан из пальца, и проще маленький циклик набросать, чем городить огород, да ещё и со сторонней библиотекой.

seiken ★★★★★
()
Ответ на: комментарий от seiken
#include <flux.hpp>

#include "assert.hpp"
#include <iostream>
#include <vector>

int main()
{
    std::array nums{1, 1, 2, 3, 3, 2, 2};

    // The adjacent_filter adaptor applies the given predicate to each pair
    // of elements in the sequence, and if the predicate returns false then
    // the second element of the pair is discarded
    auto filtered1 = flux::adjacent_filter(nums, std::less{});
    assert(flux::equal(filtered1, std::array{1, 2, 3}));

    // For the common case of removing adjacent equal elements, Flux provides
    // the dedup() function as shorthand for adjacent_filter(std::not_equal_to{})
    auto filtered2 = flux::dedup(nums);
    assert(flux::equal(filtered2, std::array{1, 2, 3, 2}));

    // We can use adjacent_filter with a custom comparator as well
    auto compare = [](auto p1, auto p2) { return p1.first != p2.first; };
    std::pair<int, int> pairs[] = {{1, 2}, {1, 3}, {1, 4}, {2, 5}, {2, 6}};

    auto filtered3 = flux::adjacent_filter(flux::ref(pairs), compare);
    assert(flux::equal(filtered3,
                       std::array{std::pair{1, 2}, std::pair{2, 5}}));
}

Годится?

dataman ★★★★★
() автор топика
Ответ на: комментарий от dataman

А что за задача решается? Просто в этом заборе нихрена не понятно. Функциональщина ради функциональщины?

seiken ★★★★★
()
Ответ на: комментарий от seiken

А что за задача решается?

Там же описано прямо в комментах. Вот самое очевидное, удаление дублирующихся элементов:

    auto filtered2 = flux::dedup(nums);
    assert(flux::equal(filtered2, std::array{1, 2, 3, 2}));
wandrien ★★★
()
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от wandrien

Наверное, поэтому:

If trailing-type is not provided, the return type of operator() is determinted as follows:

If body only consists of a return statement that returns an expression, the return type is the type of that expression after lvalue-to-rvalue conversion, array-to-pointer conversion and function-to-pointer conversion.
Otherwise, the return type is void.
(until C++14)
If trailing-type is not provided, or provided as -> auto, the return type of operator() is automatically deduced.

(since C++14)
dataman ★★★★★
() автор топика
Ответ на: комментарий от Lrrr

Какой неоптимизированный п.ц. Сделайте меня развидеть это. А если там строка несколько метров? Там же на каждой итерации memmove

PPP328 ★★★★★
()
Ответ на: комментарий от wandrien

Хорошо, но это просто реализовали функцию в этой либе, и она вызывается. Я точно так же могу реализовать императивщину, и завернуть её в одну функцию.

seiken ★★★★★
()
Ответ на: комментарий от wandrien
line.remove_prefix(1);

Вы думаете там волшебство?


P.S. Посмотрел сорцы gcc:

       constexpr void
       remove_prefix(size_type __n)
       {
    __glibcxx_assert(this->_M_len >= __n);
    this->_M_str += __n;
    this->_M_len -= __n;
       }

Конкретно у них - двигают только указатель. Но всё равно делать это по одному байту - какой-то кошмар.

P.P.S. Ну и да, мне вот ну крааайне интересно, как себя эта их итерация по одному байту поведет на UTF-8 строках.

PPP328 ★★★★★
()
Последнее исправление: PPP328 (всего исправлений: 3)
Ответ на: комментарий от dataman

Мощный значит в конце месяца счёт за электричество будет мощнее!

a1ba ★★
()
Ответ на: комментарий от PPP328

Какой неоптимизированный п.ц.

А зачем супероптимизировать очередной AoC? Он его, возможно, решил за пару минут, да и дальше пошёл.

dataman ★★★★★
() автор топика
Ответ на: комментарий от PPP328

Конкретно у них - двигают только указатель

Конкретно у них – это в стандарте цпп, где гарантировано constant time.

Но всё равно делать это по одному байту - какой-то кошмар.

У вас есть более продвинутый способ найти в строке цифру?

Ну и да, мне вот ну крааайне интересно, как себя эта их итерация по одному байту поведет на UTF-8 строках.

И какие у вас предположения?

wandrien ★★★
()
Ответ на: комментарий от wandrien

это в стандарте цпп, где гарантировано constant time.

Можно, пожалуйста, ссылочку на пункт для общего развития?

У вас есть более продвинутый способ найти в строке цифру?

Да пожалуйста. Во-первых откусывая по одному байту вы (внезапно) ломаете utf-8 строку. В которой внутри юникодного символа могут (и будут) символы 0x30..0x39. Поэтому надо:

  • Идти не по байтам, а по символам
  • Сначала понять до какой позиции резать, а потом отрезать одним вызовом remove_prefix(n)

И какие у вас предположения?

Поломается к чертям. А если сорцы промотать выше - то там вообще у человека константный массив с «one», «two» и т.д. Т.е. это вообще никак не предназначено для юникода

PPP328 ★★★★★
()
Ответ на: комментарий от PPP328

Поломается к чертям.

При поиске одной валидной utf-последовательности в другой валидной utf-последовательности вторая ломается к херам? А пруфы будут?

Во-первых откусывая по одному байту вы (внезапно) ломаете utf-8 строку. В которой внутри юникодного символа могут (и будут) символы 0x30..0x39.

0x30..0x39 могут быть внутри юникодного символа? А пруфы будут?

Поэтому надо: Идти не по байтам, а по символам

Ненадо.

Поэтому надо: Сначала понять до какой позиции резать, а потом отрезать одним вызовом remove_prefix(n)

Может вы в представленном алгоритме разберётесь сначала?

wandrien ★★★
()
Ответ на: комментарий от dataman

If trailing-type is not provided, or provided as -> auto, the return type of operator() is automatically deduced.

И какой именно тип резолвится automatically deduced type в данном случае?

wandrien ★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.