JS/Node | 고차함수

SURI·2021년 12월 5일
0

1. 개념


1.1 일급객체의 3가지 특징

  1. 변수에 할당 가능
    • 함수를 배열의 요소나 객체의 속성값으로 저장할 수 있다.
  2. 다른 함수의 인자로 전달 가능
    • 함수에 인자로 전달되는 함수를 콜백함수라고 부른다.
  3. 다른 함수의 결과로서 리턴 가능
    • 함수를 리턴하는 함수를 커리함수라고 한다.

자바스크립트에서 함수는 일급객체이다. 즉, 문자열, 숫자 같은 다른 데이터처럼 사용 가능하다.

호이스팅

함수 표현식과 선언식의 차이점은 호이스팅이다. 함수 표현식은 호이스팅이 적용되지 않는다. 호이스팅은 선언된 위치에 관계없이 어디서든 함수를 사용할 수 있도록 한다. 코드가 실행되는 과정에서 함수 선언부를 코드의 최상단으로 끌어올리는 것처럼 보이게 한다.

메소드

객체 안에 내장되어 있는 함수를 메소드라고 한다. 배열 안에 프로토타이프(원형객체)가 있고, 그 안에 메소드가 정의되어 있기 때문에, 배열 메소드를 사용할 수 있다.

1.2 고차 함수

함수를 인자로 받는 함수이거나 함수를 리턴하는 함수이거나 둘 다인 경우에도 고차함수이다.

1.3 배열 내장 고차함수

자바스크립트에는 기본 내장된 고차함수가 여럿 있다. 배열 메소드 중 일부가 대표적인 고차함수에 해당한다.

arr.filter()

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

function keep(arr, keeper) {
  return arr.filter( el => { // 콜백 함수의 실행값이 true인 요소들로 새로운 배열을 리턴하는 filter 함수
    return el === keeper; // boolean 값을 리턴하는 콜백 함수
  })
}
  • 걸러내는 기준이 되는 조건(함수)를 인자로 받는다.
  • 조건을 만족하는(true) 배열의 요소를 가진 새로운 배열을 실행값으로 리턴한다.
  • 입력받은 배열을 수정하지 않는다.
  • filter 메소드에 들어가는 콜백 함수는 Deep equality를 통해 조건을 명확하게 밝히길 권장한다.

=> 배열의 각 요소에 콜백 함수를 적용시켰을 때, true를 리턴하는 요소를 모아 새로운 배열을 리턴한다.

arr.map()

배열의 각 요소가 특정 논리(함수)에 의해 다른 요소로 지정(map) 된다.

function getLengthOfElements(arr) {
  return arr.map( el => {
    return el.length; // 배열의 각 요소의 길이를 구해 새로운 배열을 리턴한다.
  })
}
  • 적용하는 논리(함수)를 인자로 받는다.
  • 배열의 각 요소에 콜백 함수를 적용시킨 새로운 배열을 실행값으로 리턴한다.
  • 입력받은 배열은 수정하지 않는다.
  • 콜백함수에 두 번째 인자를 줄 경우에 인덱스를 가져온다.


조건문을 걸어줬을 때, 해당하지 않는 요소가 있는 경우에는 자동으로 undefined를 반환하게 된다.

arr.reduce()

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

function computeProductOfAllElements(arr) {
  return arr.reduce((acc, cur) => {
    return acc * cur;
    // acc = acc * cur;
    // return acc; 이렇게 해도 맞게 되는 것 같다. 흠!
  }, 1)
}

  • acc : 누적값. 초기값이 없으면 배열의 첫 번째 요소가 acc가 된다.
  • cur : 현재 요소
  • 배열의 각 요소를 콜백 함수에 맞게 하나로 응축시킨 값을 리턴한다.

arr.sort()

let = age = [3, 5, 2, 1, 7];

// 오름차순
age.sort((a, b) => a - b)

// 내림차순
age.sort((a, b) => b - a)

// objArr의 경우,
const student = [
    { name : "suri", age : 29},
    { name : "masuri", age : 25},
    { name : "hoon", age : 8},
    { name : "wook", age : 95}
]

