My Dream Being Visualized

고차함수란? 본문

Backend/Javascript

고차함수란?

마틴킴 2022. 1. 25. 23:34
728x90

 개인 공부를 위한 공간입니다. 틀린 부분 지적해주시면 감사하겠습니다 (_ _)

 

고차 함수에 대해서 공부하게 되면서,

너무너무너무너무 잘 정리된 글이 있어서 번역을 하였습니다.

원본은 아래에 참고 바랍니다!

https://blog.bitsrc.io/understanding-higher-order-functions-in-javascript-75461803bad

 

 


자바스크립트의 고차함수(Higher-Order Functions) 이해하기.

 

자바스크립트를 배우고 있다면, 고차 함수에 대한 단어를 들어보았을 것이다. 복잡해 보여도 실은 그렇지 않다.

 

함수형 프로그래밍에 자바스크립트가 적합한 이유는, 고차 함수를 사용하기 때문이다.

 

고차함수는 자바스크립트에서 많이(extensively) 사용된다. 자바스크립트로 프로그래밍을 해왔다면, 고차함수인지 알지 못하고 사용했을 수도 있다.

 

이 컨셉을 완벽하게 이해하기 위해서, 함수형 프로그래밍FIrst-Class 함수가 무엇인지 알아야 한다.

 

함수형 프로그래밍이 무엇인가?

간단하게 이야기하자면, 함수형 프로그래밍은 다른 함수에 파라미터로 함수를 전달하고 반환값으로 함수를 리턴할 수 있는 프로그래밍의 형태이다. 함수형 프로그래밍에서는 함수의 개념으로 생각하고 코딩한다.

 

자바스크립트, Haskell, Clojure, Scala, Erlang은 함수형 프로그래밍을 구현하는 언어이다.

 

First-Class 함수

자바스크립트를 배워왔다면, 자바스크립트는 함수를 first-class citizens으로 대한다는 것을 들어 봤을 것이다. (JavaScript treats functions as first-class citizens.) 이는 자바스크립트 또는 다른 함수형 프로그래밍 언어에서 함수는 오브젝트이기 때문이다.

 

자바스크립트에서 함수는 특별한 형태의 오브젝트이다. 함수는 Function 객체이다. 예를들어,

function greeting() {
  console.log('Hello World');
}
// 함수 호출
greeting();  // 'Hello World'

자바스크립트에서 함수가 오브젝트라는 것을 증명하기 위해서, 아래와 같이 할 수도 있다.

// 오브젝트와 같이 함수의 프로퍼티를 추가할 수 있다.
greeting.lang = 'English';

console.log(greeting.lang); // 'English'

참고 - 자바스크립트에서 이를 허용 해주지만, 안 좋은 방법으로 여겨진다. 함수 오브젝트에 랜덤한 프로퍼티들을 추가하지 않아야 하며 대신에 오브젝트를 사용해야 한다.

 

자바스크립트에서, object, string, number와 같은 다른 타입들과 할 수 있는 모든 것들은 함수로도 할 수 있다. 다른 함수에 파라미터로 전달할 수도 있고 (콜백), 변수에 할당하고 다른 곳으로 넘길 수도 있다. 이 이유때문에 자바스크립트에서 함수는 First-Class 함수라고 알려진다.

 

변수에 함수 할당하기

자바스크립트에서는 변수에 함수를 할당할 수 있다.

const square = function(x) {
  return x * x;
}
// prints 25
square(5);

또한 넘길 수도 있다.

const foo = square;
// prints 36
foo(6);

 

인자에 함수 넘기기

다른 함수에 인자로 함수를 넘길 수도 있다.

function formalGreeting() {
  console.log("How are you?");
}
function casualGreeting() {
  console.log("What's up?");
}
function greet(type, greetFormal, greetCasual) {
  if(type === 'formal') {
    greetFormal();
  } else if(type === 'casual') {
    greetCasual();
  }
}
// prints 'What's up?'
greet('casual', formalGreeting, casualGreeting);

