Day +21

비트·2023년 5월 10일
0

CodeStates

목록 보기
21/54
post-thumbnail

고차 함수

일급 객체

  • 대표적인 일급 객체 중 하나가 함수
  • JavaScript에서 함수는 아래와 같이 특별하게 취급된다.
    • 변수에 할당(assignment)할 수 있다.
    • 다른 함수의 전달인자(argument)로 전달될 수 있다.
    • 다른 함수의 결과로써 리턴될 수 있다.
  • 함수를 변수에 할당할 수 있기 때문에, 함수를 배열의 요소나 객체의 속성 값으로 저장할 수 있다.

    • 함수를 데이터(string, number, boolean, array, object)처럼 다룰 수 있다.
    1. 변수에 함수를 할당하는 경우

      • // 아래는 변수 square에 함수를 할당하는 함수 표현식입니다.
        // JavaScript에서 함수는 일급 객체이기 때문에 변수에 할당할 수 있습니다.
        //
        // 함수 표현식은 할당 전에 사용할 수 없습니다.
        // square(7); // --> ReferenceError: Can't find variable: square
           
         
         const square = function (num) {
           return num * num;
         };
        
         // 변수 square에는 함수가 할당되어 있으므로 (일급 객체), 함수 호출 연산자 '()'를 사용할 수 있습니다.
         output = square(7);
         console.log(output); // --> 49
  • 함수는 변수에 저장된 데이터를 전달 인자로 받거나, 리턴 값으로 사용할 수 있습니다.

  • 함수도 변수에 저장될 수 있기 때문에 함수를 인자로 받거나, 리턴 값으로 사용할 수 있습니다.


고차 함수의 이해

  • 고차 함수(higher order function) : 함수를 전달인자(argument)로 받을 수 있고, 함수를 리턴할 수 있는 함수
  • 변수에 할당하지 않고 함수를 바로 이용할 수 있습니다.
    • 어떤 고차 함수에 함수를 전달인자로 전달하고, 고차 함수는 함수 자체를 리턴합니다.
    • 변수가 빠졌을 뿐, 동일하게 동작합니다.
  • 이때, 다른 함수(caller)의 전달인자(argument)로 전달되는 함수를 콜백 함수(callback function)라고 합니다.
    • 어떤 작업이 완료되었을 때 호출하는 경우가 많아서, 답신 전화를 뜻하는 콜백 함수라는 이름이 붙여졌습니다.
    • 콜백 함수를 전달받은 고차 함수(caller)는, 함수 내부에서 이 콜백 함수를 호출(invoke)할 수 있고, 조건에 따라 콜백 함수의 실행 여부를 결정할 수도 있습니다.
    • 아예 호출하지 않을 수도 있고, 여러 번 실행할 수도 있습니다.
    • 특정 작업의 완료 후에 호출하는 경우는 이후에 충분히 접할 수 있습니다.

  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

  1. 함수를 리턴하는 경우

    • 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

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

    • 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



내장 고차 함수

내장 고차 함수 이해하기

  • JavaScript에는 기본적으로 내장된 고차 함수가 여럿 있습니다.
  • 그중에서 배열 메서드들 중 일부가 대표적인 고차 함수에 해당합니다.

filter( )

  • filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환합니다.
    • 다음 예는 값이 10 이하인 모든 요소가 제거된 걸러진 배열을 만들기 위해 filter()를 사용합니다.
      • function isBigEnough(value) {
           return value >= 10;
         }
           
         let filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
         // filtered 는 [12, 130, 44]
  • 문제
    • 만화책 식객 27권의 정보가 배열에 담겨있습니다. 출판 연도가 2003년인 단행본만 담은 배열을 만드세요.
  • 수도코드
    • 배열의 각 요소 : 각 식객 1- 27권의 정보
    • 특정 논리(함수) : 책의 출판 연도가 2003년입니다. (true / false)
    • 따로 분류 : 출판 연도가 2003년인 책의 정보
  • 실제 코드

    • ilter는 이렇게 조건에 맞는 데이터만 분류(filtering) 할 때 사용합니다.

    •  // 단행본 모음
       const cartoons = [
         {
           id: 1,
           bookType: 'cartoon',
           title: '식객',
           subtitle: '어머니의 쌀',
           createdAt: '2003-09-09',
           genre: '요리',
           artist: '허영만',
           averageScore: 9.66,
         },
         {
           id: 2,
           // .. 이하 생략
         },
         // ... 이하 생략
       ]; 
      
       // 단행본 한 권의 출판 연도가 2003인지 확인하는 함수
       const isCreatedAt2003 = function (cartoon) {
         const fullYear = new Date(cartoon.createdAt).getFullYear()
         return fullYear === 2003;
       }; 
      
       // 출판 연도가 2003년인 책의 모음
      const filteredCartoons = cartoons.filter(isCreatedAt2003); 

