1. 함수형 프로그래밍(FP)
* 소개
- 최근 프로그래밍 패러다임은 크게 명령형 프로그래밍과 선언형 프로그래밍으로 나뉜다.
- 명령형 프로그래밍 : 어떻게 할 것인지를 설명하는 방식으로 절차지향 프로그래밍과 객체지향 프로그래밍이 있다.
- 선언형 프로그래밍 : 무엇을 할 것인지를 설명하는 방식으로 함수를 조합하여 SW를 만드는 함수형 프로그래밍이 있다.
- 함수형 프로그래밍은 자료처리를 수학적 함수의 계산으로 표현하려 한다. f(x) 와 같은 것을 생각하면 좋겠다.
* 의미
- 함수형 프로그래밍은 거의 모든 것을 순수함수로 나누어 문제를 해결하려한다. 이를 통해 가독성을 높이고, 유지보수를 용이하게 한다.
- 함수형 프로그래밍의 가장 큰 특징은 순수함수, 1급함수, 불변성, 참조 투명성이다. 이들은 아래에서 자세히 알아보자.
- 함수형 프로그래밍은 상태값 없이 복사만을 사용하여 구현된다. 함수가 1급시민이기 때문에 함수의 매개변수로 넘길 수 있기 때문이다.
- 상태와 가변데이터 대신 오로지 불변데이터만을 사용한다.
- 순수한 함수형 언어에서는 반복문과 조건문도 사용하지 않고 재귀로만 구현된다. 아래의 예시들은 자바스크립트로 작성하였으며, 순수한 함수형 언어가 아니므로 함수형 프로그래밍을 지향하여 작성했으나, 일부 완전하지 못하므로 참고바란다.
2. 특징
* 순수함수
- 동일한 input 에는 동일한 output이 나와야하는 것을 의미한다.
- 부작용(side-effect)가 없는 함수를 의미한다. 이를 위해 외부의 상태를 변경하거나 외부의 값을 참조하지 않는다.
- 느긋한 계산(Laze-Evaluation)을 사용한다. 이는 아래에서 나오는 계속 복사되는 특성에서 중요한 개념이다. 함수형 프로그래밍은 복사가 빈번하게 이루어지는데, 이것이 직접적으로 계속 일어나는 것이아니라 실제 함수 호출 시 그 값이 필요할 때까지는 계산이 일어나지 않는다. 즉 필요할 때 복사하고 계산한다.
* 1급 함수
- 1급 함수를 알아보기 전에 1급 시민을 알아보자. 1급 시민은 프로그래밍 언어에서 다음 세 가지 조건을 만족한다.
∙ 대상을 함수의 매개변수로 넘겨 사용할 수 있는가.
∙ 대상을 함수의 반환값으로 돌려줄 수 있는가.
∙ 대상을 변수나 자료구조에 담을 수 있는가.
- 위의 문장에서 '대상'을 '함수'로 바꾼 것이 1급 함수이다. 즉 함수형 프로그래밍에서는 함수자체를 매개변수로 넘길 수 있으며, 계속 반환 값이 복사되며 사용된다.
* 불변성
- 어떤 값의 상태를 변경하지 않는다는 의미이다. 상태의 변경의 외부에 side-effect를 일으킬 수 있으므로 지양해야한다.
* 참조투명성
- 기존의 값은 변경되지 않고 유지됨을 의미한다.
- 즉 바깥 스코프에 영향을 끼치지 않는다.
3. 람다함수와 클로저
* 람다함수
- 현재 사용되는 람다의 근간은 수학 및 기초CS분야에서의 람다 대수이다.
- 람다 함수는 프로그래밍 언어에서 익명함수를 지칭한다.
- 자바스크립트에서는 모든 함수가 1급객체이므로 익명함수이기만 하면 람다라고도 볼 수 있다.
- 람다식을 통해 고차함수를 사용할 수 있고, 코드를 간결하게 유지할 수 있다.
* 클로저
- 클로저는 실행 당시의 상황을 기억한다. 코드를 통해 자세히 알아보자.
function test(n) {
var number = n;
function printNumber() {
console.log(n);
}
return printNumber;
}
var print = test(50);
print(); // 50
- 위에서 print = test(50)으로 받은 것이 클로저이다.
- 클로저는 실행당시 test내의 지역변수인 number을 test호출 후에도 기억한다.
- 클로저는 자신을 포함하고 있는 외부의 인자나 지역변수 등을 외부 함수가 종료된 후에도 사용할 수가 있는데, 이를 자유 변수라 한다.
- 클로저가 생성될 때 범위 내의 변수들을 자유변수로 만드는 것을 캡처(capture)라고 한다. 이를 통해 클로저는 마치 상수처럼 계속 같은 값을 보여줄 수 있다.
- 이 자유변수는 직접 접근할 수 없고, 클로저를 통해서만 사용가능하다는 특징이 있다.
4. 함수의 합성
- 함수형 프로그래밍에서는 함수의 조합으로 원하는 값을 만들어 낸다.
- 함수를 합성하는 방법인 합성함수와 커링함수에 대해서 알아보자.
* 합성함수(Function Composition)
- 함수 컴포지션은 흔히 우리가 이미 사용하고 있는 함수의 합성 방식이다.
- 수학에서 g ◦ f = g(f(x)) 를 기억한다면 이와 같다고 생각해도 무방하다.
- 위의 형식처럼 합성함수는 func1(func2(x)) 의 형태를 가지며, 함수가 1급함수이기 때문에 이런식으로 활용이 가능하다.
- 1을 더하고 2의 제곱을 하는 코드를 살펴보자.
let x = 2;
const addOne = (n) => n + 1;
const powTwo = (n) => n ** 2;
const y = addOne(x);
const result = powTwo(y);
console.log(result); // 9
- 위의 코드는 절차지향 스타일로 작성된 코드이다 . 위에서 y와 같은 변수는 사실상 result를 위한 값이므로 변수로 할당하는 것은 메모리 낭비라고 볼 수 있다.
- 이를 변수를 제거하여 합성함수로 나타내면 다음과 같다.
const result = powTwo(addOne(x));
console.log(result); // 9
* 커링함수(Currying)
- 커링은 매개변수를 모두 채우지 않은 형태로 함수를 남아있게하여 재활용이 가능하도록 한다.
- 형태는 func(x)(y)(z) 처럼 나타난다. func(x, y, z) 를 매개변수를 하나만 사용할 수 있도록 바꾼 것이다.
- 커링은 함수를 호출하는 것이 아니라 단지 변형시킬 뿐이다.
- 아래의 코드는 배열의 원소에 num만큼 더하여 반환하는 예제이다.
const myArray1 = [1, 2, 3, 4, 5, 6];
const myArray2 = [2, 4, 6, 8, 10];
const myArray3 = [1, 3, 5, 7, 9];
const getAddArr1 = (num, arr) => {
let newArr = [];
arr.forEach((elem) => newArr.push(elem + num));
return newArr;
};
console.log(getAddArr1(10, myArray1)); // [ 11, 12, 13, 14, 15, 16 ]
console.log(getAddArr1(10, myArray2)); // [ 12, 14, 16, 18, 20 ]
console.log(getAddArr1(10, myArray3)); // [ 11, 13, 15, 17, 19 ]
- 위의 코드에서 모두 10을 더하고 있지만 반복해서 10을 입력하고 있다. 그렇다보니 코드가 지저분하다.
- 커링을 사용해보자.
const getAddArr2 = (num) => (arr) => {
let newArr = [];
arr.forEach((elem) => newArr.push(elem + num));
return newArr;
};
const addTwo = getAddArr2(10);
console.log(addTwo(myArray1)); // [ 11, 12, 13, 14, 15, 16 ]
console.log(addTwo(myArray2)); // [ 12, 14, 16, 18, 20 ]
console.log(addTwo(myArray3)); // [ 11, 13, 15, 17, 19 ]
- 10이라는 num을 가진 함수를 미리 addTwo라는 이름으로 만들고 나중에 gettAddArr2(10)(arr) 형태로 재활용 했음을 볼 수 있다.
- 아래는 두 개의 래퍼를 사용하여 제곱하는 함수를 구현한 예제이다.
function curry(f) {
return function(a) {
return function(b) {
return f(a, b);
};
};
}
function pow(a, b) {
return a ** b;
}
let curriedPow = curry(pow);
console.log( curriedPow(3)(2) ); // 9
- 처음 호출되면 curriedPow(3)의 인수 3이 Lexical Environment에 저장되고 새 래퍼 function(b)가 반환된다.
- 새 래퍼는 2라는 인수로 호출되고 호출은 원본인 Pow에 전달된다.
참고
[프로그래밍] 함수형 프로그래밍(Functional Programming) 이란?
1. 함수형 프로그래밍(Functional Programming)에 대한 이해 [ 프로그래밍 패러다임(Programming Paradigm) ] 프로그래밍 패러다임(Programming Paradigm)은 프로그래머에게 프로그래밍의 관점을 갖게 하고 코드를..
mangkyu.tistory.com
함수형 프로그래밍에 관해
함수형 프로그래밍 알아보기
medium.com
[JAVA] 람다식(Lambda)의 개념 및 사용법
람다함수란? 람다 함수는 프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous functions)를 지칭하는 용어입니다. 현재 사용되고 있는 람다의 근간은 수학과 기초 컴퓨터과학 분야에서의
khj93.tistory.com
JavaScript 관점 : 람다, 익명함수, 클로저
람다대수 특징 1. 람다 대수는 이름을 가질 필요가 없다. 2. 두개 이상의 입력이 있는 함수는 최종적으로 1개의 입력만 받는 람다 대수로 단순화 될 수 있다. 1번 특징에서 말하는 것은 익명함수이
centbin-dev.tistory.com
FP in JS (자바스크립트로 접해보는 함수형 프로그래밍) - 함수 컴포지션, 커링
두 번째 글입니다. 함수형 프로그래밍에서는 함수의 조합으로 원하는 값을 만들어 냅니다. 함수의 조합인 함수 컴포지션에 대해서 살펴보도록 하겠습니다. 그리고 커링 기법을 이용해 함수 컴
velog.io
Currying
javascript.info
'Computer Science > etc.' 카테고리의 다른 글
< OOP > 객체 지향 프로그래밍이란? (0) | 2021.06.12 |
---|