이제 우리는 first-class 함수가 무엇인지 알게 되었으니, 자바스크립트 내 고차함수(Higher-Order function)에 대해서 알아보자.

 

고차함수(Higher-order Functions)

고차 함수란 함수를 인자로 받거나 또는 함수를 반환함으로써 다른 함수에(operate on) 작동 하는 함수를 말한다. 간단히 말해서, 고차 함수는 함수를 인자로 받거나 함수를 output으로 반환하는 함수를 말한다.

예를들어, Array.prototype.map, Array.prototype.filter, Array.prototype.reduce는 자바스크립트 내에 내재되어있는 함수들이다.

 

고차함수의 동작

내재된 고차함수의 몇가지 예들을 보면서 고차함수를 쓰지 않았을 때의 경우와 비교를 해보자.

 

Array.prototype.map

map() 메서드는 input 배열의 모든 element가 인자로 제공된 콜백함수를 호출함으로써 새로운 배열을 생성한다. 콜백 함수로부터 반환 값을 받아서 그 반환 값으로 새로운 배열을 생성한다.

map() 메서드로 전달된 콜백 함수는 element, index, array라는 3개의 인자를 받는다.

예를 한번 보도록 하자.

 

예 #1

숫자로 이루어진 배열이 있으며 그 배열의 각 숫자의 값이 2배가 된 새로운 배열을 만들고 싶다고 가정하자.

Without 고차함수

const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i++) {
  arr2.push(arr1[i] * 2);
}
// prints [ 2, 4, 6 ]
console.log(arr2);

With 고차함수 map

const arr1 = [1, 2, 3];
const arr2 = arr1.map(function(item) {
  return item * 2;
});
// array function 문법을 사용해서 더 간단하게 만들 수도 있다.
// const arr2 = arr1.map(item => item * 2)
// prints [ 2, 4, 6 ]
console.log(arr2);

예 #2

각 사람의 태어난 연도를 가진 배열이 있고 그들의 나이를 보여주는 배열이 있다고 가정하자.

Without 고차함수

const birthYear = [1975, 1997, 2002, 1995, 1985];
const ages = [];
for(let i = 0; i < birthYear.length; i++) {
  let age = 2018 - birthYear[i];
  ages.push(age);
}
// prints [ 43, 21, 16, 23, 33 ]
console.log(ages);

With 고차함수 map

const birthYear = [1975, 1997, 2002, 1995, 1985];
const ages = birthYear.map(year => 2018 - year);
// prints [ 43, 21, 16, 23, 33 ]
console.log(ages);

 

Array.prototype.filter

filter() 메서드는 콜백 함수에 의해 테스트를 통과한 요소들을 가진 새로운 배열을 만든다. filter() 메서드로 전달된 콜백 함수는 element, index, array라는 3개의 인자를 받는다.

예를 한번 보도록 하자.

 

예 #1

이름과 나이를 프로퍼티로 갖는 오브젝트들을 갖고 있는 배열이 있으며, 18세 이상의 사람의 정보만 가지고 있는 배열을 만들고 싶다고 가정하자.

Without 고차함수

const persons = [
  { name: 'Peter', age: 16 },
  { name: 'Mark', age: 18 },
  { name: 'John', age: 27 },
  { name: 'Jane', age: 14 },
  { name: 'Tony', age: 24},
];
const fullAge = [];
for(let i = 0; i < persons.length; i++) {
  if(persons[i].age >= 18) {
    fullAge.push(persons[i]);
  }
}
// prints [ { name: 'Mark', age: 18 },
//	    { name: 'John,  age: 27 },
//	    { name: 'Tony', age: 24 } ]
console.log(fullAge);

With 고차함수 filter

const persons = [
  { name: 'Peter', age: 16 },
  { name: 'Mark', age: 18 },
  { name: 'John', age: 27 },
  { name: 'Jane', age: 14 },
  { name: 'Tony', age: 24},
];
const fullAge = persons.filter(person => person.age >= 18);
// prints [ { name: 'Mark', age: 18 },
//	    { name: 'John,  age: 27 },
//	    { name: 'Tony', age: 24 } ]
console.log(fullAge);

 

