본문 바로가기

개발인생다반사/TIL(Today i learned)

TIL 210810 - 고차함수(higher-order function)

벌써 3주차다. SE 분야는 배움에 끝이 없다는 느낌이다.

 

경영학 이론이 블루오션 전략에서 더 이상 나아가지 못하고

정체된 느낌을 받고

뒤이서 데이터 분석이나 AI와 같은 컴퓨터 과학으로

대체되는 인상을 받고 있다.

 

그런데 컴퓨터 과학은 그 기술 진화의 속도와 범위가

점차 확장되는 느낌이다.

코.스의 코스도 점차 심화되고 있다.

물론 시작의 시작일 뿐이지만.

넋두리가 길었다. 시작해 보자

 

고차함수 시작!

 

고차함수가 프로그래밍을 산 정상에서 보는 것과 같다고 한다.

복잡한 알고리즘 구현하라고 요구하네.

 

Achievement Goals

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

Advanced Challenges

  • 더 생각해 볼 주제에 대해서 공부하고, TIL을 작성할 수 있다.
  • 아래 키워드는 고차 함수와 관련된 심화 주제에 해당입니다. 위의 모든 레슨을 충분히 이해하고, 코플릿 문제를 모두 다 푸셨다면 아래 키워드를 직접 구글링하여, TIL을 작성해보세요.
    • MapReduce 학습하기 (MapReduce Model)
    • 자바스크립트에서 커링(currying)과 클로저(closure)의 차이 이해하기 (js closure vs curry)
    • 선언형 프로그래밍(declarative programming)과 절차형 프로그래밍(imperative programming)의 차이를 배열 메소드를 통해 이해하기 (js imperative vs declarative)
    • 함수의 조합(function composition)에 대해 학습하기 (javascript function composition)

 

 

First-class citizen

자바스크립트의 퍼스트 클래스가 함수라고 한다.(웃긴 표현)

 

  • 변수에 할당(assignment) 할 수 있다.
  • 다른 함수의 인자(argument)로 전달될 수 있다.
  • 다른 함수의 결과로서 리턴될 수 있다.

함수를 변수에 할당하고 함수를 배열의 요소나

객체의 속성값으로 저장할 수 있다. 대단허다!

 

1. 함수를 변수에 할당 할 때

/*
 * 아래는 변수 square에 함수를 할당하는 함수 표현식입니다.
 * 자바스크립트에서 함수는 일급 객체이기 때문에 변수에 저장할 수 있습니다.
 *
 * 함수 표현식은 할당 전에 사용할 수 없습니다.
 * square(7); // --> ReferenceError: Can't find variable: square
 */

const square = function (num) {
  return num * num;
};

// square에는 함수가 저장되어 있으므로 (일급 객체), 함수 호출 연산자 '()'를 사용할 수 있습니다.
output = square(7);
console.log(output); // --> 49

여기서 주의할 점은

함수 표현식은 변수에 할당 하기 전에는 사용할 수 없다는 것과

호이스팅이 되지 않는다는 점이다.

 

호이스팅 정의(아래 접은 글)

더보기
  • 호이스팅은 선언된 위치에 관계없이 어디서든 함수를 사용할 수 있도록 합니다.
  • 코드가 실행되는 과정에서 함수 선언부를 코드의 최상단으로 끌어올리는 것처럼 보이게 합니다.

 

함수 선언식과 함수 표현식을 비교하는 관점에서

함수 표현식은 함수의 할당과 실행의 위치에 따라 결과가 달라지기 때문에

코드 리뷰나 디버깅을 할 때 코드의 위치를 어느 정도 예측할 수 있다.

 

그래서 고차함수

함수를 인자로 받을 수 있고 

함수의 형태로 리턴할 수 있는 함수이다.

(뭐지 클로저 느낌인데, 다른 점이 있나?)

 

다른 함수(caller)의 인자(argument)로 전달되는 함수를

콜백 함수(callback function)라고 한다.

콜백 함수의 이름은, 어떤 작업이 완료되었을 때 호출하는 경우가 많아서,

답신 전화를 뜻하는 콜백이라는 이름이 붙여졌다.

 

아무튼 결론

함수를 인자로 받는 함수

함수를 리턴하는 함수

모두 고차함수로!!

 

 

1. 다른 함수를 인자로 받는 경우

function double(num) {
  return num * 2;
}

function doubleNum(func, num) {
  return func(num);
}

/*
 * 함수 doubleNum은 다른 함수를 인자로 받는 고차 함수입니다.
 * 함수 doubleNum의 첫 번째 인자 func에 함수가 들어올 경우
 * 함수 func는 함수 doubleNum의 콜백 함수입니다.
 * 아래와 같은 경우, 함수 double은 함수 doubleNum의 콜백 함수입니다.
 */
let output = doubleNum(double, 4);
console.log(output); // -> 8

 

2. 함수를 리턴하는 경우

function adder(added) {
  return function (num) {
    return num + added;
  };
}

/*
 * 함수 adder는 다른 함수를 리턴하는 고차 함수입니다.
 * adder는 인자 한 개를 입력받아서 함수(익명 함수)를 리턴합니다.
 * 리턴되는 익명 함수는 인자 한 개를 받아서 added와 더한 값을 리턴합니다.
 */

