CodeStates/JavaScript

Section2 / Unit1 : JS 고차함수

yeeendy 2023. 5. 10. 21:37

코드스테이츠 수업 21일 차!

어느덧 한 달이 지나고 섹션2에 들어왔는데 어제 한 달 회고하면서 굳건히 다졌던 마음이 하루만에 무너질 뻔했다. 너무 어려웠다🫠

어제 다잡았던 마음을 벌써 까먹지 않기 위해 다시 회상해보자.

다른 분들의 열정 가득한 회고를 보니 무너졌던 멘탈에 아주 큰 동기부여가 됐다.

모두가 열심히 하고 있고 스스로와 싸우며 이겨내려 노력하고 있었다.

뒤쳐지지 않게 공부하자.

개념이 바로바로 인지가 안 되는 것 같으니 블로깅 하면서 다시 복습해야겠다. 될 때까지 가보자고


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

 

일급 객체의 특징
  • JavaScript의 대표적인 일급 객체 중 하나가 함수
  1. 변수에 할당할 수 있다.
  2. 다른 함수의 전달인자로 전달될 수 있다.
  3. 다른 함수의 결과로써 리턴될 수 있다.
//변수에 함수를 할당하는 경우
//함수 표현식은 변수에 할당한 다음 사용할 수 있습니다.
const square = function(num){
    return num * num;
}
output = square(7);
console.log(output);  //49

 

고차 함수(higher order function)
  • 함수를 전달인자로 받을 수 있고, 함수를 리턴할 수 있는 함수
  • 함수는 변수에 저장할 수 있다.
  • 함수를 담은 변수를 전달인자로 받을 수 있다.
  • 함수 내부에서 변수에 함수를 할당할 수 있다.
    그리고 함수는 이 변수를 리턴할 수 있다.
    여기서 변수에 할당하지 않고 함수를 바로 이용할 수 있다.
  • 어떤 고차 함수에 함수를 전달인자로 전달하고, 고차 함수는 함수 자체를 리턴한다.
  • 콜백 함수(callback function) : 다른 함수의 전달인자로 전달되는 함수
  • 콜백 함수를 전달받은 고차 함수는, 함수 내부에서 이 콜백 함수를 호출할 수 있고,
    조건에 따라 콜백 함수의 실행 여부를 결정할 수도 있다.
  • 커링 함수 : 함수를 리턴하는 함수
  • 고차 함수가 커링 함수를 포함한다.
//다른 함수를 인자로 받는 경우
function double(num){
    return num * 2;
}
function doubleNum(func, num){
    return func(num);
}
//아래 경우, double은 doubleNum의 콜백 함수
let output = doubleNum(double, 4);
console.log(output);  // 8
//함수를 리턴하는 경우
function adder(added){
    return function(num){
    	return num + added;
    }
}
let output = adder(5)(3); 
console.log(output);  // 8
//함수는 일급 객체이기 때문에 adder가 리턴하는 함수를 변수에 저장할 수 있다.
const add3 = adder(3);
output = add3(2);
console.log(output);  // 5
//함수를 인자로 받고, 함수를 리턴하는 경우
function double(num){
    return num * 2;
}
function doubleAdder(added, func){
    const doubled = fun(added);
    return function(num){
        return num + doubled;
    }
}
//doubleAdder의 인자 func는 함수 doubleAdder의 콜백 함수입니다.
//함수 double은 함수 doubleAdder의 콜백으로 전달되었습니다.
//doubleAdder(5, double)는 함수
doubleAdder(5, double)(3);  // 13
//doubleAdder가 리턴하는 함수를 변수에 저장할 수 있다.
const addTwice3 = doubleAdder(3, double);
addTwice3(2); // 8

 

 

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

 

추상화
  • 복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것
  • 생산성(productivity)의 향상
  • 추상화 관점에서, 함수는 사고(thought) 또논 논리(logic)의 묶음
  • 함수를 통해 얻은 추상화를, 한 단계 더 높인 것이 고차 함수
  • 함수 = 값을 전달받아 값을 리턴한다. = 값에 대한 복잡한 로직은 감추어져 있다 = 값 수준에서의 추상화
  • 값 수준의 추상화 : 단순히 값을 전달받아 처리하는 수준
  • 사고의 추상화 : 함수(사고의 묶음)를 전달받아 처리하는 수준
  • 고차 함수 = 함수를 전달받거나 함수를 리턴한다 = 사고(함수)에 대한 복잡한 로직은 감추어져 있다 = 사고 수준에서의 추상화

 


코플릿 고차함수 오답노트

17번

