이 글은 Eric Elliott 이 medium에서 연재하는 Composing Software 시리즈를 번역한 것입니다. [원문보기]
Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0)
참고 : 이 글은 JavaScript ES6+의 함수형 프로그래밍 및 소프트웨어 합성 방법론을 기초부터 다루는 "소프트웨어 합성"시리즈의 일부 입니다. 앞으로 계속하여 연재될 것입니다.
<이전 | << Part 1에서 다시 시작 | 다음>
고차 함수higher order function 는 함수를 인수로 받거나 함수를 리턴하는 함수입니다. 반면에 1차 함수first order function는 함수를 인수로 사용하거나 함수를 출력으로 리턴하지 않습니다.
이전 글에서 우리는 .map()
과 .filter()
예제를 보았습니다. 둘 다 인수로 함수를 사용합니다. 즉, 둘 다 고차 함수입니다.
단어 목록에서 네 글자로 이루어진 단어를 선택하는 1차 함수의 예를 살펴 보겠습니다.
1 | const censor = words => { |
이제 's’로 시작하는 모든 단어를 선택하려면 어떻게 해야 할까요? 또 다른 함수를 만들면 됩니다 :
1 | const startsWithS = words => { |
딱봐도 두 함수가 동일한 코드를 많이 반복하고 있습니다. 코드를 더 일반화된 해결책으로 추상화하는 패턴이 있습니다. 두 함수는 공통점이 많습니다. 둘 다 목록을 순회iterate하고 주어진 조건으로 필터링합니다.
순회와 필터링을 위한 코드가 자기들을 추상화 해달라고 구걸하고 있습니다. 모든 종류의 유사한 함수들을 작성할 때 공유하고 재사용해달라고 말합니다. 사실 어떤 목록에서 물건을 선택하는 것은 매우 일반적인 작업입니다.
다행스럽게도 JavaScript의 함수는 일급first class입니다. 그게 무슨 뜻이냐구요? 숫자, 문자열 또는 객체와 마찬가지로 함수는 다음과 같은 일을 할 수 있습니다.
- 식별자 (변수)값으로 할당
- 객체 속성 값에 할당
- 인수로 전달
- 함수에서 리턴됨
기본적으로 프로그램에 있는 다른 데이터들처럼 함수를 사용할 수 있으므로 추상화하기가 훨씬 쉬워졌습니다. 예를 들어 목록을 순회하는 과정을 추상화하고 데이터를 처리하는 함수인 reducer를 전달하여 리턴 값을 누적하는 함수를 만들 수 있습니다. 이 함수를 reduce 라고 부릅니다.
1 | const reduce = (reducer, initial, arr) => { |
reduce()
함수는 reducer 함수, 누적값accumulator의 초기값 그리고 순회할 배열을 인자로 받습니다. 배열의 각 항목마다 reducer가 호출되어 누적값과 현재 배열 요소를 전달합니다. 누적값에는 계속해서 값이 누적되며 배열의 모든 요소에 대해 순회한 이후 최종적인 누적값이 리턴됩니다.
맨 아래줄에서는 reducer함수로 (acc, curr) => acc + curr
를 전달하는데 이는 배열 요소를 계속하여 누적하는 프로세스가 됩니다. 다음으로 초기 값인 0
과 순회할 데이터 배열을 전달합니다.
반복과 누적accumulation이 추상화되면서 이제는 좀 더 일반화 된 filter()
함수를 구현할 수 있습니다.
1 | const filter = ( |
filter()
에서는 인수로 전달 된 fn()
함수를 제외한 모든 것이 다른 곳에서 재사용될 수 있는 것들입니다. 이 때 fn()
은 술어predicate라고합니다. 술어 는 부울 값을 리턴하는 함수입니다.
전달받은 배열에서 값을 하나씩 순회하며 fn()
을 적용합니다. fn(curr)
테스트가 true
리턴하면 curr
값을 빈 배열에 계속하여 연결합니다. 테스트가 실패할경우 현재 배열값을 넘깁니다.
이제 filter()
를 사용해 네 글자로 이루진 단어를 필터링하는 censor()
를 구현해보겠습니다.
1 | const censor = words => filter( |
인상적이지 않습니까? 다양한 작업(필터링, 순회)들이 추상화되었고 censor()
는 아주 짧은 함수가 됐습니다.
startsWithS()
도 마찬가지입니다.
1 | const censorstartsWithS = words => filter( |
몇몇 독자들은 이미 JavaScript가 이러한 추상화를 제공한다는걸 알고 있을 겁니다. Array.prototype
메서드에는 .reduce()
.filter()
.map()
와 같은 다양한 함수이 이미 존재합니다.
고차 함수는 다양한 데이터유형에서 동일하게 작동하도록 추상화하는데도 사용됩니다. 예를 들어 .filter()
가 꼭 문자열 배열에서만 작동하라는 법은 없습니다. 인자로 전달하는 함수가 다른 데이터 유형을 처리하게만 하면 됩니다. highpass()
예제를 기억하십니까?
1 | const highpass = cutoff => n => n >= cutoff; |
즉, 고차 함수를 사용하여 함수에 다형성을 부여할 수 있습니다. 보시다시피 고차 함수는 1차 함수보다 훨씬 다재다능합니다. 일반적으로, 실제 응용 프로그램애플리케이션은 고차함수와 매우 간단한 1 차 함수를 함께 사용합니다.