(SEB_FE) Section2 Unit1 고차 함수

PYM·2023년 3월 13일
0

(SEB_FE) SECTION2

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

  • 배열 내장 고차 함수 filter에 대해서 이해할 수 있다.
  • filter에 대한 이해를 기반으로, 나머지 내장 고차 함수를 학습할 수 있다.
  • filter, map, reduce, forEach, find, sort, some, every
  • 고차 함수를 쓰는 이유를 설명할 수 있다.
  • 고차 함수를 활용하여 프로그램을 작성할 수 있다.

  • 추상화에 대해 설명할 수 있다.
  • 추상화의 관점에서 고차 함수가 갖는 이점에 대해 설명할 수 있다.
  • 고차 함수를 통해 사고 수준에서의 추상화를 달성할 수 있다.

🏀일급 객체

JavaScript에는 비행기의 first-class 좌석처럼 특별한 대우를 받는 일급 객체(first-class citizen) 가 있다. 대표적인 일급 객체 중 하나가 함수.
JavaScript에서 함수는 아래와 같이 특별하게 취급된다.

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

즉 ,함수를 데이터(string, number, boolean, array, object)처럼 다룰 수 있다.


🏀고차 함수

  • 함수를 전달인자(argument)로 받을 수 있고, 함수를 리턴할 수 있는 함수

  • 이때 다른 함수(caller)의 전달인자(argument)로 전달되는 함수를
    콜백 함수(callback function)라고 한다.

    • 콜백 함수를 전달받은 고차 함수(caller)는,
    1. 함수 내부에서 이 콜백 함수를 호출(invoke)할 수 있고
    2. 조건에 따라 콜백 함수의 실행 여부를 결정할 수도 있다.
    3. 아예 호출하지 않을 수도 있고, 여러 번 실행할 수도 있다.
  • "커링 함수" 란? ➡ 함수를 리턴하는 함수

//함수 adder는 다른 함수를 리턴하는 고차 함수
function adder(added) {
  return function (num) {
    return num + added;
  };
}

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

// javascript에서 함수는 일급 객체이기 때문에,
//adder가 리턴하는 함수를 변수에 저장할 수 있다
const add3 = adder(3);
output = add3(2);

🏀내장 고차 함수

JavaScript에는 기본적으로 내장된 고차 함수가 여럿 있다!
그중에서 배열 메서드들 중 일부가 대표적인 고차 함수에 해당
filter, map, reduce 가 있다. 지금부터 하나씩 알아보자!

🏅filter

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

  • 걸러내는 기준인 특정 조건은 함수의 형태로, filter 메서드의 전달인자로 전달
    ➡ 즉, 걸러내기 위한 조건을 명시한 함수를 전달인자로 받기 때문에 고차 함수

  • filter 메서드는 배열의 요소를 콜백 함수에 다시 전달 ➡ 콜백 함수는 전달받은 배열의 요소를 받아 함수를 실행하고, 콜백 함수 내부의 조건에 따라 참(true) 또는 거짓(false)을 리턴

// 함수 표현식
const isEven = function (num) {
  return num % 2 === 0;
};

let arr = [1, 2, 3, 4];

// '짝수'를 판별하는 함수가 조건으로서 filter 메서드의 전달인자로 전달됨
let output = arr.filter(isEven);
console.log(output); // ->> [2, 4]

🏅map

  • 하나의 데이터를 다른 데이터로 매핑(mapping) 할 때 사용

  • 어떤 배열에 있는 모든 요소들의 값을 변경해서 만든 새로운 배열을 써야 할 때

let users = [
  {firstName : "Susan", lastName: "Steward"},
  {firstName : "Daniel", lastName: "Longbottom"},
  {firstName : "Jacob", lastName: "Black"}
];

let userFullnames = users.map(function(element){
    return `${element.firstName} ${element.lastName}`;
})

console.log(userFullnames);
// ["Susan Steward", "Daniel Longbottom", "Jacob Black"]

🏅reduce

  • 여러 데이터를, 하나의 데이터로 응축(reduce)할 때 사용

  • 배열의 각 값에 함수를 적용한 누적 값을 반환

  • 초기값이 있으면 배열의 첫번째 값이 바로 현재 값(초기값에 누적해 주는 값)이되고, 초기값이 없다면 배열의 첫번째 값이 초기값이 된다.

// 단행본 모음
const cartoons = [
  {
    id: 1,
    bookType: 'cartoon',
    title: '식객',
    subtitle: '어머니의 쌀',
    createdAt: '2003-09-09',
    genre: '요리',
    artist: '허영만',
    averageScore: 9.66,
  },
  {
    id: 2,
    // .. 이하 생략
  },
  // ... 이하 생략
];

// 단행본 한 권의 평점을 누적값에 더한다.
const scoreReducer = function (sum, cartoon) {
  return sum + cartoon.averageScore;
}; 

