Back to the Basics

[JS/Node][Codestates] 고차함수 본문

Programming Languages/JavaScript & Node.js

[JS/Node][Codestates] 고차함수

9Jaeng 2021. 8. 3. 22:43
728x90

이번 포스팅은 JavaScript 고차 함수 (Higher order function)에 대해 학습해보겠다.

1. 고차 함수 이해하기

Achievement Goals

  • 일급 객체(first-class citizen)의 세 가지 특징을 설명할 수 있다.
  • 고차 함수(higher-order function)에 대해 설명할 수 있다.
  • 고차 함수를 JavaScript로 작성할 수 있다.

JavaScript 에서 함수는 1) 변수에 assignment 가 가능하고, 2) 다른 함수의 argument로 전당 될 수 있으며 3) 다른 함수의 결과로써 리턴될 수 있다 는 특징이 있어 특별하게 취급된다. 이를 first-class citizen (일급 객체)라고 한다. 이 중 고차 함수(Higher oeder function) 이란 함수를 argument로 받을 수 있고, 함수의 형태로 리턴할 수 있는 함수를 말한다.

다른 함수(caller)의 인자(argument)로 전달되는 함수를 콜백 함수(callback function)이라고 한다. 함수를 리턴하는 함수를 커리함수(Haskell Curry의 이름을 따서 curry 함수 하고 한다)라고 하지만, 함수를 인자로 받는 함수든, 한수를 리턴하는 함수 모두 고차 함수라고 부른다.

간단하게 코드를 보면 아래와 같다.

const squareAdd=(sqared,func)=>{
    return function(num){
        return num+func(sqared)
    }
}
const square=num=>num*num

squareAdd(5,square)(3)
//28

const square2=squareAdd(3,square)

square2(2)
//11
  • 위의 코드에서 squareAdd는 고차함수이이고, func는 이 함수의 callback 함수이다. 함수 square는 함수 squareAdd의 콜백으로 전달되었다.
  • 또한 const square2와 같이 squareAdd가 return 하는 함수를 변수에 저장할 수 있다.

내장 고차함수 이해하기

Achievement Goals

  • 배열 내장 고차함수 filter에 대해 이해할 수 있다.
  • filter에 대한 이해를 기반으로, 나머지 고차 함수를 학습한다.
    • forEatch, find, filter, map, recuce, sort, some, every
      • 추상화(abstraction)에 대해 설명할 수 있다.
      • 추상화의 관점에서 고차 함수가 갖는 이점에 대해 설명할 수 있다.
      • 고차 함수를 활용하여 프로그램을 작성할 수 있다.

1.array.filter()

  • filter method는 모든 배열의 요수 중에서 특정 조건을 만족하는 요소를 걸러내는 method이다. 새로운 배열로 return을 하고, 만약 특정 조건을 만족하는 요소가 없다면, 빈 배열을 return 한다. Array.prototype.filter() MDN 에서 알 수 있듯이, 특정 조건은 함수의 형태로 filter method 인자로 전달된다.
  • filter함수는 아래의 과정을 따른다

1) 배열의 각 요소가 2) 특정 논리(함수)에 따르면, 사실(boolean) 일 때 3) 따로 분류한다(filter)

  • filter method는 배열의 요소를 인자로 전달되는 콜백 함수에 다시 전달한다. 콜백 함수는 전달받은 배열의 각 요소를 차례로 받아 함수를 실행하고 콜백함수의 내부 조건에 따라 true or false를 리턴한다.
const lessThan10OddNum=num=>(num%2===0 && num>10)

const arr=[10,5,6,8,2,4,12]

const arr2=arr.filter(lessThan10OddNum)

arr2
//[12]

위의 코드에서 arr.filter method는 인자로 받는 callback 함수인 lessThan100OddNum에 arr의 요소를 차례대로 하나 씩 매개변수로 넣고 실행한다. 콜백 함수인 lessThan100 OddNum에서는 arr 각 요소가 2의 배수이고 10 이상일 경우에만 ( 조건이 true인 경우) 그 요소를 새로운 배열의 요소로 넣는다. arr의 모든 요소가 콜백 함수를 거치면, 최종적으로 조건을 만족하는 모든 요소가 담긴 배열이 filter 함수에 의해 출력된다.

