함수형 자바스크립트 -3

2018-04-02

Section 2

currying

  • 커링 / 함수와 인자를 다루는 기법

    • 평가시점을 미뤄 뒀다가 원하는 시점에 평가 하는 방법

    • curry, curryr

    function _curry(fn) {
      return function(a){
        return function(b) {
          return fn(a, b);
        }
      }
    }
    
    var add = _curry(function(a,b){
      return a+b;
    })
    
    var add10 = add(10);
    console.log( add10(5) ); // 15
    
    console.log( add(5)(3)); // 8
    
    console.log( add(1, 2)); // function이 리턴된다.
    
    // 인자를 2개를 받았을때 원하는 값을 리턴받고 싶다면 arguments.length 를 이용한다.
    
    function _curry(fn) {
      return function(a, b){
        if (arguments.length == 2) return fn(a,b) // 또는 3항연산자 사용
        // return arguments.length == 2 ? fn(a,b) : function(b){return fn(a,b)}
        return function(b) {
          return fn(a, b);
        }
      }
    }
    console.log( add(1, 2)); // 3
    
    var sub = _curry(a, b){
      return a-b;
    }
    console.log( sub(10, 5)); // 5
    var sub10 = sub(10);
    console.log(sub10(5)); // 5
    // 값은 올바르게 출력되지만 변수와 인자를 봤을때 표현력이 떨어진다.
    // curry의 경우 인자를 왼쪽에서 부터 읽지만(a->b) curryr은 오른쪽부터 읽는다(b->a)
    
    function _curryr(fn){
      return function(a,b) {
        return arguments.length == 2 ? fn(a,b) : function(b){return fn(b,a)}
      }
    }
    console.log(sub10(5)); // -5
    // 올바르게 표현됨
    
    
    • get 만들어 좀 더 간단하게 하기
    function _get(obj, key) {
      return obj == null ? undefined : obj[key];
    }
    var users = [
      {'name': 'mh'},
      {'name': 'aa'}
    ]
    var user1 = users[0];
    console.log(user1.name); // mh
    console.log(_get(user1, 'name')); // mh
    
    console.log(users[10].name); // Error
    console.log(_get(users[10], 'name')); // undefined
    
    // curryr을 사용하여 인자 순서를 바꿀 수 있다.
    
    var _get = _curryr(function _get(obj, key) {
      return obj == null ? undefined : obj[key];
    })
    console.log(_get('name')(user1)); // mh
    
    // 내부 동작
    var get_name = _get('name'); // get_name은 유저네임을 가져오는 함수가 된다.
    console.log(get_name(user1)); // mh
    
    

reduce

  • 기존 데이터들을 가공해서 새로운 데이터를 만듬
_each(list, function(val) {
    memo = iter(memo, val)
});
return memo
}

console.log(
_reduce([1, 2, 3], function (a,b) {
    return a+b; // add
}, 0)
);


console.log(
_reduce([1,2,3], add, 0)
);

// memo = add(0,1)
// memo = add(memo, 2);
// memo = add(memo, 3);
// return memo

// => add(add(add(0, 1), 2), 3);

// function _reduce(list, iter, memo){
//    return  iter(iter(iter(0, 1), 2), 3);
// }


// reduce 는 축약하는 함수



// reduce의 3번째 인자를 생략하는 경우

var slice = Array.prototype.slice;
function _rest(list, num) {
return slice.call(list, num || 1);
}

function _reduce(list, iter, memo){
if (arguments.length === 2) {
    memo = list[0];
    // list = list.slice(1);
    // slice는 arr의 메소드이기 때문에 arr만 받을 수 있다.
    // array_like와 같이 querySelectorAll은 arr 같지만 slice메소드가 없다.
    // 위의 문제점을 해결하기 위해서 rest함수를 사용한다.

    list = _rest(list);


}
_each(list, function(val) {
    memo = iter(memo, val)
});
return memo
}

console.log(
_reduce([1, 2, 3], add, 10)
); // 16

console.log(
_reduce([1, 2, 3], add)
); // 6

console.log(
_reduce([1, 2, 3, 4], add, 10)
); // 20

pipe

  • 함수를 리턴하는 함수
  • 연속적으로 함수를 실행해주는 함수
  function _pipe() {
    var fns = arguments;
    return function(arg) {
      return  _reduce(fns, function (arg, fn) {
        return fn(arg);
      }, arg)
    }
  }

  var f1 = _pipe(
    function(a){ return a+1 }, // 1 + 1
    function(a){ return a*2 } // 2 * 2
  );

  console.log(f1(1));

go

  • 즉시 실행되는 pipe 함수
function _go() {

}