Я уже некоторое время занимаюсь программированием на Clojure (функциональном языке), и мне нужно использовать C ++ для класса. Я пытался использовать некоторые функции, которые мне нравились в Clojure (например, функции высшего порядка, лямбда-выражения, поток аргументов, динамическая типизация и т. Д.), Но я натыкался на некоторые кирпичные стены.

Во-первых, я реализовал функцию get, которая принимает два аргумента:

  1. коллекция (вектор, список, хеш-карта и т. д.) и
  2. индекс

И возвращает элемент по этому индексу.

Я также реализовал функцию conj, которая принимает два аргумента:

  1. коллекция (вектор, список, очередь и т. д.) и
  2. элемент / объект (любого типа коллекция)

И возвращает коллекцию с добавленным элементом. В случае векторов это в основном то же самое, что и push_back.

Теперь я хочу иметь возможность «пересылать» или «передавать» аргументы, используя функции высшего порядка, например:

using std::vector;
vector<double> my_vec;

forward(my_vec,    // take "my_vec"
        conj(0.1), // "push" the value of 0.1 to the back of "my_vec"
        get(0),    // retrieve the first value
        inc);      // increment that value

Это то же самое, что и inc(get(conj(my_vec, 0.1), 0);, но намного (!) Более читабельно.

Возвращаемое значение forward в этом случае должно быть 1.1.

Чтобы функция forward работала, все аргументы после начального аргумента должны быть функциями высшего порядка. То есть они должны работать аналогично следующему:

template<typename Func>
Func get(int i){
  return [i](vector<boost::any> coll)
           -> boost::optional<boost::any> {
             return get(coll, i);
           };
}

Однако компилятор не может определить тип возвращаемой лямбда-функции. Кроме того, я предполагаю, основываясь на моем крайне ограниченном опыте работы с boost::any, что он не сможет преобразовать vector<double> в vector<boost::any>, несмотря на очевидные утверждения boost::any что он может действовать как замена практически любому типу.

Я хочу, чтобы функция get была общей, поэтому я не хочу использовать boost::function<double (vector <double>, int)> или какой-либо подобный специфический набор текста.

Кроме того, я использую boost::optional вместо vector для возврата null_ptr, если индекс, запрошенный от get, выходит за границы.

Вот как выглядит моя функция forward:

template <typename T1>
optional<T1> forward (T1 expr1){
  return expr1;
}
template <typename T1, typename T2>
optional<T1> forward (T1 expr1, T2 expr2){
  return forward(expr2(expr1));
}
template <typename T1, typename T2, typename T3>
optional<T1> forward (T1 expr1, T2 expr2, T3 expr3){
  return forward(expr2(expr1), expr3);
}

и т. д. ...

Есть идеи, как заставить эту функцию forward работать?

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

4
alexandergunnarson 29 Авг 2014 в 19:26
Поиск монадических функций и континуаторов, концепция функционального программирования. Посмотрите, как это реализовано в C++11.
 – 
David G
29 Авг 2014 в 19:33
Какой компилятор и версия?
 – 
Rusan Kax
29 Авг 2014 в 19:43

2 ответа

Лучший ответ

Просто дополнение к ответу Хорстлинга, на самом деле forward можно реализовать намного проще:

template <typename Value>
Value forward(Value v) {
    return v;
}

template <typename Value, typename Func, typename... Funcs>
auto forward(Value v, Func f, Funcs... fs) -> decltype(forward(f(v), fs...)) {
    return forward(f(v), fs...);
}

А C ++ 14 еще больше подслащивает:

template <typename Value>
Value forward(Value v) {
    return v;
}

template <typename Value, typename Func, typename... Funcs>
decltype(auto) forward(Value v, Func f, Funcs... fs) {
    return forward(f(v), fs...);
}

Взгляните на полный код здесь (Coliru, похоже, поддерживает boost)

2
Anton Savin 29 Авг 2014 в 22:09

Вот что я могу придумать:

http://coliru.stacked-crooked.com/a/039905c5deff8dcf

Вместо лямбд я использовал полноценные функторы в трех разных вариантах. Он поддерживает ваш пример и не требует стирания типа или чего-то подобного (например, boost :: any или std :: function).

boost::optional<double> result = forward(my_vec, conj(0.1), get(0), inc);

Кроме того, функция forward реализована как вариативный шаблон, допускающий любое количество функций.

Код не доработан, но, возможно, он может вдохновить.

РЕДАКТИРОВАТЬ: Антон абсолютно прав, моя реализация форварда была излишне сложной. Ссылка выше теперь указывает на его исправленную версию кода.

2
Community 23 Май 2017 в 14:49