2. arr.map()

  • map method의 과정도 위의 filter method와 배열의 요소를 콜백함수를 거쳐 새로운 배열을 return 한다는 점에서 비슷하다. map 매소드의 진행 과정은 1) 배열의 각 요소가 2) 특정 논리(함수)에 의해 3) 다른 요소로 지정(mzp) 된다.라고 할 수 있다.
  • map method는 하나의 데이터를 다른 데이터로 mapping할 때 사용한다.
// 배열의 각 요소의 루트 값을 갖는 새로운 배열로 리턴하는 함수
let numbers=[1,2,4,9,16,25]
let roots=numbers.map(el=>Math.sqrt(el))
roots
// [1, 1, 2, 3, 4, 5]

위의 코드처럼 callback 함수를 map의 매개변수를 삽입한는 부분에 바로 선언할 수 있다. roots는 map에 배열의 각 인자를 매개변수 el로 받는 익명 함수를 선언하였고, 이 함수는 매개변수로 들어온 배열의 각 요소 (el)을 Math.sqrt(el)을 사용하여 el의 루트 값을 새로운 배열의 요소로 넣는다. arr의 모든 요소가 선언되어있는 콜백 함수를 거치면, 최종적으로 배열의 각 요소에 루트를 씌운 요소들이 담긴 배열이 map 함수에 의해 출력된다

3. arr.reduce()

  • reduce는 여러 개의 데이터를 하나의 데이터로 응축할 때 사용한다.
  • 위에서 정리한 filter와 map 함수와 비슷하게 구동 방식은 크게 다르지 않다. 1) 배열의 각 요소를 2) 특정 방법(함수)에 따라 3) 원하는 하나의 형태로 4) 응축한다.
  • arr.reduce((acc,cur)=>acc+cur,초기값) 와 같은 형식을 띤다. 좀 더 쉽게 단순화하자면,, arr.reduce.(function, 초기값)이라고 할 수 있다.
    • function : 배열의 각 값을 처리 할 콜백 함수
    • cur : 현재 들어있는 값으로 배열의 각 요소의 값이 차례로 들어간다.
    • acc : sum을 구할 때와 같이 값 또는 무언가를 축적하는 변수이다.
    • 초기값 : cur가 갖는 현재 값을 지정해준다 optional이며 지정하지 않을 시 배열의 첫 번째 원소의 값이 들어간다.
  • reducer 함수의 반환 값은 acc(누산기)에 할당되고, 누산기는 순회 중 유지되므로 결국 최종 결과는 하나의 값이 된다고 한다 (mdn)

간단한 예제를 보자.

const adder=(sum,current)=>sum+current

arr=[1,2,3,4,5,6,7,8,9]

const arr=[1,2,3,4,5,6,7,8,9]

let result=arr.reduce(adder,0)

result
//45

위의 코드는 call back 함수로 두 변수의 합을 구하는 함수를 넣었고, 초기값을 0으로 지정하였다. 배열 arr의 각 요소는 이 콜백 함수로 차례로 들어가고 콜백 함수를 실행한다. arr의 모든 요소가 콜백 함수를 거치면, 최종적으로 누적된 값이 reduce 함수에 의해 출력된다.

 

4. filter, map, reduce method 단점

- 악용? 오용? 될 수 있다. 세 함수의 사용 목적이 있는데, 그 기능을 이용하여 본 사용 목적을 너무 벗어나버리면 이 코드를 읽고 작업을 해야 하는 다른 사람의 이해, 가독성에 좋지 않을 수 있다. 

예를들어, 어떤 조건이 참인 경우엔 filter를 쓰는 것이 바람직하다. 비록  map을 사용하여 우회하여 구할 수 있겠지만 더욱 명시적인 방법을 사용하는 것이 바람직하다.

- 세 가지의 metohd는 배열의 요소를 모두 거쳐가므로, 마치 for문과  비슷하다. 하지만, for문은 조건에 따라 break를 걸 수 있지만, 위의 method들은 중간에 break를 할 수 없어 결국 배열 전체의 요소를 거쳐야 한다. 이는 효율성의 문제가 있을 수 있다.

 

오늘은 고차 함수에 대해 간단하게만 설명을 하고, 다음 포스팅에서 고차 함수의 이해를 위해 다양한 문제에 대해 포스팅을 하겠다.

추가사항

  • MapReduce Model?
  • 자바스크립트에서 currying과 클로저 closure의 차이는?
  • 선언형 프로그래밍 (declareative programming)과 절차형 프로그래밍 (imperative programming의 차이?
    • 배열 메서드를 통해 이해하기
  • 함수의 조합(function composition)에 대해 학습하기
728x90
Comments