함수형 프로그래밍 소개: JavaScript 패러다임

게시 됨: 2022-03-11

함수형 프로그래밍은 상태와 데이터를 변경하지 않고 표현식과 함수를 사용하여 컴퓨터 프로그램을 작성하는 패러다임입니다.

이러한 제한 사항을 준수함으로써 함수형 프로그래밍은 더 명확하고 버그에 강한 코드를 작성하는 것을 목표로 합니다. 이는 코드를 따르기 어렵게 만드는 흐름 제어 문( for , while , break , continue , goto )을 사용하지 않음으로써 달성됩니다. 또한 함수형 프로그래밍에서는 버그가 발생할 가능성이 적은 순수하고 결정적인 함수를 작성해야 합니다.

이 기사에서는 JavaScript를 사용하여 함수형 프로그래밍을 수행하는 방법에 대해 설명합니다. 또한 이를 가능하게 하는 다양한 JavaScript 방법과 기능을 살펴봅니다. 결국, 우리는 함수형 프로그래밍과 관련된 다양한 개념을 탐구하고 그것이 왜 그렇게 강력한지 알아볼 것입니다.

하지만 함수형 프로그래밍에 들어가기 전에 순수 함수와 순수 함수의 차이점을 이해해야 합니다.

순수 함수 대 순수 함수

순수 함수는 일부 입력을 받고 고정 출력을 제공합니다. 또한, 그들은 외부 세계에서 부작용을 일으키지 않습니다.

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

여기서 add 는 순수 함수입니다. 이는 ab 고정 값의 경우 출력이 항상 동일하기 때문입니다.

 const SECRET = 42; const getId = (a) => SECRET * a;

getId 는 순수 함수가 아닙니다. 그 이유는 출력을 계산하기 위해 전역 변수 SECRET 를 사용하기 때문입니다. SECRET 이 변경되면 getId 함수는 동일한 입력에 대해 다른 값을 반환합니다. 따라서 순수 함수가 아닙니다.

 let id_count = 0; const getId = () => ++id_count;

이것은 또한 불순한 함수이며, 그 이유도 두 가지입니다. (1) 출력을 계산하기 위해 비-로컬 변수를 사용하고 (2) 외부 세계에서 변수를 수정하여 부작용을 만듭니다. 세계.

getId는 순수하지 않은 그림입니다.

이 코드를 디버그해야 하는 경우 문제가 될 수 있습니다.

id_count 의 현재 값은 얼마입니까? id_count 를 수정하는 다른 기능은 무엇입니까? id_count 에 의존하는 다른 기능이 있습니까?

이러한 이유 때문에 우리는 함수형 프로그래밍에서 순수 함수만 사용합니다.

순수 함수의 또 다른 이점은 병렬화 및 메모화할 수 있다는 것입니다. 앞의 두 함수를 살펴보십시오. 병렬화하거나 메모하는 것은 불가능합니다. 이것은 성능이 좋은 코드를 만드는 데 도움이 됩니다.

함수형 프로그래밍의 신조

지금까지 우리는 함수형 프로그래밍이 몇 가지 규칙에 의존한다는 것을 배웠습니다. 그것들은 다음과 같습니다.

  1. 데이터를 변경하지 마십시오
  2. 순수 함수 사용: 고정 입력에 대해 고정 출력, 부작용 없음
  3. 표현식 및 선언 사용

이러한 조건을 만족하면 코드가 기능적이라고 말할 수 있습니다.

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는 그 어느 때보다 훨씬 더 나은 기능적 프로그래밍 경험을 가능하게 합니다.