Array.prototype.reduce

reduce() 메서드는 호출하는 배열의 각 요소에 콜백 함수를 실행하고, 단일 output 값을 결과로 낸다. reduce() 메서드는 2개의 매개변수를 받는데, reducer 함수(콜백)와 optional한 initialValue이다.

reducer 함수(콜백)는 4개의 매개변수를 받는데, accumulator, currentValue, currentIndex, sourceArray이다.

initialValue가 주어지면, accumulator는 initialValue와 같고, currentValue는 배열의 첫 번째 요소와 같다.

initialValue가 주어지지 않는다면, accumulator는 배열의 첫 번째 요소와 같고, currentValue는 배열의 두 번째 요소와 같다.

 

예 #1

숫자로 이루어진 배열의 합을 더해야 한다고 가정하자.

Without 고차함수

const arr = [5, 7, 1, 8, 4];
let sum = 0;
for(let i = 0; i < arr.length; i++) {
  sum = sum + arr[i];
}
// prints 25
console.log(sum);

With 고차함수 reduce

const arr = [5, 7, 1, 8, 4];
const sum = arr.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
});
// prints 25
console.log(sum);

배열에서 각 요소에 대해  reducer 함수가 호출될 때 마다, accumulator는 reducer 함수에 의해 반환된 이전 동작의 결과값을 갖고 있고 currentValue는 배열의 현재 값이 된다. 그리하여, 결과는 sum 변수에 저장된다.

initialValue를 설정할 수도 있다.

const arr = [5, 7, 1, 8, 4];
const sum = arr.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
}, 10);
// prints 35
console.log(sum);

고차함수를 사용하는 것이, 코드를 더 깔끔하게, 간결하게, 덜 장황하게 만들어준다는 것을 알 수 있다.

 

자체 고차함수 만들기

지금까지, 자바스크립트 내 만들어진 다양한 고차함수를 보았다. 이제는 자체 고차 함수를 만들어보자.

자바스크립트가 자체 map 메서드가 없다고 가정해보자. 자체적으로 만들어보자.

문자열이 담겨있는 배열이 있고 각 문자열의 길이를 나타내는 숫자가 담긴 배열로 변환한다고 가정해보자.

const strArray = ['JavaScript', 'Python', 'PHP', 'Java', 'C'];
function mapForEach(arr, fn) {
  const newArray = [];
  for(let i = 0; i < arr.length; i++) {
    newArray.push(
      fn(arr[i])
    );
  }
  return newArray;
}
const lenArray = mapForEach(strArray, function(item) {
  return item.length;
});
// prints [ 10, 6, 3, 4, 1 ]
console.log(lenArray);

위 예시에서, 배열과 콜백 함수인 fn을 받는 mapForEach라는 고차함수를 생성했다. 이 함수는 배열을 돌며 각 반복마다 newArray.push 함수 호출 내 콜백 함수인 fn을 호출한다.

콜백 함수 fn은 배열의 현재 요소를 받아서 해당 요소의 길이를 리턴하는데, 이는 newArray에 저장된다. loop가 끝나고 나서, newArray가 리턴되고 lenArray에 할당된다.

결론

지금까지, 고차함수가 무엇인지, 내장 고차함수를 알아보았다. 또한 고차 함수를 어떻게 만드는지도 알아보았다.

다시 말해서, 고차 함수는 인자로 함수를 받고 함수를 리턴할 수 있는 함수이다. 고차 함수는 일반 함수랑 똑같은데, 함수를 인자로 받고 함수를 반환할 수 있는 추가적인 기능을 갖고 있다.

 

 

확실히, 글을 읽고 넘어갈 때 보다 번역을 하거나 정리를 하게되면 확실히 입력이 잘 되는 것 같다.
앞으로도 이런 기회가 많았으면 좋겠고 그렇게 만들거다!
내가 지금까지 적었던 레거시 코드들에 대해서 다시 한번 생각해보게 되는 계기가 되었다..
그리고 앞으로 적게 될 코드들도..ㅎㅎ