<문제>
수를 요소로 갖는 배열을 입력받아 각 요소가 2의 배수인지에 대한 정보를 요소로 갖는 새로운 배열을 리턴해야 합니다.

통과 안 된 코드

// 전체 통과 X
function checkEvenOrNot(arr) {
  // TODO: 여기에 코드를 작성합니다.
  const newArr = function(el){
    if(el%2 === 0) return 'ok'
    else if(el === 0) return 'no'
    else return 'no'
  }
  return arr.map(newArr)
}

통과된 코드

// 전체 통과 O
function checkEvenOrNot(arr) {
  // TODO: 여기에 코드를 작성합니다.
  const newArr = function(el){
    if(el%2 !== 0) return 'no'
    else if(el === 0) return 'no'
    else return 'ok'
  }
  return arr.map(newArr)
}

조건문은 거름망이라고 생각하면 된다고 했다. 아래로 내려갈 수록 세분화 된다고

그래서

if(el%2 === 0) return 'ok'
else if(el === 0) return 'no' 이 구문에서 

0도 0%2 는 0이므로 'no'가 안 먹히는 거였다

 

따라서 아래코드처럼 순서를 바꾸면 해결된다.

function checkEvenOrNot(arr) {
  // TODO: 여기에 코드를 작성합니다.
  const newArr = function(el){
    if (el === 0) return 'no'
    else if(el%2 === 0) return 'ok'
    else return 'no'
  }
  return arr.map(newArr)
}

20번

<문제>
객체와 키를 입력받아 키에 해당하는 값이 배열인 경우, 배열의 각 요소를 제곱한 새로운 배열을 리턴해야 합니다.

통과 안 된 코드

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

function getSquaredElementsAtProperty(obj, property) {
  // TODO: 여기에 코드를 작성합니다.
  if(Array.isArray(obj.property)){
    return obj.property.map(square);
  }
  return [];
}

통과 된 코드

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

function getSquaredElementsAtProperty(obj, property) {
  // TODO: 여기에 코드를 작성합니다.
  if(Array.isArray(obj[property])){
    return obj[property].map(square);
  }
  return [];
}

obj.property 와 obj[property]의 차이인데 아직 개념이 덜 인지돼서 그런지 헷갈렸다

property는 변수이므로 []를 사용해서 집어넣어야 한다.

 

obj.key는 객체의 속성 키가 문자열일 때 사용하는 구문, 객체에서 속성 이름이 고정된 경우에만 사용

obj[key]는 객체의 속성이름이 동적으로 사용될 때 사용

 

그리고 return []; 을 사용한 이유는

property 속성이 존재하지 않거나 배열이 아닌 경우 빈 배열을 반환,

함수의 반환 값이 항상 배열 형태이기 위해 사용


21번

<문제>
개인 정보를 담고 있는 객체를 요소로 갖는 배열을 입력받아 18세 이상인 사람의 이름을 요소로 갖는 배열을 리턴해야 합니다.

통과 안 된 코드

function getOnlyAllowedToDrink(arr) {
  // TODO: 여기에 코드를 작성합니다.
  const arrAge = arr.filter(function(person){
    return person[age] >= 18
  })
  const arrName = arrAge.map(function(person){
    return person[name];
  })
  return arrName;
}

통과 된 코드

function getOnlyAllowedToDrink(arr) {
  // TODO: 여기에 코드를 작성합니다.
  const arrAge = arr.filter(function(person){
    return person.age >= 18
  })
  const arrName = arrAge.map(function(person){
    return person.name;
  })
  return arrName;
}

20번과 비슷한 상황이다

 

person[age]와 person[name]은 변수 age와 name을 찾으려고 시도하지만

정의되어 있지 않은 변수들이므로 ReferenceError를 발생시킨다

 

person.age와 person.name은 age와 name 대신 person 객체의 age와 name 속성에 직접 접근한다

따라서 이 방식이 올바르다


27번

<문제>
문자열을 요소로 갖는 배열을 입력받아 배열에서 가장 긴 문자열을 리턴해야 합니다.

나도 통과한 코드기는 한데 효재님 코드가 더 짧고 간결하게 표현하셨다

//내 코드
function getLongestElement(arr) {
  // TODO: 여기에 코드를 작성합니다.
  return arr.reduce(function (a, b){
    if(a.length >= b.length){
      return a
    }else{
      return b
    }
  },'')
}

//효재님 코드
function getLongestElement(arr) {
    return arr.reduce((acc, cur) => acc.length < cur.length ? cur : acc, '')
}

코드를 간결하게 표현하기 위해 노력하자