map( )

  • map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다.
    • 다음 코드는 숫자의 배열을 받아 각 숫자들의 제곱근이 들어있는 새로운 배열을 만듭니다.
      • var numbers = [1, 4, 9];
         var roots = numbers.map(Math.sqrt);
         // roots는 [1, 2, 3]
         // numbers는 그대로 [1, 4, 9]
  • 문제
    • 만화책 식객 27권의 정보가 배열에 담겨있습니다. 각 책의 부제(subtitle)만 담은 배열을 만드세요.
  • 수도 코드
    • 배열의 각 요소 : 각 식객 1- 27권의 정보
    • 특정 논리(함수) : 책 한 권의 부제를 찾습니다.
    • 다른 요소로 지정 : 각 식객 1- 27권의 부제
  • 실제 코드

    • map은 이렇게 하나의 데이터를 다른 데이터로 매핑(mapping) 할 때 사용합니다.

    •  // 만화책 모음
       const cartoons = [
         {
           id: 1,
           bookType: 'cartoon',
           title: '식객',
           subtitle: '어머니의 쌀',
           createdAt: '2003-09-09',
           genre: '요리',
           artist: '허영만',
           averageScore: 9.66,
         },
         {
           id: 2,
           // .. 이하 생략
         },
         // ... 이하 생략
       ]; 
      
       // 만화책 한 권의 부제를 리턴하는 로직(함수)
       const findSubtitle = function (cartoon) {
        return cartoon.subtitle;
       }; 
      
       // 각 책의 부제 모음 
      const subtitles = cartoons.map(findSubtitle); // ['어머니의 쌀', ...]

reduce( )

  • reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서 (reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.
    • 배열의 모든 값 합산
      • var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
           return accumulator + currentValue;
         }, 0);
         // sum is 6
         
         //<------------------------------------>//
         
         //화살표 함수로도 표현이 가능하다.
         var total = [ 0, 1, 2, 3 ].reduce(
           ( accumulator, currentValue ) => accumulator + currentValue,
           0
         );
  • 문제
    • 만화책 식객 27권의 정보가 배열에 담겨있습니다. 각 단행본의 평점의 평균을 리턴하세요.
  • 수도코드
    • 배열의 각 요소 : 각 식객 1- 27권의 정보
    • 응축하는 방법 (함수) : 각 단행본의 평점을 누적값에 더합니다.
    • 원하는 형태 : 숫자로 누적합니다.
    • 응축된 결과 : 각 단행본의 평점의 합을 단행본의 길이로 나눈 평점의 평균
  • 실제 코드

    • 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;

reduce의 색다른 사용법

배열을 문자열로

  • 수도 코드
    • 배열의 각 요소 : 유저 정보
    • 응축하는 방법 (함수) : 하나의 유저의 이름과 쉼표를 이어 붙입니다.(concat)
    • 원하는 형태 : 문자열로 누적합니다.
    • 응축된 결과 : 쉼표로 구분되는 모든 유저의 이름
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, '');

배열을 객체로

  • 수도 코드
    • 배열의 각 요소 : 유저 정보
    • 응축하는 방법 (함수) : 유저 한 명의 이름 중 첫 글자를 주소록 객체 속성의 키(key)로, 유저의 정보를 주소록 객체 속성의 값(value)으로 추가합니다.
    • 원하는 형태 : 주소록 객체에 누적합니다.
    • 응축된 결과 : 모든 유저의 정보가 알파벳으로 구분된 주소록
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, {});

profile
Drop the Bit!

0개의 댓글