함수형 프로그래밍은 기존의 절차적, 객체지향 프로그래밍과는 다른 방식이다. 자료 처리를 수학적 함수의 계산으로 취급하고 가변 데이터를 멀리하는 프로그래밍의 패러다임이다.

프로그래밍 패러다임

프로그래밍 패러다임은 프로그래머에게 프로그래밍의 관점을 갖게 해주고, 결정하는 역할을 한다. 예를 들어 객체지향 프로그래밍은 프로그램을 상호작용하는 객체들의 집합으로 바라보도록 한다.

서로 다른 프로그래밍 언어는 서로 다른 프로그래밍 패러다임을 지원한다. 자바가 객체지향 프로그래밍을 지원하는 반면, 하스켈은 함수형 프로그래밍을 지원한다. 하지만 프로그래밍 언어가 여러 프로그래밍 패러다임을 지원하기도 한다.

  • 구조적 vs. 비구조적

    비구조적 프로그래밍은 하나의 연속된 덩어리에 모든 코드를 넣는 프로그래밍 패러다임으로, 코드의 특정 부분으로 건너뛰는 GOTO문에 의존한다. 구조화 되지 않은 원시 코드는 가독성이 떨어져 디버깅이 어렵다.

  • 명령형(how) vs. 선언형(what) : 명령형 프로그래밍은 어떻게 할 것인가 에, 선언형 프로그래밍은 무엇을 할 것인가 에 초점을 둔다.
    • 명령형 : 반죽을 짜고, 돌돌 말아 토마토 소스를 넣고, 치즈를 넣고, 햄을 넣고, 파인애플을 넣고, 섭씨 200도로 예열된 돌가마에 구워...
    • 선언형 : 하와이안 피자 주세요.

    SELECT * FROM users WHERE country = '서울'
    SQL 언어는 선언형 프로그래밍의 대표적인 예다. 서울에 사는 사용자들의 정보를 가져오는 what에 초점을 맞출 뿐, 테이블에서 어떻게 정보를 조회하는지 how에 대한 구현은 알 수 없다.


함수형 프로그래밍 (⊂ 선언형)

함수형 프로그래밍은 다음과 같은 특징들을 가진다.

  • 과정보다 결과에 초점을 둔다.
  • 무엇(what)을 할 지를 강조한다.
  • 데이터의 불변성을 유지해야 한다.
  • 반복문 대신 재귀를 사용한다.

1급 객체

1급 객체는 다른 객체들에 적용할 수 있는 연산을 모두 지원하는 객체를 의미한다. 다음 조건을 모두 충족하면 1급 객체에 해당한다.

  1. 변수나 데이터 구조에 할당할 수 있다.
  2. 객체의 파라미터로 전달할 수 있다.
  3. 객체의 반환값으로 사용할 수 있다.

자바스크립트에서 함수는 1급 객체다.


참조 투명성과 부작용

  • 참조 투명성 : 동일한 입력에 대해 항상 같은 값을 반환
  • 부작용 : 함수가 결과값 이외의 다른 상태를 변경시키는 것
    ex. 전역/정적변수를 수정하거나 파라미터를 변경

순수함수

순수함수는 동일한 입력에 대해 항상 같은 값을 반환한다. 함수 내부에서 인자의 값을 변경하거나 프로그램의 상태를 변경하지 않기 때문에 함수의 실행이 프로그램의 실행에 영향을 미치지 않는다.

const add = (a, b) => a + b;

불변성

함수형 프로그래밍은 데이터의 불변성을 유지해야 한다. 데이터의 변경이 필요한 경우, 원본 데이터를 변경하지 않고, 그 데이터의 복사본을 만들어 사용한다.

const add = (person) => { return {...person, age: person.age+1}; };

선언형 함수

선언형 함수는 어떻게 할 것인가보다 무엇을 할 것인가에 초점을 맞춘다.

const multifly = (nums, n) => {
  return nums.map((num) => num * n);
};

고차함수

고차함수는 함수를 파라미터로 전달받거나 결과로 반환하는 함수다.

  • 함수를 파라미터로 전달하는 경우
const double = (num) => num * 2;
const highDouble = (func, num) => func(num);
  • 함수를 리턴하는 경우
const sub = (a) => (b) => a - b;
console.log(sub(2)(1)); // 1
  • 함수를 파라미터로 받고, 함수를 리턴하는 경우
const double = (num) => num * 2;
const doubleSub = (a, func) => (b) => a - func(b);
console.log(doubleSub(2, double)(1)); // 0

Javascript Array 고차함수

  • forEach() : for문을 대체하는 고차함수 (리턴값 X )
/*
    item :현재 요소
    index : 배열 인덱스 (생략 가능)
    thisArg : 콜백함수에서 this로 사용할 값 (생략 가능)
    
    배열의 각 요소에 대해 오름차순으로 콜백함수 실행
*/
arr.forEach((item) => { });
// num 배열의 요소 합 구하기
const num = [1, 2, 3, 4, 5];
let total = 0;
num.forEach((item) => total += item);
  • map() : for문을 대체하는 고차함수 (리턴값 O )
/*
    item : 현재 요소
    index :  배열 인덱스 (생략 가능)
    array : 참조한 배열 (생략 가능)
    thisArg : 콜백함수에서 this로 사용할 값 (생략 가능)
    
    배열 내의 모든 요소에 대해 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환
*/
arr.map((item) => { }, thisArg);
const num = [1, 2, 3, 4, 5];
const res = num.map((item) => {
	return (item % 2) ? 'even' : 'odd';
});

forEach()map()은 배열을 순회한다는 점에서 동일하지만 forEach()는 각 요소를 참조한 연산이 이루어지고 map()은 각 요소를 다른 값으로 맵핑한 새로운 배열이 반환된다는 점에서 차이가 있다.

  • filter() : 배열을 순회하며 콜백함수의 반환값이 true인 요소로만 구성된 새로운 배열 생성 (리턴값 O )
// num 배열의 요소에서 홀수만 뽑아내기
const num = [1, 2, 3, 4, 5];
const odd = num.filter((item) => item % 2);
  • reduce() : 배열의 각 요소에 대해 리듀서를 실행하고 결과값을 반환
/*
    acc : 누산기, 순회하면서 계속 더해져서 합쳐지는 값
    curr : 현재 요소
    index : 배열 인덱스 (생략 가능)
    array : 참조한 배열 (생략 가능)
    initialValue : 콜백 최초 호출에서 acc 누산기에 제공하는 값 (생략 가능)
*/
arr.reduce((acc, curr) => { }, initialValue);
// num 배열의 요소 합 구하기
const num = [1, 2, 3, 4, 5];
const res = num.reduce((acc, curr) => acc + curr, 0);

initialValue가 있는 경우 acc값은 initialValue가 지정한 값이 된다. 하지만 initalValue가 없는 경우, acc은 배열의 첫번째 값이 된다.


참고자료
wikipedia : 프로그래밍 패러다임
wikipedia : 함수형 프로그래밍
함수형 프로그래밍
고차함수와 1급 객체
mdn web docs : Array
javascript array 고차함수

0개의 댓글