01. Introduction
C++는 함수형 프로그래밍을 지원하지만
OOP 중심의 언어이다
C++ 17부터 Lambda Function이 도입되면서
함수형 프로그래밍을 쉽게 사용할 수 있도록 바뀌었다
함수형 프로그래밍의 기본은
함수를 변수처럼 다루는 것으로부터 시작된다
java script의 closure개념과 같은 Function Object를
C++에서 아래와 같이 구현할 수 있다
#include <iostream>
class Plus
{
public:
explicit Plus(int a)
: localVar{ a }
{
}
int operator()(int x) const
{
return localVar + x;
}
private:
int localVar;
};
int main()
{
Plus plus3{ 3 };
Plus plus5{ 5 };
std::cout << plus3(10) << std::endl;
std::cout << plus5(10) << std::endl;
}
02. Lambda Expression
# Lambda Expression vs. Function Object
람다 표현식과 함수 객체는 어셈블리에서
똑같이 동작한다
#include <iostream>
class Plus
{
public:
explicit Plus(int a)
: localVar{ a }
{
}
int operator()(int x) const
{
return localVar + x;
}
private:
int localVar;
};
int main()
{
Plus plus3(3);
int a = plus3(10);
auto lambdaPlus3 = [localvar = 3](int x)
{
return localvar + x;
};
int b = lambdaPlus3(10);
}
# Lambda Expression 기본 형태
captures는 Value, Reference, this
params는 인자
body는 정의(구현)부분이 들어간다
# Captures
captures에서 Value를 넣는 경우도 있지만
큰 오브젝트의 경우는 &(Reference)를 넣는 것이 좋다
[=]로 작성하면, 인자들을 모두 Value로 받고
[&]로 작성하면, 인자들을 모두 &(Reference)로 받는다
인자들을 직접 작성하지 않는다면
자동으로 람다 표현식 외부에서 해당 변수를 찾아준다
들어오는 인자들을 직접 작성하고
각각 어떠한 형태로 받을 것인지
다 작성해주는 것이 가독성과 안정성에서 좋다
03. Lambda This
# 클래스 안에서의 Lambda
람다 표현식 body 안에서
멤버 변수와 멤버 함수를 호출하려면 [&]를 작성해주어야 하고
컴파일러가 자동으로 [this]를 호출한다
실수를 줄이려면 자동으로 되는 [=]과 [&]보다는
직접 [this]로 작성해주는 게 좋다
#include <iostream>
class Cat
{
public:
explicit Cat(int age)
: mAge{ age }
{
}
void speak() const
{
std::cout << "meow" << std::endl;
}
void test() const
{
//auto lambda = [&]()
// {
// std::cout << "lambda" << std::endl;
// std::cout << mAge << std::endl;
// speak();
// };
auto lambda = [this]()
{
std::cout << "lambda" << std::endl;
std::cout << this->mAge << std::endl;
this->speak();
};
lambda();
}
private:
int mAge;
};
int main()
{
Cat kitty{ 1 };
kitty.test();
}
# Lambda Expression과 STL
Lambda Expression은 STL 라이브러리와 함께
사용하면 더욱 효율이 증가된다
대표적인 std::for_each 예시는 아래와 같다
또한, 람다의 이름을 정해 선언해주지 않고
그냥 바로 사용하면 anonymous lambda function이 된다
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> nums{ 1,2,3,4,5 };
auto lambdaAdd10 = [](int& n)
{
n += 10;
};
std::for_each(nums.begin(), nums.end(), lambdaAdd10);
//std::for_each(nums.begin(), nums.end(), [](int& n){ n += 10;});
for (int num : nums)
{
std::cout << num << " ";
}
}
04. Higher Order Functions
# Higher Order Function
함수를 인자로 받거나 함수를 결과로 반환하는 함수를 의미한다
대표적인 Higher Order Function에는
for_each,
erase, remove_if,
sort,
reduce가 있다
# erase, remove_if
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> nums{ 1,2,3,4,5 };
auto filterOdd = [](int& n)
{
return n % 2 == 1;
};
nums.erase(std::remove_if(nums.begin(), nums.end(), filterOdd), nums.end());
for (int num : nums)
{
std::cout << num << " ";
}
}
# sort
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> nums{ 1,2,3,4,5 };
std::sort(nums.begin(), nums.end(), [](int a, int b) { return std::abs(a - 3) < std::abs(b - 3); });
for (int num : nums)
{
std::cout << num << " ";
}
}
# reduce
합계를 구할 때 주로 사용된다
accumulate()보다 reduce()를 사용하는 이유는
reduce()는 C++ 17에 도입되었고 policy를 정의해 줄 수 있고
policy를 통해 parallel programming을 지원해주기 때문이다
#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
int main()
{
std::vector<int> nums{ 1,2,3,4,5 };
auto filterOdd = [](int& n)
{
return n % 2 == 1;
};
int addSum = std::reduce(nums.begin(), nums.end(), 0, [](int a, int b) { return a + b; });
int multiSum = std::reduce(nums.begin(), nums.end(), 1, [](int a, int b) { return a * b; });
std::cout << addSum << " " << multiSum;
}
05. std::function
# 함수 포인터
일반적으로 함수는 호출을 하는 경우가 많지만
함수를 변수처럼 가리키게 하고 싶다면
함수 포인터를 통해 가능하게 할 수 있다
#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
void Function(int i)
{
std::cout << "Function" << std::endl;
}
void runFunction(int i, void(&fnPtr)(int))// 함수 포인터를 이용해 함수의 인자에 함수를 받을 수 있다
{
(*fnPtr)(i);
}
int main()
{
//Function();// 일반적인 호출
void (*fnPtr)(int);// 함수 포인터 생성
fnPtr = Function;
(*fnPtr)(20);
}
# std::function
위의 예시처럼 함수 포인터만 인자로 받는 것이 아닌
함수 객체와 람다도 인자로 받게 하려면
std::function을 이용하면 된다
#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
#include <functional>
class FunctionObj
{
public:
void operator()(int i)
{
std::cout << "Function Obj" << i << std::endl;
}
};
void Function(int i)
{
std::cout << "Function" << std::endl;
}
void runFunction(int i, const std::function<void(int)>& fn)
{
fn(i);
}
int main()
{
void (*fnPtr)(int);
fnPtr = Function;
runFunction(10, fnPtr);// 함수 포인터
FunctionObj functionObj;
runFunction(10, functionObj);// 함수 객체
auto lambdaFn = [](int i) {std::cout << "LambdaFunction" << std::endl; };
runFunction(10, lambdaFn);// 람다 함수
}
# 함수를 변수처럼 사용
함수를 변수처럼
벡터에 여러 함수들을 넣어서 실행시키도록 만들 수도 있다
#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
#include <functional>
class FunctionObj
{
public:
void operator()(int i)
{
std::cout << "Function Obj" << i << std::endl;
}
};
void Function(int i)
{
std::cout << "Function" << std::endl;
}
void runFunction(int i, const std::function<void(int)>& fn)
{
fn(i);
}
void runFunctions(const std::vector<std::function<void(int)>>& functions)
{
int i = 0;
for (const auto& fn : functions)
{
fn(++i);
}
}
int main()
{
void (*fnPtr)(int);
fnPtr = Function;
//runFunction(10, fnPtr);
FunctionObj functionObj;
//runFunction(10, functionObj);
auto lambdaFn = [](int i) {std::cout << "LambdaFunction" << std::endl; };
//runFunction(10, lambdaFn);
std::vector<std::function<void(int)>> functions;
functions.emplace_back(fnPtr);
functions.emplace_back(functionObj);
functions.emplace_back(lambdaFn);
runFunctions(functions);
}
# Functional Programming의 장점
Functional Programming을 이용하는 경우에
함수가 정확하게 정의가 되어있다는 가정하에
input이 들어오면, output이 명확하게 나오고
input과 output 사이에는 어떠한 state 없이
함수로만 연결이 되어있기 때문에 side effect가 없는 코드를 작성할 수 있다
C++은 OOP 중심의 언어이고
performance가 중요해지면서, Data oriented Programming도 자연스럽게 도입되었다
C++ 17부터 lambda가 지원되면서
함수형 프로그래밍을 performance를 헤치지 않는 선에서만 사용한다면
가독성이 좋고 쉽게 사용할 수 있게 적절히 활용하는 것이 중요하다
'C++ > [노코프] C++' 카테고리의 다른 글
[C++ NOTE] Exceptions (0) | 2024.04.03 |
---|---|
[C++ NOTE] Types (0) | 2024.04.01 |
[C++ NOTE] Template (0) | 2024.03.30 |
[C++ NOTE] Smart Pointer (0) | 2024.03.29 |
[C++ NOTE] Inheritance (0) | 2024.03.28 |