C ++의 함수형 프로그래밍. f (a) (b) (c) 구현
저는 C ++로 함수형 프로그래밍의 기초를 배우고 있습니다. f(a)(b)(c)
반환 할 함수를 만들려고합니다 a + b + c
. f(a)(b)
a + b를 반환하는 함수 를 성공적으로 구현했습니다 . 이에 대한 코드는 다음과 같습니다.
std::function<double(double)> plus2(double a){
return[a](double b){return a + b; };
}
f(a)(b)(c)
이전에 언급했듯이 반환해야하는 함수를 구현하는 방법을 알 수 없습니다 a + b + c
.
2 요소 솔루션을 가져와 다른 람다로 래핑하여 확장하십시오.
당신이 얻을 람다 반환 할 때문에 double
그리고 반환 double
의 '추가 (λ)를, 당신이 할 필요가 반환 람다 그 (람다를 다른 기능으로 현재의 반환 유형을 포장하고 현재에 중첩 된 람다를 추가하는 것입니다 ) :
std::function<std::function<double(double)>(double)> plus3 (double a){
return [a] (double b) {
return [a, b] (double c) {
return a + b + c;
};
};
}
으로 Ðаn는 @ 언급, 당신은 건너 뛸 수
std::function<std::function<double(double)>(double)>
와 함께 얻을auto
:auto plus3 (double a){ return [a] (double b) { return [a, b] (double c) { return a + b + c; }; }; }
더 깊은 중첩 람다를 사용하여 모든 요소 수에 대해이 구조를 확장 할 수 있습니다. 4 가지 요소에 대한 데모 :
auto plus4 (double a){ return [a] (double b) { return [a, b] (double c) { return [a, b, c] (double d) { return a + b + c + d; }; }; }; }
함수 f
가 functor , 즉 operator()
. 한 가지 방법은 다음과 같습니다.
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;
}
템플릿 버전
컴파일러가 유형을 추론 할 수있는 템플릿 버전을 작성할 수도 있습니다. 여기에서 시도해보십시오 .
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;
}
예
이 예에서는 T
궁극적으로 다음으로 해결됩니다 double
.
std::cout << f(1)(2.5)(3.1f)(4) << std::endl;
다음은 *this
from에 대한 참조를 반환하는 약간 다른 접근 방식 operator()
이므로 주위에 복사본이 떠 다니지 않습니다. 상태와 왼쪽 접기를 재귀 적으로 저장하는 펑터의 매우 간단한 구현입니다.
#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);
}
이것은 f(a)(b)(c)
아니라 오히려 curry(f)(a)(b)(c)
. f
각 추가 인수가 다른 인수를 반환 curry
하거나 실제로 함수를 열심히 호출하도록 래핑 합니다. 이것은 C ++ 17이지만 많은 추가 작업을 통해 C ++ 11에서 구현할 수 있습니다.
이것은 함수를 커링하기위한 해결책이 아니라 이진 함수를 접는 해결책이 아니라 질문에서 얻은 인상입니다.
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...);
});
}
};
}
간결함을 위해 참조 전달을 건너 뛰었습니다. 사용 예는 다음과 같습니다.
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
내가 생각할 수있는 가장 간단한 방법 plus3()
은 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); };
}
이것은 처음 두 개의 인수 목록을를 호출하는 데 사용되는 단일 arglist로 결합합니다 plus2()
. 이렇게하면 최소한의 반복으로 기존 코드를 재사용 할 수 있으며 향후 쉽게 확장 할 수 있습니다. plusN()
를 호출하는 람다를 반환하면 plusN-1()
됩니다.이 호출은에 도달 할 때까지 이전 함수로 차례로 전달 plus2()
됩니다. 다음과 같이 사용할 수 있습니다.
int main() {
std::cout << plus2(1)(2) << ' '
<< plus3(1)(2)(3) << '\n';
}
// Output: 3 6
인라인으로 호출하고 있다는 점을 고려할 때이를 함수 템플릿으로 쉽게 변환 할 수 있으므로 추가 인수에 대한 버전을 만들 필요가 없습니다.
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
나는 놀 것이다.
덧셈보다 커리 폴드를하고 싶습니다. 우리는이 문제를 해결할 수도 있고이를 포함하는 문제를 해결할 수도 있습니다.
따라서 먼저 추가 :
auto add = [](auto lhs, auto rhs){ return std::move(lhs)+std::move(rhs); };
그것은 덧셈의 개념을 아주 잘 표현합니다.
이제 접기 :
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;
}
};
시드 값과 T를 취한 다음 연결을 허용합니다.
template<class F, class T>
folder_t<F, T> folder( F fin, T tin ) {
return {std::move(fin), std::move(tin)};
}
이제 우리는 그들을 연결합니다.
auto adder = folder(add, 0);
std::cout << adder(2)(3)(4) << "\n";
folder
다른 작업 에도 사용할 수 있습니다 .
auto append = [](auto vec, auto element){
vec.push_back(std::move(element));
return vec;
};
사용하다:
auto appender = folder(append, std::vector<int>{});
for (int x : appender(1)(2)(3).get())
std::cout << x << "\n";
라이브 예 .
우리는 전화를해야 .get()
하기 때문에 여기에 for(:)
루프가 우리 폴더의를 이해하지 않습니다 operator T()
. 약간의 작업으로 해결할 수 있지만 .get()
더 쉽습니다.
라이브러리 사용에 개방적이라면 Boost의 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);
그런 다음 원하는대로 사용합니다.
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
}
이 모든 대답은 매우 복잡해 보입니다.
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.
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;
}
참고URL : https://stackoverflow.com/questions/43783517/functional-programming-in-c-implementing-fabc
'code' 카테고리의 다른 글
OS X에서 'make'사용 (0) | 2020.11.22 |
---|---|
matplotlib에서 임의의 색상을 생성하는 방법은 무엇입니까? (0) | 2020.11.22 |
ORA-01461 : LONG 열에 삽입하기 위해서만 LONG 값을 바인드 할 수 있습니다-질의시 발생합니다 (0) | 2020.11.22 |
솔루션에서 TFS 연결 제거 (0) | 2020.11.22 |
Swift에서 AVPlayer를 어떻게 반복합니까? (0) | 2020.11.22 |