위키백과에 따르면 함수형 프로그래밍
은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다.
선언형
은What
,명령형
은How
에 집중하는 것이다.
선언형
은 원하는 결과를 표현하기 위해 코드를 작성한다. 선언형 코드의 좋은 예시는 CSS
!!
body {
font-siez:1rem;
color:skyblue;
}
직관적이다! what에 대해서만 작성할 뿐, how에 대해서는 작성하지 않는다.
선언형 코드
는 원하는 결과값을 선언
하는 것이다.
명령형
은 원하는 결과를 얻기 위해 필요한 지침에 따라 코드를 작성한다.
function spaceToDelimiter(text, delimiter = '❤') {
let result = '';
for (let i = 0; i < text.length; ++i) {
if (text[i] === ' ') {
result += delimiter;
} else {
result += text[i];
}
}
return result
}
명령형 코드
는 결과값에 어떻게 도달
하는 것인가에 대한 작성이다.
위의 명령형 코드를 선언형 코드로 바꾸면 아래와 같다.
function spaceToDelimiter(text, delimiter = '❤') {
return text.replaceAll(' ', delimiter);
}
String.prototype.replaceAll()를 선언하여 사용했는데, 내부 구현은 어떻게 되어있는지 정확히 모른다. 어떻게 사용하는지만 익혀서 선언형 코드로 바로 적용할 수 있다.
순수한 함수
란, 부작용(side-effect)이 없는 함수, 즉, 함수의 실행이 외부에 영향을 끼치지 않는 함수
를 뜻한다. 따라서 순수한 함수는 스레드에 안전
하고, 병렬적인 계산
이 가능하다.
다음과 같은 코드에서 f는 순수한 함수라고 하자.
y = f(x) * f(x);
이 때, f가 두 번 중복되는 것을 다음과 같이 최적화할 수 있다.
z = f(x);
y = z * z;
이렇게 하면, f(x)를 계산하는 과정이 두 번에서 한 번으로 줄어들지만 두 코드의 결과는 항상 같게 된다.
익명 함수
란, 이름이 없는 함수를 뜻한다. 전통적인 명령형 언어에서는 모든 함수에 이름이 부여되어야만 한다.
예컨대 인수를 제곱하는 함수를 C 언어로 작성한다면 다음과 같이 작성할 수 있을 것이다.
int square(int x) { return x * x; }
C++11에서는 익명 함수를 지원해줘서 위의 코드를 익명함수로 바꾸면 아래와 같다.
[](int x) -> int { return x * x; }
고차 함수(고계 함수)
란, 함수를 다루는 함수를 뜻한다. 사실 함수형 언어에서는 함수도 '값(value)'으로 취급한다. 그러므로 정수 1이나 인수를 제곱하는 함수나 동등한 입장에서 다룰 수 있다. 정수를 함수의 인수로 전달할 수 있듯이 어떤 함수도 다른 함수의 인수로 전달할 수 있다. 마찬가지로 함수의 결과 값으로 정수를 반환할 수 있듯이 함수를 반환할 수도 있다.
예를 들어서 1에서 10까지 숫자로 이루어진 리스트의 각 원소를 제곱하고 싶다고 하자. 명령형 언어에서는 반복문을 이용하여 리스트를 훑어 가며 각 원소를 제곱하겠지만, 함수형 언어에서는 리스트를 다루는 고계 함수로 이를 처리할 수 있다. 다음은 하스켈을 이용하여 이를 수행한 예를 보여준다.
map (x -> x * x) [1..10]
여기서 [1..10]은 1에서 10까지 숫자로 이루어진 리스트다. 고계 함수 map은 첫 번째 인수로 주어진 함수(여기서는 제곱을 수행하는 익명함수)를 두 번째 인수로 주어진 리스트의 각 원소에 적용한 결과 리스트를 반환한다. 위 코드를 수행하면 다음과 같은 결과를 얻을 수 있다.
[1,4,9,16,25,36,49,64,81,100]