[Front-end] 개발자 공부

[개발 공부 94일차] 함수형 코딩 | 함수형 도구 체이닝

MOLLY_ 2024. 9. 4. 23:00
728x90

< 목차 >

[들어가며] 알아둬야 할 개념

0. TL;DR

1. 복잡한 반복문을 함수형 도구 체인으로 바꾸는 방법

2. 반복문을 함수형 도구로 리팩토링
3. 체이닝 디버깅을 위한 Tip

 

 

[이미지 출처] 요즘IT - 테오의 프론트엔드 (https://yozm.wishket.com/magazine/detail/1485)

 

 

[들어가며] 알아둬야 할 개념

  • 시퀀스: 컴퓨터 과학 & 프로그래밍에서 ‘특정한 순서대로 내열된 데이터’를 표현하는 자료구조 or 데이터 유형
    • 데이터의 순서가 중요
    • 각 요소가 그 순서에 따라 접근할 수 있는 특성을 갖고 있음
    • e.g. 자바스크립트에선 배열, 문자열이 해당
  • 항등 함수: 인자로 받은 값을 그대로 return 하는 함수
    • 아무 일도 하지 않지만, 아무것도 하지 않아야 할 때 유용하게 사용 가능

 

 

0. TL;DR

  1. 체인: 여러 단계를 조합하는 것
    1. 원하는 결과에 가까워지도록 데이터를 한 단계씩 변환
    2. 기존에 있던 반복문을 함수형 도구 체인으로 리팩토링
    3. reduce()로 결과값 생성
  2. 함수형 도구를 체인으로 조합하면 복잡한 계산을 작고 명확한 단계로 표현 가능

 

데이터 시퀀스에 사용할 수 있는 강력한 함수형 도구에 대해 알아보자.

 

 

1. 복잡한 반복문을 함수형 도구 체인으로 바꾸는 방법

  1. 3개 이상 구매한 우수 고객을 거르고 (filter())
  2. 가장 비싼 구매를 가져오기 (map())
    : 가장 비싼 구매를 찾는 코드는 reduce()로 구현 가능
// 원래 코드
function biggestPurchaseBestCustomers (customers) {
	let bestCustomers = filter(customers, function (customer) {
		return customer.purchases.length >= 3;
	});
	
	let biggestPurchases = map(bsetCustomers, function (customer) {
		return maxKey(customer.purchases, {total: 0}, function (purchase) {
			return purchase.total;
		});
	});
	return biggestPurchases;
}

function maxKey (array, init, f) {
	return reduce(array, init, function (biggestSoFar, element) {
		if (f(biggestSoFar) > f(element)) {
			return biggestSoFar;
		} else {
			return element;
		}
	})
}

 

위 코드를 토대로 체인을 명확하게 만들어 보자.

 

 

(1) 단계에 이름 붙이기

각 단계의 고차 함수를 빼내 이름을 붙일 수 있다.

// maxKey 함수만 그대로 사용. 나머지는 모두 리팩토링
function biggestPurchaseBestCustomers (customers) {
	let bestCustomers = selectBestCustomers(customers); // 1단계
	let biggestPurchases = getBiggestPurchases(bestCustomers); // 2단계
	return biggestPurchases;
}

function selectBestCustomers (customers) {
	return filter(customers, function (customer) {
		return customer.purchases.length >= 3;
	});
}

function getBiggestPurchases (customers) {
	return map(bsetCustomers, getBiggestPurchase);
}

function getBiggestPurchase (customer) {
	return maxKey(customer.purchases, {total: 0}, function (purchase) {
		return purchase.total;
	});
}

 

 

(2) 콜백에 이름 붙이기

function biggestPurchaseBestCustomers (customers) {
	let bestCustomers = filter(customers, isGoodCustomer); // 1단계
	let biggestPurchases = map(bsetCustomers, getBiggestPurchase); // 2단계
	return biggestPurchases;
}

const isGoodCustomer = (customer) => customer.purchases.length >= 3;
const getBiggestPurchases = (customers) => maxKey(customer.purchases, {total: 0}, getPurchaseTotal;
const getPurchaseTotal = (purchase) => purchase.total;

 

2번째 방법이 더 명확하고 가독성도 좋다.

 

 

2. 반복문을 함수형 도구로 리팩토링

  1. 데이터 생성
  2. 배열 전체를 다루기
  3. 작은 단계로 쪼개기

 

 

절차적 코드 vs. 함수형 코드

// 절차적인 원래 코드
// ⇒ 반복문이 중첩되고, 인덱스를 계산하며 지역변수를 바꾸는 코드
let answer = []; // 반복문 안에서 결과가 완성되는 배열
let window = 5;

for (let i = 0; i < array.length; i++) { // 외부 배열은 배열 개수만큼 반복
	let sum = 0;
	let count = 0;
	for (let w = 0; w < window; w++) { // 내부 배열은 0~4까지 작은 구간 반복
		let idx = i + w; // 새로운 인덱스 계산
		if (idx < array.length) {
			sum += array[idx];
			count += 1; // 어떤 값 누적
		}
	}
	answer.push(sum/count); // answer 배열에 값 추가
}

 

// 함수형 도구를 사용한 코드
let window = 5;

let indices = range(0, array.length); // range()로 인덱스 배열 생성
let windows = map(indices, function (i) {
	return array.slice(i, i + window); // 1. slice()로 데이터 생성 (하위 배열로 만듦)
});
let answer = map(windows, averages); // 2. 한 번에 전체 배열 조작 (평균 계산, averages 함수 재사용)

// 재사용 가능한 추가 도구
// ⇒ 인덱스 배열을 만드는 코드를 빼내, 유용한 함수로 정의 (나중에 사용 가능한 함수)
function range (start, end) {
	let ret = [];
	for (let i = start; i < end; i++) {
		ret.push(i);
	}
	return ret;
}

 

 

3. 체이닝 디버깅을 위한 Tip

  1. 구체적인 것을 유지하기
    • 각 단계에서 어떤 걸 하고 있는지 알기 쉽게 작명 잘하기
  2. console.log()로 출력해 보기
    • 각 단계 사이에 console.log() 구문을 넣어, 예상대로 동작하는지 확인
  3. 타입을 따라가 보기
    • map()은 새로운 배열을 return. 콜백이 return 하는 타입의 값 들어있음
    • reduce()는 결과값이 콜백이 return 하는 값과 같음. 초기값도 같음

 

 

728x90