본문 바로가기
책정리/자바스크립트

함수형 자바스크립트 - 4장 고차원 함수

by 난파선 2017. 2. 3.



  • 고차원 함수
    • 고차원 함수는 일급이다.
    • 함수를 인자로 받는 다.
    • 함수를 결과로 반환한다.

4.1 다른 함수를 인자로 취하는 함수

4.1.1 함수 전달에 대한 고찰

_.max([1,2,3,4,5])
=> 5    //가장 큰 수를 찾는 함수

var people = [{name: "Fred", age: 65}, {name: "Lucy", age: 36}];

_.max(people, function(p) { return p.age });    // 두번 째 인자를 함수로 받아 비교할 값을 선택한다. [고차원 함수]

//=> {name: "Fred", age: 65}
  • max 함수는 두번째 인자로 함수를 받아 객체에서 비교할 값을 선택할 수 있게 한다.
  • max에서는 '>' 연산자로만 비교가 가능하다. (제한적, 진정한 함수형도 아니다)

함수형으로 업그레이드 ㅋ

function finder(valueFun, bestFun, coll) {
  return _.reduce(coll, function(best, current) {
    var bestValue = valueFun(best);
    var currentValue = valueFun(current);

    return (bestValue === bestFun(bestValue, currentValue)) ? best : current;
  });
}

finder(_.identity, Math.max, [1,2,3,4,5]);
//=>5
  • 첫번째 인자로 값을 선택하는 함수를 전달한다.
  • 두번째 인자로 비교 방식을 함수로 전달한다.
  • 다양한 최적의 값을 찾을 수 있다.

함수를 다른 인자로 전달하는 상황에 대한 더 깊은 고찰

function repeat(times, VALUE) {
  return _.map(_.range(times), function() { return VALUE; });
}

repeat(3, "aaa");
//=> ["aaa", "aaa", "aaa"]
  • 반복횟수와 값을 전달해서 해당 값의 반복횟수 만큼 배열 값을 리턴한다.
  • 고정된 값만 리턴하는 함수, 제한적이다.

값 대신 함수를 사용하라.

  • 반복성이라는 것을 두고 어떤 숫자 만큼 값을 반복하는 것보다 어떤 동작을 특정 횟수 만큼 반복한다면 더 좋다.
function repeatedly(times, fun) {    // 이제 값 대신 함수를 전달한다.(고차원 함수)
  return _.map(_.range(times), fun);
}

repeatedly(3, function() {
  return Math.floor((Math.random()*10)+1);
});
//=> [1, 3, 8]
  • 함수를 사용함으로써 '반복성'이라는 새로운 가능성이 열렸다.

단언컨대 '값 대신 함수를 사용하라

  • 함수를 몇 번을 호출할 것인지 정하는 반복횟수는 아직 상수이다.
  • 함수의 반복횟수를 특정 값이 초과라거나 부호가 바뀐다거나 하는 여러 조건에서 반복을 중단 할 수 있다.
function iterateUntil(fun, check, init) {
  var ret = [];
  var result = fun(init);

  while (check(result)) {
    ret.push(result);
    result = fun(result);
  }

  return ret;
};

iterateUntill(function(n) {return n+2}
, function(n) {return n < 10}
, 1}
//=> [3, 5, 7, 9]
  • repeatedly를 더 일반화한 함수
  • 함수의 반환 값이 반복 횟수를 결정한다.
  • 몇 번을 실행해야 원하는 결과를 수행할 수 있는 지 알 수 있는 상황도 있고 혹은 실행 횟수는 모르지만 어떤 조건에서 실행을 멈춰야 하는 지 알고 있을 때도 있다.

4.3 다른 함수를 반환하는 함수

  • 상수를 반환하는 함수는 대부분의 함수형 프로그래밍에서 등장하는 유용한 기능이며 줄여서 k라고도 부른 다.
function always(VALUE) {    //상수를 반환하는 함수, 명확하게 식별할 수 있도록
  return function() {
    return VALUE;
  };
};

repeatedly(3, always("aaa"));   // 익명함수 대신에 사용하면 코드가 더 명확해 진다.

invoker 함수

  • 메서드를 인자로 받아서 함수를 리턴한다.
  • 주어진 객ㅇ체에 인자로 받은 메서드를 호출한다.
function invoker (NAME, METHOD) {
  return function(target /* args ... */) {
    if (!existy(target)) fail("Must provide a target");

    var targetMethod = target[NAME];
    var args = _.rest(arguments);

    return doWhen((existy(targetMethod) && METHOD === targetMethod), function() {
      return targetMethod.apply(target, args);
    });
  };
};

var rev = invoker('reverse', Array.prototype.reverse);

_.map([[1,2,3]], rev);
//=> [[3,2,1]]

4.2.1 고차원 함수로 인자 캡처하기

  • 인자가 반환된 함수의 '설정' 역할을 하도록 고차원 함수를 구현하려면 다른 함수를 반환하는 함수가 필요하다.
var add100 = makeAdder(100);
add100(50)
//=> 150
  • makeAdder에 리턴된 함수를 add100이라는 이름으로 바인딩함으로써 어떻게 '설정'되었는 지 보여준다.
  • 유용한 기법이지만 기능에 제한이 있다.

4.2.2 변수를 캡처하는 이유

고유의 문자열을 만드는 함수

function uniqueString(len) {
  return Math.random().toString(36).substr(2, len);
};

uniqueString(10);
//=> "3rm6ww5w0x"

고유의 문자열에 접두어를 붙이는 함수

function uniqueString(prefix) {
  return [prefix, new Date().getTime()].join('');
};

uniqueString("argento");
//=> "argento1356107740868"

주어진 값에 호출할 때마다 값이 증가하는 접미사를 추가한 문자열을 반환하는 함수

function makeUniqueStringFunction(start) {
  var COUNTER = start;

  return function(prefix) {
    return [prefix, COUNTER++].join('');
  }
};

var uniqueString = makeUniqueStringFunction(0);

uniqueString("dari");
//=> "dari0"

uniqueString("dari");
//=> "dari1"
  • 의도대로 잘 동작하지만 객체와 같은 동작을 제공하지는 않는 다. 기능이 제한 적이다.

또 다른 코드

var generator = {
  count: 0,
  uniqueString: function(prefix) {
    return [prefix, this.count++].join('');
  }
};

generator.uniqueString("bohr");
//=> bohr0

generator.uniqueString("bohr");
//=> bohr1
  • 위 코드는 함수형이 아니다.
  • generator.count = "test"; 이런 형식으로 객체의 값을 변경할 수 있다.

개선된 코드

var omgenerator = (function(init) {
  var COUNTER = init;

  return {
    uniqueString: function(prefix) {
      return [prefix, COUNTER++].join('');
    }
// 추가 기능을 넣을 수 있다. 아마도?
  };
})(0);

omgenerator.uniqueString("lichking-");
//=> "lichking-0"
  • COUNTER는 비공개 변수가 된다.
  • init 값으로 초기화가 가능하다.

값을 바꿀 때 주의를 기울이자

makeUniqueStringFunction에서 COUNTER라는 변수를 이용하면 외부에서 데이터를 바꾸지 못하도록 보호할 수 있는 장점이 있지만 상황이 복잡해진다는 단점도 있다. 자신이 반환할 값과 관련된 인자만을 활용하는 함수를 가리켜 참고 투명성이 있다고 표현한다.


참고 투명성이란 함수가 기대하는 모든 값으로 함수 호출을 대체할 수있는 함수라는 단순한 의미를 가진다.

댓글