// 초기값에 0을 주고, 숫자의 형태로 평점을 누적한다.
let initialValue = 0 
// 모든 책의 평점을 누적한 평균을 구한다.
const cartoonsAvgScore = cartoons.reduce(scoreReducer, initialValue) / cartoons.length;
const addAccCur = function (acc, cur) {
  return acc + cur;
};
const initialValue = 1;

let arr = [1, 2, 3, 4, 5, 6, 7];
let output = arr.reduce(addAccCur, initialValue);
// 만약 initialValue가 없다면 arr.reduce(addAccCurr) 라고 쓰고 
// arr의 첫번째 요소인 1이 initialValue가 된다. 

console.log(output); // -> 29
// initialValue 없을 경우 28

💡reduce의 색다른 사용법!

1. 배열을 문자열로

//콜백 함수 joinName은 users 배열 안에 있는 요소의 이름을 하나로 응축
function joinName(resultStr, user) {
  resultStr = resultStr + user.name + ', ';
  return resultStr;
}

let users = [
  { name: 'Tim', age: 40 },
  { name: 'Satya', age: 30 },
  { name: 'Sundar', age: 50 }
];

users.reduce(joinName, ''); // -> Tim, Satya, Sundar,  

2. 배열을 객체로

// 콜백 함수 makeAddressBook은 users 배열 안에 있는 요소로 주소록을 만듬
function makeAddressBook(addressBook, user) {
  let firstLetter = user.name[0];

  if(firstLetter in addressBook) {
    addressBook[firstLetter].push(user);
  } else {
    addressBook[firstLetter] = [];
    addressBook[firstLetter].push(user);
  }

  return addressBook;
}

let users = [
  { name: 'Tim', age: 40 },
  { name: 'Satya', age: 30 },
  { name: 'Sundar', age: 50 }
];

users.reduce(makeAddressBook, {}); 
/*
{
T: [{ name: 'Tim', age: 40 }], 
S: [ { name: 'Satya', age: 30 },
  { name: 'Sundar', age: 50 }]
}
*/

🏀고차 함수의 중요성

🏅추상화(abstraction)

  • 복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것

  • JavaScript를 비롯한 많은 프로그래밍 언어 역시, 추상화의 결과이다!
    ➡ 컴퓨터를 구성하는 장치(중앙처리장치, CPU; Central Processing Unit)는 0과 1만 이해한다

  • 하지만 우리가 0과 1이 아닌, 인간의 언어로 코드를 짜고, 그걸 실행했을때 원하는 결과 값을 얻을 수 있다. 이는 모두 그 복잡한 과정을 JavaScript 해석기(엔진)가 대신해 주기 때문!

  • 즉, 컴퓨터의 내부 구조에 대한 고민이 추상화로 해결된 것.
    이로 인해 우리는 JavaScript의 문법(syntax)을 올바르게 사용하는 것만으로, JavaScript가 없었을 때보다 다양한 프로그램을 보다 쉽게 작성할 수 있다.

  • 자주 반복해서 사용하는 로직은 별도의 함수로 작성하는 것 역시 추상화
    값 수준의 추상화: 단순히 값(value)을 전달받아 처리하는 수준

    • 함수를 통해 얻은 추상화를, 한 단계 더 높인 것이 고차 함수
      사고의 추상화: 함수(사고의 묶음)를 전달받아 처리하는 수준

      즉, 고차 함수를 통해, 보다 높은 수준(higher order)에서 생각할 수 있다

이처럼 고민거리가 줄어들고, 그래서 문제의 해결이 더 쉬워지는 것이 추상화의 이점. 즉 추상화 = 생산성(productivity)의 향상 이라고 볼 수 있다.

💡고차 함수 예시

function getOnlyMales(data) {
  return data.filter(function (d) {
    return d.gender === 'male';
  });
}

function getOnlyAges(data) {
  return data.map(function (d) {
    return d.age;
  });
}

function getAverage(data) {
  const sum = data.reduce(function (acc, cur) {
    return acc + cur;
  }, 0);
  return sum / data.length;
}

function compose(...funcArgs) {
  // compose는 여러 개의 함수를 인자로 전달받아 함수를 리턴하는 고차 함수
  // compose가 리턴하는 함수(익명 함수)는 임의의 타입의 data를 입력받아,
  return function (data) {
    // funcArgs의 요소인 함수들을 차례대로 적용(apply)시킨 결과를 리턴
    let result = data;
    for (let i = 0; i < funcArgs.length; i++) {
      result = funcArgs[i](result);
    }
    return result;
  };
}

// compose를 통해 함수들이 순서대로 적용된다는 것이 직관적으로 드러난다
// 각각의 함수는 다른 목적을 위해 재사용(reuse) 될 수 있다.
const getAverageAgeOfMale = compose(
  getOnlyMales, // 배열을 입력받아 배열을 리턴하는 함수
  getOnlyAges, // 배열을 입력받아 배열을 리턴하는 함수
  getAverage // 배열을 입력받아 `number` 타입을 리턴하는 함수
);

const result = getAverageAgeOfMale(data);
console.log(result); // --> 26
profile
목표는 "함께 일하고 싶은, 함께 일해서 좋은" Front-end 개발자

0개의 댓글