BEB 07 2-3

Donghun Seol·2022년 9월 21일
0

코드스테이츠 BEB 07

목록 보기
6/39

고차함수

고차함수의 정의

정의에 앞서 알아두어야 할 개념들

일급객체

변수에 할당 가능
다른 함수의 인자로 전달 가능
다른 함수의 결과로 리턴 가능

함수 표현식

const square = num => num*num // function expression

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

함수표현식은 완전히 호이스팅되지 않아 선언전에 사용할 수 없다.
따라서 코드가 절차적으로 엄격해지고, 유지보수가 편해질 수도 있다.

커리 함수

고차함수는 함수를 인자로 받는 함수, 함수를 리턴하는 함수를 의미하는데
커리함수는 고차함수 중 함수를 리턴하는 함수만을 의미한다

그래서 고차함수는 어떻게 생겼죠?

  1. 다른 함수를 인자로 받는 경우
function double(num) {
  return num * 2;
}

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

function processNum(callback, num) {
  return callback(num)
}

let output = processNum(double, 4);
let output2 = processNum(square, 4);
console.log(output); // -> 8
console.log(output2); // -> 16
  1. 함수를 리턴
function adder(added) {
  return function (num) {
    return num + added;
  };
}

let output = adder(5) // output is function
console.log(output(3)) // -> 8

let outputAtOnce = adder(5)(5) // this works since return value of adder(5) is function
console.log(outputAtOnce) // -> 10
  1. 함수를 인자로 받고, 함수를 리턴하는 경우
function double(num) {
  return num * 2;
}

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

// 인자로 받은 func로 process된 결과를 adder와 합한 값을 리턴한다.
function processedAdder(num, func) {
  const processed = func(num);
  return function (adder) {
    return adder + processed;
  };
}

processedAdder(5, square)(3) // -> 28
const addTwice3 = processedAdder(3, double);
addTwice3(3); // -> 9

고차함수 활용

파이프라인 패턴

reduce 또는 reduceRight를 활용하면 간단하게 구현할 수 있다.
reduce의 콜백함수는 인자로 (acc, val, idx, arr)를 받을 수 있다.

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

function add5(num) {
  return num + 5;
}

function mul3(num) {
  return num * 3;
}

function isOdd(num) {
  return num % 2 !== 0;
}

// reduceRight
function pipe(...funcs) {
  // NOTE: reduce's callback args are (acc, val, idx, array) , [initial val]
  return initialVal => funcs.reduce((acc, val, idx) => val(acc), initialVal)
}

output = pipe(square, add5, mul3);
// output's arg is initialVal as above declaration
console.log(output(4)); // --> 63

reduce

객체에 특정 키가 존재하는지 확인하는 방법

// Using in operator
'key' in object

// Using hasOwnProperty() method
object.hasOwnProperty('key')

리듀스 적용해서 사전식으로 객체 정리하기

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

const makeAddressBook = (acc, elem, idx) => {
    const firstChar = elem.name[0];
    if (!acc.hasOwnProperty(firstChar)) {
        acc[firstChar] = [];
    }
    acc[firstChar].push(elem);
    return acc;
};
const resultObj = users.reduce(makeAddressBook, {});
console.log(resultObj);
/*
{
  T: [ { name: 'Tim', age: 40 } ],
  S: [ { name: 'Satya', age: 30 }, { name: 'Sundar', age: 50 } ]
}
*/

왜 고차함수를 사용하는가?

높은 수준의 추상화로 생산성 향상

한 가지 일만 하는 함수를 작성한 뒤, 컴포지션을 통해 합성하고, 이를 조합해서 특정 작업에 활용할 수 있다. 다음과 같은 장점이 있음.
특정 작업만 하는 함수는 좀더 generic하게 활용될 수 있음.

  1. 코드 가독성
  2. 코드 유지보수
  3. 코드 재사용성

이 향상된다.

// bad code 💩 
function getAverageAgeOfMaleAtOnce(data) {
  const onlyMales = data.filter(function (d) {
    // data.filter는 배열의 각 요소에 인자로 전달받은 함수를 적용하고,
    // 그 결과가 true인 요소만을 갖는 배열을 리턴합니다.
    return d.gender === 'male';
  });

  const numOfMales = onlyMales.length;

  const onlyMaleAges = onlyMales.map(function (d) {
    // onlyMales.map는 배열의 각 요소에 인자로 전달받은 함수를 적용하고,
    // 각 결과를 요소로 갖는 배열을 리턴합니다.
    return d.age;
  });

  const sumOfAges = onlyMaleAges.reduce(function (acc, cur) {
    // onlyMaleAges.reduce는 배열의 각 요소에 인자로 전달받은 함수를 적용하고,
    // 각 결과를 두 번째 인자로 전달받은 초기값(0)에 누적한 결과를 리턴합니다.
    return acc + cur;
  }, 0);

  return sumOfAges / numOfMales;
}

// good code ✅
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;
}

// 일반적인 for loop 로 pipe 패턴 구현 👋
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;
  };
}

// reduce 적용한 버전 훨씬 갈끔 👍
function composeSimple(...funcArgs) {
  return initVal => funcArgs.reduce((acc, elem) => {
    return elem(acc);
  }, initVal);
}

// compose를 통해 함수들이 순서대로 적용된다는 것이 직관적으로 드러납니다.
// 각각의 함수는 다른 목적을 위해 재사용(reuse) 될 수 있습니다.
// 합성함수를 통해서 컴포넌트를 재사용하고, 직관적으로 로직을 파악 가능하다 
// 짱 좋군요 😍😍😍😍
const getAverageAgeOfMale = compose(
  getOnlyMales, // 배열을 입력받아 배열을 리턴하는 함수
  getOnlyAges, // 배열을 입력받아 배열을 리턴하는 함수
  getAverage // 배열을 입력받아 `number` 타입을 리턴하는 함수
);

const result = getAverageAgeOfMale(data);
console.log(result); // --> 26

forEach, find, sort, some, every 정리

some 콜백함수를 만족하는 조건이 하나라도 있는지
every 모두 콜백함수를 만족하는지

profile
I'm going from failure to failure without losing enthusiasm

0개의 댓글