diff --git "a/Chapter_13/\355\225\250\354\210\230\355\230\225_\353\217\204\352\265\254_\354\262\264\354\235\264\353\213\235.md" "b/Chapter_13/\355\225\250\354\210\230\355\230\225_\353\217\204\352\265\254_\354\262\264\354\235\264\353\213\235.md" new file mode 100644 index 0000000..18fd7fc --- /dev/null +++ "b/Chapter_13/\355\225\250\354\210\230\355\230\225_\353\217\204\352\265\254_\354\262\264\354\235\264\353\213\235.md" @@ -0,0 +1,237 @@ +### 이번장에서 살필 내용 + +- 복합적인 쿼리로 데이터를 조회하기 위해 함수형 도구를 조합하는 방법 +- 복잡한 반복문을 함수형 도구 체인으로 바꾸는 방법 +- 데이터 변환 파이프라인을 직접 만들어 작업을 수행하는 방법 + +--- + +### 인트로 + +계산이 더 복잡해지면 함수형 도구 하나로 작업을 할 수 없다. + +따라서, 여러 단계를 하나로 엮은 체인으로 복합적인 계산을 표현하는 방법을 알아보자. + +함수형 도구를 조합해서 읽고 쓰기 쉽고 단순한 단계를 유지하며 복잡한 계산을 해결해나갈것이다. + +### 체이닝 + +여러 단계를 하나로 조합하는 것이다. + +### 체인을 명확하게 만들기 1 : 단계에 이름 붙이기 + +각 단계에 이름을 붙이는 것이다. + +```jsx +function **biggestPurchasesBestCustomers**(customers) { // 1 +    var bestCustomers = filter(customers, function(customer) { +        return customer.purchases.length >= 3; +    }); +  +    var biggestPurchases = map(bestCustomers, function(customer) { // 2 +        return maxKey(customer.purchases, {total: 0}, function(purchase) { +            return purchase.total; +        }); +    }); +  +    return biggestPurchases; +} +``` + +위 코드를 각 단계의 고차 함수를 빼내 이름을 붙일 수 있다 + +```jsx +function biggestPurchasesBestCustomers(customers) { +    var bestCustomers  = **selectBestCustomers**(customers); // 1 +    var biggestPurchases = **getBiggestPurchases**(bestCustomers); // 2 +    return biggestPurchases; +} +  +function selectBestCustomers(customers) { // 고차함수에 이름을 붙여 현재 문맥에 추가 +    return filter(customers, function(customer) { +        return customer.purchases.length >= 3; +    }); +} +  +function getBiggestPurchases(customers) { +    return map(customers, **getBiggestPurchase**); // 고차함수를 함수로 쉽게 빼냄 +} +  +function getBiggestPurchase(customer) { +    return maxKey(customer.purchases, {total: 0}, function(purchase) { +        return purchase.total; +    }); +} +``` + +아쉬운점은 아직 콜백 함수를 여전히 인라인으로 사용하고 있다는 점이다. + +인라인으로 정의된 콜백함수는 재사용할 수 없다. + +### 체인을 명확하게 만들기 2: 콜백에 이름 붙이기 + +콜백을 빼내 이름을 붙이는 방법이다. + +```jsx +function biggestPurchasesBestCustomers(customers) { +    var bestCustomers  = filter(customers, **isGoodCustomer**); // 1 +    var biggestPurchases = map(bestCustomers, **getBiggestPurchase**); // 2 +    return biggestPurchases; +} +  +function **isGoodCustomer**(customer) { +    return customer.purchases.length >= 3; +} +  +function **getBiggestPurchase**(customer) { +    return maxKey(customer.purchases, {total: 0}, getPurchaseTotal); +} +  +function getPurchaseTotal(purchase) { +    return purchase.total; +} +``` + +콜백을 빼내고 이름을 붙여 재사용할 수 있는 함수로 만들었다. + +**selectBestCustomers()함수는 고개 배열로만 쓸 수 있지만 isGoodCustomer() 함수는 고객 하나를 넘겨 쓸 수 있다.** + +### 체인을 명확하게 만들기 3 : 두 방법을 비교 + +두번째 방법이 명확하다. + +고차 함수를 그대로 쓰는 첫번째 방법보다 이름을 붙이 두 번째 방법이 재사용하기 더 좋다. + +인라인 대신 이름을 붙여 코랩긍ㄹ 사용하면 단계가 중첩되는 것을 방지할 수 있다. + +### 스트림 결합 + +map(), filter(), reducer() 체인을 최적화하는 것이다. + +```jsx +// 값 하나에 map() 두 번 사용 +var names    = map(customers, getFullName); +var nameLengths = map(names, stringLength); +``` + +```jsx +// 값 하나에 map() 한 번 사용 +var nameLengths = map(customers, function(customer) { +    return stringLength(getFullName(customer)); +}); +``` + +오른쪽 코드는 카비지 컬렉션이 필요없다. + +### 리팩터링 팁 1: 데이터 만들기 (반복문을 함수형 도구로 만들기) + +```jsx +var answer = []; +  +var window = 5; +  +for(var i = 0; i < array.length; i++) { +    var sum = 0; +    var count = 0; +    for(var w = 0; w < window; w++) { +        var idx = i + w; +        if(idx < array.length) { +            sum  += array[idx]; +            count += 1; +        } +    } +    answer.push(sum/count); +} +``` + +.slice()를 활용해 리팩토링 + +```jsx +var answer = []; +  +var window = 5; +  + + for(var i = 0; i < array.length; i++) { +    var sum  = 0; +    var count = 0; +    var subarray = array.slice(i, i + window); +    for(var w = 0; w < subarray.length; w++) { +        sum  += subarray[w]; +        count += 1; +    } +    answer.push(sum/count); +} +``` + +### 팁 2: 한 번에 전체 배열을 조작하기 + +```jsx +var answer = []; +  +var window = 5; +  +    for(var i = 0; i < array.length; i++) { +      var subarray = array.slice(i, i + window); +      answer.push(average(subarray)); // 안쪽 반복문 전체를 .slice()와 average()를 호출 +    } +``` + +### 팁 3: 작은 단계로 나누기 + +```jsx +var indices = []; +  +for(var i = 0; i < array.length; i++) // index가 들어있는 배열을 만든다 +    indices.push(i); +  +var window = 5; +  +var answer = map(indices, function(i) { // 하위 배열 만들고 평균 계산하는 일 2개나 함 +    var subarray = array.slice(i, i + window); +    return average(subarray); +}); +``` + +```jsx +function range(start, end) { +    var ret = []; +    for(var i = start; i < end; i++) +        ret.push(i); +    return ret; +} +  +var window = 5; +  +var indices = range(0, array.length); // 인덱스 배열 생성 +var windows = map(indices, function(i) { // 하위 배열만들기 +    return array.slice(i, i + window); +}); +var answer = map(windows, average); // 평균 계산하기 +``` + +처음 절차적인 코드에서 함수형 코드로 변경되었다. + +**보너스 팁** + +- 조건문을 filter()로 바꾸기 +- 유용한 함수로 추출하기, 알려준 3개 말고도 더 좋은 도구를 스스로 찾아봐라 +- 개선을 위해 함수형 도구 조합을 실험하기 + +### 체이닝 디버깅을 위한 팁 + +- 구체적인 것을 유지하기 +- 출력해보기 +- 타입을 따라가보기 + +### 다양한 함수형 도구 + +1. pluck(), concat(), frequenciesBy() & groupBy() +- [https://medium.com/@cheonmyung0217/underscore-js-pluck-함수-c71319710d67](https://medium.com/@cheonmyung0217/underscore-js-pluck-%ED%95%A8%EC%88%98-c71319710d67) +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy + +1. 도구 찾는 곳 + - Lodash + - Laravel(PHP) + - 클로저 표준 라이브러리 + - 하스켈