Reputation: 1068
I have been getting into the basics of functional programming with C++. I am trying to make a function f(a)(b)(c)
that will return a + b + c
. I successfully implemented the function f(a)(b)
which returns a + b. Here is the code for it:
std::function<double(double)> plus2(double a){
return[a](double b){return a + b; };
}
I just cannot figure out how to implement the function f(a)(b)(c)
which as I previously stated should return a + b + c
.
Upvotes: 66
Views: 10809
Reputation: 7017
You can do it by having your function f
return a functor, i.e., an object that implements operator()
. Here is one way to do it:
struct sum
{
double val;
sum(double a) : val(a) {}
sum operator()(double a) { return val + a; }
operator double() const { return val; }
};
sum f(double a)
{
return a;
}
int main()
{
std::cout << f(1)(2)(3)(4) << std::endl;
}
You can even write a templated version that will let the compiler deduce the type. Try it here.
template <class T>
struct sum
{
T val;
sum(T a) : val(a) {}
template <class T2>
auto operator()(T2 a) -> sum<decltype(val + a)> { return val + a; }
operator T() const { return val; }
};
template <class T>
sum<T> f(T a)
{
return a;
}
In this example, T
will ultimately resolve to double
:
std::cout << f(1)(2.5)(3.1f)(4) << std::endl;
Upvotes: 115
Reputation: 56577
Here is a slightly different approach, which returns a reference to *this
from operator()
, so you don't have any copies floating around. It is a very simple implementation of a functor which stores state and left-folds recursively on itself:
#include <iostream>
template<typename T>
class Sum
{
T x_{};
public:
Sum& operator()(T x)
{
x_ += x;
return *this;
}
operator T() const
{
return x_;
}
};
int main()
{
Sum<int> s;
std::cout << s(1)(2)(3);
}
Upvotes: 30
Reputation: 303897
This isn't f(a)(b)(c)
but rather curry(f)(a)(b)(c)
. We wrap f
such that each additional argument either returns another curry
or actually invokes the function eagerly. This is C++17, but can be implemented in C++11 with a bunch of extra work.
Note that this is a solution for currying a function - which is the impression that I got from the question - and not a solution for folding over a binary function.
template <class F>
auto curry(F f) {
return [f](auto... args) -> decltype(auto) {
if constexpr(std::is_invocable<F&, decltype(args)...>{}) {
return std::invoke(f, args...);
}
else {
return curry([=](auto... new_args)
-> decltype(std::invoke(f, args..., new_args...))
{
return std::invoke(f, args..., new_args...);
});
}
};
}
I've skipped forwarding references for brevity. Example usage would be:
int add(int a, int b, int c) { return a+b+c; }
curry(add)(1,2,2); // 5
curry(add)(1)(2)(2); // also 5
curry(add)(1, 2)(2); // still the 5th
curry(add)()()(1,2,2); // FIVE
auto f = curry(add)(1,2);
f(2); // i plead the 5th
Upvotes: 15
Reputation: 2790
All these answers seem terribly complicated.
auto f = [] (double a) {
return [=] (double b) {
return [=] (double c) {
return a + b + c;
};
};
};
does exactly what you want, and it works in C++11, unlike many or perhaps most other answers here.
Note that it does not use std::function
which incurs a performance penalty, and indeed, it can likely be inlined in many cases.
Upvotes: 4
Reputation: 275906
I'm going to play.
You want to do a curried fold over addition. We could solve this one problem, or we could solve a class of problems that include this.
So, first, addition:
auto add = [](auto lhs, auto rhs){ return std::move(lhs)+std::move(rhs); };
That expresses the concept of addition pretty well.
Now, folding:
template<class F, class T>
struct folder_t {
F f;
T t;
folder_t( F fin, T tin ):
f(std::move(fin)),
t(std::move(tin))
{}
template<class Lhs, class Rhs>
folder_t( F fin, Lhs&&lhs, Rhs&&rhs):
f(std::move(fin)),
t(
f(std::forward<Lhs>(lhs), std::forward<Rhs>(rhs))
)
{}
template<class U>
folder_t<F, std::result_of_t<F&(T, U)>> operator()( U&& u )&&{
return {std::move(f), std::move(t), std::forward<U>(u)};
}
template<class U>
folder_t<F, std::result_of_t<F&(T const&, U)>> operator()( U&& u )const&{
return {f, t, std::forward<U>(u)};
}
operator T()&&{
return std::move(t);
}
operator T() const&{
return t;
}
};
It takes a seed value and a T, then permits chaining.
template<class F, class T>
folder_t<F, T> folder( F fin, T tin ) {
return {std::move(fin), std::move(tin)};
}
Now we connect them.
auto adder = folder(add, 0);
std::cout << adder(2)(3)(4) << "\n";
We can also use folder
for other operations:
auto append = [](auto vec, auto element){
vec.push_back(std::move(element));
return vec;
};
Use:
auto appender = folder(append, std::vector<int>{});
for (int x : appender(1)(2)(3).get())
std::cout << x << "\n";
We have to call .get()
here because for(:)
loops doesn't understand our folder's operator T()
. We can fix that with a bit of work, but .get()
is easier.
Upvotes: 11
Reputation: 501
Here is a state pattern singleton inspired approach using operator()
to change state.
Edit: Exchanged the unnecessary assignment for an initialization.
#include<iostream>
class adder{
private:
adder(double a)val(a){}
double val = 0.0;
static adder* mInstance;
public:
adder operator()(double a){
val += a;
return *this;}
static adder add(double a){
if(mInstance) delete mInstance;
mInstance = new adder(a);
return *mInstance;}
double get(){return val;}
};
adder* adder::mInstance = 0;
int main(){
adder a = adder::add(1.0)(2.0)(1.0);
std::cout<<a.get()<<std::endl;
std::cout<<adder::add(1.0)(2.0)(3.0).get()<<std::endl;
return 0;
}
Upvotes: -8
Reputation: 25377
If you are open to using libraries, this is really easy in Boost's Hana:
double plus4_impl(double a, double b, double c, double d) {
return a + b + c + d;
}
constexpr auto plus4 = boost::hana::curry<4>(plus4_impl);
And then using it is just as you desire:
int main() {
std::cout << plus4(1)(1.0)(3)(4.3f) << '\n';
std::cout << plus4(1, 1.0)(3)(4.3f) << '\n'; // you can also do up to 4 args at a time
}
Upvotes: 10
Reputation: 4339
The simplest way I can think of to do this is to define plus3()
in terms of plus2()
.
std::function<double(double)> plus2(double a){
return[a](double b){return a + b; };
}
auto plus3(double a) {
return [a](double b){ return plus2(a + b); };
}
This combines the first two argument lists into a single arglist, which is used to call plus2()
. Doing so allows us to reuse our pre-existing code with minimal repetition, and can easily be extended in the future; plusN()
just needs to return a lambda that calls plusN-1()
, which will pass the call down to the previous function in turn, until it reaches plus2()
. It can be used like so:
int main() {
std::cout << plus2(1)(2) << ' '
<< plus3(1)(2)(3) << '\n';
}
// Output: 3 6
Considering that we're just calling down in line, we can easily turn this into a function template, which eliminates the need to create versions for additional arguments.
template<int N>
auto plus(double a);
template<int N>
auto plus(double a) {
return [a](double b){ return plus<N - 1>(a + b); };
}
template<>
auto plus<1>(double a) {
return a;
}
int main() {
std::cout << plus<2>(1)(2) << ' '
<< plus<3>(1)(2)(3) << ' '
<< plus<4>(1)(2)(3)(4) << ' '
<< plus<5>(1)(2)(3)(4)(5) << '\n';
}
// Output: 3 6 10 15
See both in action here.
Upvotes: 12
Reputation: 16224
Just take your 2 elements solution and expand it, by wrapping it with another lambda.
Since you want to return a lambda that get a double
and returns a double
s' addition lambda, all you need to do is to wrap your current return type with another function, and add a nested lambda into your current one (a lambda that returns a lambda):
std::function<std::function<double(double)>(double)> plus3 (double a){
return [a] (double b) {
return [a, b] (double c) {
return a + b + c;
};
};
}
As @Ðаn noted, you can skip the std::function<std::function<double(double)>(double)>
and get along with auto
:
auto plus3 (double a){
return [a] (double b) {
return [a, b] (double c) { return a + b + c; };
};
}
You can expand this structure for every number of elements, using deeper nested lambdas. Demonstration for 4 elements:
auto plus4 (double a){
return [a] (double b) {
return [a, b] (double c) {
return [a, b, c] (double d) {
return a + b + c + d;
};
};
};
}
Upvotes: 60