함수형 프로그래밍 소개: JavaScript 패러다임
게시 됨: 2022-03-11함수형 프로그래밍은 상태와 데이터를 변경하지 않고 표현식과 함수를 사용하여 컴퓨터 프로그램을 작성하는 패러다임입니다.
이러한 제한 사항을 준수함으로써 함수형 프로그래밍은 더 명확하고 버그에 강한 코드를 작성하는 것을 목표로 합니다. 이는 코드를 따르기 어렵게 만드는 흐름 제어 문( for
, while
, break
, continue
, goto
)을 사용하지 않음으로써 달성됩니다. 또한 함수형 프로그래밍에서는 버그가 발생할 가능성이 적은 순수하고 결정적인 함수를 작성해야 합니다.
이 기사에서는 JavaScript를 사용하여 함수형 프로그래밍을 수행하는 방법에 대해 설명합니다. 또한 이를 가능하게 하는 다양한 JavaScript 방법과 기능을 살펴봅니다. 결국, 우리는 함수형 프로그래밍과 관련된 다양한 개념을 탐구하고 그것이 왜 그렇게 강력한지 알아볼 것입니다.
하지만 함수형 프로그래밍에 들어가기 전에 순수 함수와 순수 함수의 차이점을 이해해야 합니다.
순수 함수 대 순수 함수
순수 함수는 일부 입력을 받고 고정 출력을 제공합니다. 또한, 그들은 외부 세계에서 부작용을 일으키지 않습니다.
const add = (a, b) => a + b;
여기서 add
는 순수 함수입니다. 이는 a
및 b
고정 값의 경우 출력이 항상 동일하기 때문입니다.
const SECRET = 42; const getId = (a) => SECRET * a;
getId
는 순수 함수가 아닙니다. 그 이유는 출력을 계산하기 위해 전역 변수 SECRET
를 사용하기 때문입니다. SECRET
이 변경되면 getId
함수는 동일한 입력에 대해 다른 값을 반환합니다. 따라서 순수 함수가 아닙니다.
let id_count = 0; const getId = () => ++id_count;
이것은 또한 불순한 함수이며, 그 이유도 두 가지입니다. (1) 출력을 계산하기 위해 비-로컬 변수를 사용하고 (2) 외부 세계에서 변수를 수정하여 부작용을 만듭니다. 세계.
이 코드를 디버그해야 하는 경우 문제가 될 수 있습니다.
id_count
의 현재 값은 얼마입니까? id_count
를 수정하는 다른 기능은 무엇입니까? id_count
에 의존하는 다른 기능이 있습니까?
이러한 이유 때문에 우리는 함수형 프로그래밍에서 순수 함수만 사용합니다.
순수 함수의 또 다른 이점은 병렬화 및 메모화할 수 있다는 것입니다. 앞의 두 함수를 살펴보십시오. 병렬화하거나 메모하는 것은 불가능합니다. 이것은 성능이 좋은 코드를 만드는 데 도움이 됩니다.
함수형 프로그래밍의 신조
지금까지 우리는 함수형 프로그래밍이 몇 가지 규칙에 의존한다는 것을 배웠습니다. 그것들은 다음과 같습니다.
- 데이터를 변경하지 마십시오
- 순수 함수 사용: 고정 입력에 대해 고정 출력, 부작용 없음
- 표현식 및 선언 사용
이러한 조건을 만족하면 코드가 기능적이라고 말할 수 있습니다.
JavaScript의 함수형 프로그래밍
JavaScript에는 이미 함수형 프로그래밍을 가능하게 하는 몇 가지 기능이 있습니다. 예: String.prototype.slice, Array.prototype.filter, Array.prototype.join.
반면 Array.prototype.forEach, Array.prototype.push는 순수하지 않은 함수입니다.
Array.prototype.forEach
는 설계상 순수하지 않은 함수라고 주장할 수 있지만 생각해 보십시오. 로컬이 아닌 데이터를 변경하거나 부작용을 수행하는 것 외에는 아무 것도 할 수 없습니다. 따라서 불순 함수의 범주에 넣어도 괜찮습니다.
또한 JavaScript에는 const 선언이 있어 데이터를 변경하지 않기 때문에 함수형 프로그래밍에 적합합니다.
JavaScript의 순수 함수
JavaScript에서 제공하는 몇 가지 순수 함수(메소드)를 살펴보겠습니다.
필터
이름에서 알 수 있듯이 배열을 필터링합니다.
array.filter(condition);
여기서 조건은 배열의 각 항목을 가져오는 함수이며 항목을 유지할지 여부를 결정하고 이에 대한 진실한 부울 값을 반환해야 합니다.
const filterEven = x => x%2 === 0; [1, 2, 3].filter(filterEven); // [2]
filterEven
은 순수 함수입니다. 불순한 경우 전체 필터 호출을 불순하게 만들었을 것입니다.
지도
map
은 배열의 각 항목을 함수에 매핑하고 함수 호출의 반환 값을 기반으로 새 배열을 만듭니다.
array.map(mapper)
mapper
는 배열의 항목을 입력으로 받아 출력을 반환하는 함수입니다.
const double = x => 2 * x; [1, 2, 3].map(double); // [2, 4, 6]
줄이다
reduce
는 배열을 단일 값으로 줄입니다.
array.reduce(reducer);
reducer
는 누적된 값과 배열의 다음 항목을 가져와서 새 값을 반환하는 함수입니다. 배열의 모든 값에 대해 다음과 같이 호출됩니다.
const sum = (accumulatedSum, arrayItem) => accumulatedSum + arrayItem [1, 2, 3].reduce(sum); // 6
콘캣
concat
은 기존 배열에 새 항목을 추가하여 새 배열을 만듭니다. push()가 데이터를 변경하여 데이터를 불순하게 만든다는 점에서 push()
push()
와 다릅니다.
[1, 2].concat([3, 4]) // [1, 2, 3, 4]
스프레드 연산자를 사용하여 동일한 작업을 수행할 수도 있습니다.