// adder(5)는 함수이므로 함수 호출 연산자 '()'를 사용할 수 있습니다.
let output = adder(5)(3); // -> 8
console.log(output); // -> 8

// adder가 리턴하는 함수를 변수에 저장할 수 있습니다.
// javascript에서 함수는 일급 객체이기 때문입니다.
const add3 = adder(3);
output = add3(2);
console.log(output); // -> 5

 

3. 함수를 인자로 받고 함수를 리턴하는 경우

function double(num) {
  return num * 2;
}

function doubleAdder(added, func) {
  const doubled = func(added);
  return function (num) {
    return num + doubled;
  };
}

/*
 * 함수 doubleAdder는 고차 함수입니다.
 * 함수 doubleAdder의 인자 func는 함수 doubleAdder의 콜백 함수입니다.
 * 함수 double은 함수 doubleAdder의 콜백으로 전달되었습니다.
 */

// doubleAdder(5, double)는 함수이므로 함수 호출 기호 '()'를 사용할 수 있습니다.
doubleAdder(5, double)(3); // -> 13

// doubleAdder가 리턴하는 함수를 변수에 저장할 수 있습니다. (일급 객체)
const addTwice3 = doubleAdder(3, double);
addTwice3(2); // --> 8

 

약간 복잡허구먼.

그러니 고차함수지, 달리 고차함수일까?

 

내장 고차함수 이해하기(Built-in higher order functions)

filter 함수

배열의 filter 메소드는, 모든 배열의 요소 중에서 특정 조건을 만족하는 요소를 걸러내는 메소드이다.

걸러내는 기준이 되는 특정 조건은 filter 메소드의 인자로 전달된다.

이때 전달되는 조건은 함수의 형태이다.

filter 메소드는, 걸러내기 위한 조건을 명시한 함수를 인자로 받기 때문에 고차함수이다.

긴 말이 필요없다. 코드를 보고 이해하자.

 

function filterOddLengthWords(words) {
  const isOdds = function(el) {
    return el.length % 2 !== 0;
  }
  return words.filter(isOdds);
}

함수 isOdds는 2의 배수인지 여부를 판별한다.

words의 배열의 요소를 인자로 받아 isOdds 함수에 적용한다.

filter함수가 호출될 때 콜백함수(함수 호출이후에 응답하는 함수)는 isOdds이고

콜백 함수 isOdds는 true 혹은 false를 반환한다.

 

filter메소드는 콜백함수의 결과값 true인 요소만으로 이루어진

새로운 배열을 반환한다.(기존에 처음에 주어진 배열은 수정하지 않는다.)

더보기

filter 메소드는 배열의 요소를, 인자로 전달되는 콜백 함수에 다시 전달합니다.

콜백 함수는 전달받은 배열의 요소를 받아 함수를 실행하고,

콜백 함수 내부의 조건에 따라 참(true) 또는 거짓(false)을 리턴해야 합니다.

 

자기 주도 학습

  • 자바스크립트 배열 메소드 중 고차 함수 학습하기 (js array method)
    • forEach, find, filter, map, reduce, sort, some, every
  • filter 메소드에 들어가는 콜백 함수는 truthy 또는 falsy를 리턴할 수 있습니다. 그러나 filter 메소드에 들어가는 콜백 함수는 Deep equality를 통해 조건을 명확하게 밝히는 걸 권장합니다. 따라서 이 콘텐츠에서도 콜백 함수가 내부 조건에 따라 참(true) 또는 거짓(false)을 리턴하도록 구현하길 권장합니다.

 

map함수

map은 하나의 데이터를 다른 데이터로 맵핑(mapping) 할 때 사용한다.

그래서 인자로 받은 배열의 요소에 동일한 함수를 적용하여

배열로로 다시 반환한다. 즉 기존의 배열은 수정하지 않고

새로운 배열로 반환하는 것이다.

 

function getDoubledElements(arr) {
  const func = function(num) {
    return num * 2;
  }
  return arr.map(func);
}

이해를 돕기 위해 좀 풀어 썼다.

배열 arr를 인자로 받았고 그 배열 arr의 요소를 인자로 받는 함수 func가 실행되고

그 값을 map메소드를 통해 배열로 반환해주었다.

 

reduce함수

reduce 활용 시, 아래 과정을 꼭 기억하세요.

  • 배열의 각 요소를
  • 특정 방법(함수)에 따라
  • 원하는 하나의 형태로
  • 응축합니다. (reduction)

 

꼭 고차함수를 사용해야 하는 이유를 '추상화'를 통해서 설명하고 있다.

추가적으로 학습이 필요한 부분이다.

 

 

이 콘텐츠는 Advanced로, 수료 필수 조건이 아닙니다.

아래 키워드는 고차 함수와 관련된 심화 주제에 해당입니다. 위의 모든 레슨을 충분히 이해하고, 코플릿 문제를 모두 다 푸셨다면 아래 키워드를 직접 구글링하여, TIL을 작성해보세요.

  • MapReduce 학습하기 (MapReduce Model)
  • 자바스크립트에서 커링(currying)과 클로저(closure)의 차이 이해하기 (js closure vs curry)
  • 선언형 프로그래밍(declarative programming)과 절차형 프로그래밍(imperative programming)의 차이를 배열 메소드를 통해 이해하기 (js imperative vs declarative)
  • 함수의 조합(function composition)에 대해 학습하기 (javascript function composition)