// 오름차순
student.sort(function(a, b) {
	return a.age - b.age;
}
             
//  내림차순
student.sort(function(a, b) {
	return b.age - a.age;
}
  • arr.sort는 원본 배열을 수정한다.
  • 객체를 요소로 하는 배열에 대해서도 오름/내림차순을 적용해볼 수 있다.
  • 오름차순과 내림차순을 이렇게 쉽게 도와주는 내장 메소드가 있었다. 알고리즘 문제풀이에 적용하지 못한 게 아쉽다.

기타 고차함수

forEach, find, filter, map, reduce, sort, some, every

배열 <-> 객체 (배열 내장 메소드 활용)

// obj -> arr(이차원 배열)
export function objToArr(obj) {
  let outerArr = [];

  for (let key in obj) {
    let innerArr = [];
    innerArr.push(key, obj[key]);
    outerArr.push(innerArr);
  }
  return outerArr;
}  

// arr(이차원 배열) -> obj
export function arrToObj(arr) {
  return arr.reduce((acc, cur) => {
    return {
      ...acc,
      [cur[0]] : cur[1]
    }
  }, {})
}

===비교===
export function objArrToArr(objArr, name){
	objArr.reduce((acc, cur) => {
      acc.push(cur.name)
      return acc;
    }, []);
}
  
// [obj, obj, obj] -> [arr, arr, arr]
export function objArrToArrArr(objArr) {
  return objArr.map(el => {
    let outerArr = [];
    for (let key in el) {
      let innerArr = [];
      innerArr.push(key, el[key])
      outerArr.push(innerArr);
    }
    return outerArr;
  })
}

===비교 reduce 활용===
// [obj, obj, obj] -> [arr, arr, arr]
export function objArrToArrArr(objArr) {
  return objArr.reduce((acc, cur) => {
    let outerArr = [];
    for (let key in cur) {
      let innerArr = [];
      innerArr.push(key, cur[key]);
      outerArr.push(innerArr);
    }
    acc.push(outerArr);
    return acc;
  }, []);
}
  
  • reduce() 활용해서 배열을 객체로 바꾸는 부분에서 버벅거리는 부분이 있었다.
    • 전개 문법을 활용해서 객체 안에 값들을 쌓아나가는 아이디어는 이제 이해가 되는 것 같다.
    • cur[0]을 대괄호에 담아서 키를 표현해주는 것.
    • 콜론 대신 할당 연산자를 쓴 것.
    • 초기값 설정을 안 해준 것.

1.4 추상화

추상화는 복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것이다. 추상화는 생산성을 향상시키는 이점이 있다.

  • 함수는 사고 혹은 논리의 묶음이다.
  • 함수는 값을 인자로 받아 값을 리턴한다. 리턴하는 값에 대한 로직은 감추어져 있다. 이는 값 수준에서의 추상화다.
  • 고차 함수는 함수를 전달받아 처리하는 사고 수준에서의 추상화다.

2. 코플릿 정리


함수를 리턴하는 함수

  • returnFunction()(); 이렇게 해야 'Hello HoF!'가 리턴된다.

함수를 인자로 받는 함수

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

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

function compose(func1, func2, num) {
  return func1(func2(num));
}
  • 위와 아래는 차이가 있는 거 같다. 아래는 인자로 받은 함수의 실행값을 리턴하고 있다.

함수를 인자로 받고 함수를 리턴하는 함수

function compose2(func1, func2) {
  return (number) => {
    return func1(func2(number));
  }
}

가변하는 함수의 개수를 인자로 받는 경우

function pipe(...args) {
  return (num) => {
    let result = num;    
    for (let i = 0; i < args.length; i++) {
      result = args[i](result);
    }
    return result;
  }
}
  • 반복해서 넣어주려고 할 때, 어떻게 짜면 좋을지 감이 안 왔는데 로직을 보고 나서 아하! 했다.
  • 이 로직을 고차함수로 밖에 할 수 없는건가?

map 메소드의 원리

function mapCallback(func, arr) {
  let newArr = [];
  for (let el of arr) {
    newArr.push(func(el));
  }
  return newArr;
}
  • 배열의 각 요소에 함수가 적용된 새로운 배열을 리턴한다.

filter 메소드의 원리

unction filterCallback(func, arr) {
  let newArr = [];

  for (let el of arr) {
    if (func(el)) {
      newArr.push(el);
    }
  }
  return newArr;
}

filter 메소드의 실행값은 배열이다.

 return arr.filter(function (el) {
    return el < num;
  }).length;

[1, 2, 3].length // 3 결국 이것과 같다. 
  • 이렇게 길이를 구하는 거 낯설었다.

콜백 함수를 따로 빼서 적어줄 수도 있다.

function lessThan100(number) {
  return number < 100;
}

//...
return arr.filter(lessThan100)
  • lessThan100(el) 이렇게 해야하나? 헷갈렸는데 아니었다. 함수이름만 적어서 함수 그 자체를 불러와줘야 한다.

배열이 비었는데도 초기값을 주지 않고 reduce() 메소드를 실행할 경우 오류가 발생한다.

  • TypeError: Reduce of empty array with no initial value

reduce() 콜백함수를 따로 써줄 수 있다. (질문O)

function joinName(resultStr, user) {
    return resultStr + user.name + ', ';
  	// 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, '

reduce() 콜백함수에 조건문을 사용하는 것으로 필터 함수를 대체할 수도 있겠구나.

3. 에러로그


number.length 가령 3.length는 문법적 오류가 뜬다.

reduce 메소드를 활용해 이차원 배열을 단일배열로 리턴하기

  • 나는 return 없는 map을 활용해서 조금은 특이하게, 구현은 했지만 reduce의 아이디어를 한 번 이해해보자.
  • reduce 메소드는 배열로 리턴이 정해져있는 게 아니다. 사용자가 의도한 하나의 값으로 응축된다! 왜 reduce를 떠올리지 못했을까?

31번

  • 배열의 각 요소에 대해서 수정사항들을 만든다. -> 그리고 실행값을 정한다. -> 그 실행값들이 다시 모여 배열의 요소가 되고 그 새로운 배열이 map 메소드의 실행값이다.
  • 내가 쓴 답도 맞았는데, 혹시 틀린 게 아닐까 생각했지만.. 결국 같은 말인 것 같다. 나는 map 메소드의 콜백 함수에서 실행값을 만들지 않았었다. 그저 수정만 했다.

4. 질문


profile
Every step to become a better version of me 🚶‍♂️ 블로그 이사중

0개의 댓글

관련 채용 정보