У меня есть следующий код. Можете ли вы объяснить мне, как это работает?
template<typename Function, typename... Arguments>
auto curry(Function func, Arguments... args) {
return [=](auto... rest) {
return func(args..., rest...);
};
}
int main() {
auto add = [](auto x, auto y) {
return x + y;
};
auto add4 = curry(add, 4);
std::cout << add4(3) << '\n'; //output: 7. (Ok)
}
Во-первых, вы должны знать, что Выделка или, в вашем вопросе, это конкретно случай частичное применение (с которым связано карри, но немного другое).
В основном это означает сокращение функция с определенным количеством параметров для создания другая функция с некоторыми фиксированными значениями параметра.
Я ваш пример, оригинальная функция add(x,y)
который имеет два параметра Икс а также Y. Вы уменьшить Арность функции добавить, установив Икс до 4 и создать сокращенную функцию add4(y)
такие как
add4 (y) = add (x, y) где х = 4
Теперь, как это достигается в вашем коде C ++? С помощью вариационные шаблоны, вариационные функции а также лямбда-функции.
Лямбда-функции находятся в C ++, так как C ++ 11. По сути, это анонимная функция, созданная на лету, которая может храниться в переменной. Вы создаете add
как лямбда в main()
:
auto add = [](auto x, auto y) {
return x + y;
};
Один признает лямбда по шаблону [] (list-of-arguments) {function-body};
Обратите внимание []
не всегда пусто, см. «захват переменных» там, мы вернемся к этому позже.
Теперь цель функции карри состоит в том, чтобы взять одну из этих функций лямбда-выражения func
и определенное количество значений в качестве аргументов, и определить новая функция от назначение значения, по порядку, к первым аргументам func
,
«определенное количество аргументов«Механизм разрешен вариационный шаблон аргумент Arguments... args
который позволяет вызывать шаблон с любым количеством типов в качестве параметров шаблона (если они известны как время компиляции). Так что в нашем случае переданный аргумент равен 4, поэтому Arguments... args
будет заменен на int
и случай curry
примет в качестве параметров лямбда и int
,
Если мы посмотрим на код curry
мы видим, что это только сама лямбда-функция, (это переменная функция, как printf()
) единственная цель которого состоит в объединении аргументов, значение которых уже зафиксировано при создании экземпляра шаблона (args...
) и те, чье значение передается в качестве аргументов функции карри (rest...
).
Знак [=] является специальным захватить для лямбда-функции, которая позволяет использовать все локальные переменные в теле функции (здесь она позволяет использовать args
).
Итак, чтобы подвести итог, вот что происходит в вашей основной функции:
add
который содержит лямбдаcurry(add,4)
Вы создаете curry
шаблон.
Function
это тип add
(лямбда принимает два int
и возвращая int
)4
т.е. int
Инстанцированный curry
функция выглядит так
curry( (int,int)->(int) func, int arg){
return [=](auto... rest) {return func(arg, rest...);};
}
Обратите внимание, что вы никогда не видите эти типы из-за auto
и вычет типа шаблона.
Затем вы вызываете этот экземпляр с func
знак равно add
а также arg
= 4
Вы сохраняете результат этого создания в add4
который теперь лямбда принимает один параметр. Вы можете позвонить add4
с 3 в качестве аргумента (rest...
3), который затем позвонит add(4,3)
и вернуться 7.
Обратите внимание, что технически вы можете попытаться позвонить add4
с более чем одним аргументом, так как функция карри является переменной функцией. Компилятор потерпит неудачу только тогда, когда обнаружит, что у него нет места для этих дополнительных аргументов при вызове add
(видеть это Вот)
Других решений пока нет …