[1, 2, ...[3, 4]]
개체.할당
Object.assign
은 제공된 개체의 값을 새 개체로 복사합니다. 함수형 프로그래밍은 변경할 수 없는 데이터를 기반으로 하기 때문에 기존 개체를 기반으로 새 개체를 만드는 데 사용합니다.
const obj = {a : 2}; const newObj = Object.assign({}, obj); newObj.a = 3; obj.a; // 2
ES6의 출현으로 이것은 스프레드 연산자를 사용하여 수행할 수도 있습니다.
const newObj = {...obj};
나만의 순수 함수 만들기
순수 함수도 만들 수 있습니다. 문자열을 n
번 복제하는 작업을 수행해 보겠습니다.
const duplicate = (str, n) => n < 1 ? '' : str + duplicate(str, n-1);
이 함수는 문자열을 n
번 복제하고 새 문자열을 반환합니다.
duplicate('hooray!', 3) // hooray!hooray!hooray!
고차 함수
고차 함수는 함수를 인수로 받아들이고 함수를 반환하는 함수입니다. 종종 그들은 기능의 기능을 추가하는 데 사용됩니다.
const withLog = (fn) => { return (...args) => { console.log(`calling ${fn.name}`); return fn(...args); }; };
위의 예에서 우리는 함수를 받아서 래핑된 함수가 실행되기 전에 메시지를 기록하는 함수를 반환하는 withLog
고차 함수를 만듭니다.
const add = (a, b) => a + b; const addWithLogging = withLog(add); addWithLogging(3, 4); // calling add // 7
withLog
HOF는 다른 기능과도 함께 사용할 수 있으며 충돌이나 추가 코드 작성 없이 작동합니다. 이것이 HOF의 아름다움입니다.
const addWithLogging = withLog(add); const hype = s => s + '!!!'; const hypeWithLogging = withLog(hype); hypeWithLogging('Sale'); // calling hype // Sale!!!
결합 함수를 정의하지 않고 호출할 수도 있습니다.
withLog(hype)('Sale'); // calling hype // Sale!!!
카레
Currying은 여러 인수를 취하는 함수를 하나 또는 여러 수준의 고차 함수로 나누는 것을 의미합니다.
add
함수를 사용해보자.
const add = (a, b) => a + b;
카레를 만들 때 인수를 다음과 같이 여러 수준으로 분산하여 다시 작성합니다.
const add = a => { return b => { return a + b; }; }; add(3)(4); // 7
커링의 장점은 메모이제이션입니다. 이제 중복 및 재계산 없이 나중에 재사용할 수 있도록 함수 호출에서 특정 인수를 메모할 수 있습니다.
// assume getOffsetNumer() call is expensive const addOffset = add(getOffsetNumber()); addOffset(4); // 4 + getOffsetNumber() addOffset(6);
이것은 모든 곳에서 두 인수를 모두 사용하는 것보다 확실히 낫습니다.
// (X) DON"T DO THIS add(4, getOffsetNumber()); add(6, getOffsetNumber()); add(10, getOffsetNumber());
또한 간결하게 보이도록 커리 함수의 형식을 변경할 수도 있습니다. 이는 커링 함수 호출의 각 수준이 한 줄 반환 문이기 때문입니다. 따라서 ES6에서 화살표 함수를 사용하여 다음과 같이 리팩토링할 수 있습니다.
const add = a => b => a + b;
구성
수학에서 합성은 한 기능의 출력을 다른 기능의 입력으로 전달하여 결합된 출력을 생성하는 것으로 정의됩니다. 우리는 순수 함수를 사용하기 때문에 함수형 프로그래밍에서도 마찬가지입니다.
예제를 보여주기 위해 몇 가지 함수를 만들어 보겠습니다.
첫 번째 함수는 시작 숫자 a
와 끝 숫자 b
를 사용하여 a
에서 b
까지의 숫자로 구성된 배열을 생성하는 범위입니다.
const range = (a, b) => a > b ? [] : [a, ...range(a+1, b)];
그런 다음 배열을 취하여 그 안의 모든 숫자를 곱하는 함수 곱하기가 있습니다.
const multiply = arr => arr.reduce((p, a) => p * a);
이 함수를 함께 사용하여 계승을 계산합니다.
const factorial = n => multiply(range(1, n)); factorial(5); // 120 factorial(6); // 720
계승을 계산하는 위의 함수는 f(x) = g(h(x))
와 유사하므로 구성 속성을 보여줍니다.
결론
순수하고 불순한 함수, 함수형 프로그래밍, 이를 지원하는 새로운 JavaScript 기능 및 함수형 프로그래밍의 몇 가지 핵심 개념을 살펴보았습니다.
이 글이 함수형 프로그래밍에 대한 관심을 불러일으키고 코드에서 시도하도록 동기를 부여할 수 있기를 바랍니다. 이것이 학습 경험이자 소프트웨어 개발 여정의 이정표가 될 것이라고 확신합니다.
함수형 프로그래밍은 잘 연구되고 강력한 컴퓨터 프로그램 작성 패러다임입니다. ES6의 도입으로 JavaScript는 그 어느 때보다 훨씬 더 나은 기능적 프로그래밍 경험을 가능하게 합니다.