ikemonn's blog

技術ネタをちょこちょこと

【Underscore.js】_.memoizeを読んだ

jashkenas/underscore_.memoizeを読んだ。

概要

_.bind(function, object, *arguments

一度計算した値をキャッシュしておく機能を関数に付け加える。

var num = 40;

var fib = function(n) {
    return n < 2 ? n : fib(n - 1) + fib(n - 2);
};

console.time('not memoize');
console.log(fib(num));
console.timeEnd('not memoize’); // not memoize: 1701.395ms

var fib2 = _.memoize(function(n) {
    return n < 2 ? n : fib2(n - 1) + fib2(n - 2);
});

console.time('memoize');
console.log(fib2(num));
console.timeEnd('memoize’); // memoize: 0.979ms

ソースコード

_.memoize = function(func, hasher) {
    var memoize = function(key) {
      var cache = memoize.cache;
      var address = '' + (hasher ? hasher.apply(this, arguments) : key);
      if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
      return cache[address];
    };
    memoize.cache = {};
    return memoize;
  };

hasherが与えられなければ、関数に与えられる引数をkeyにしたキャッシュ用の配列を内部で持っておき、キャッシュにヒットしたらそれを返している。

参考

jashkenas/underscore

【Underscore.js】_.sortedIndexを読んだ

jashkenas/underscore_.sortedIndexを読んだ。

概要

_.sortedIndex(list, value, [iteratee], [context]

ソートされているlistに対して、valueがどの位置に入るかを返す。

_.sortedIndex([10, 20, 30, 40, 50], 35); // 3

ソースコード

_.sortedIndex = function(array, obj, iteratee, context) {
    iteratee = cb(iteratee, context, 1);
    var value = iteratee(obj);
    var low = 0, high = getLength(array);
    while (low < high) {
      var mid = Math.floor((low + high) / 2);
      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
    }
    return low;
  };

while文の中で二分探索をしている。

参考

jashkenas/underscore

【Underscore.js】_.zipを読んだ

jashkenas/underscore_.zipを読んだ。

概要

_.zip(*arrays) 

引数である各配列のindex値の値をまとめた配列を作る。返される配列のlengthは引数の中で1番長いlengthを持つ配列と同じ長さになる。

var x = _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
console.log(x); // [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]

ソースコード

  _.zip = function() {
    return _.unzip(arguments);
  };

  _.unzip = function(array) {
    var length = array && _.max(array, getLength).length || 0;
    var result = Array(length);

    for (var index = 0; index < length; index++) {
      result[index] = _.pluck(array, index);
    }
    return result;
  };

下記のコードで、引数のそれぞれの配列に対し、そのindex番目を結果にいれている。

for (var index = 0; index < length; index++) {
    result[index] = _.pluck(array, index);
}

参考

jashkenas/underscore

【Underscore.js】_.uniqを読んだ

jashkenas/underscore_.uniqを読んだ。

概要

_.uniq(array, [isSorted], [iteratee]

arrayの各値の中で同じ値があった場合、1つだけにされ各値が重複のない配列として返される。配列がソートされている際に、isSortedをtrueにするとより高速に動作する。 値同士が等しいかどうかを評価する関数を渡すこともできる。

var list = [1, 10, 2, 3, 4, 1, 2, 3];
var x = _.uniq(list);
console.log(x); // [1, 10, 2, 3, 4]

ソースコード

  _.uniq = _.unique = function(array, isSorted, iteratee, context) {
    if (!_.isBoolean(isSorted)) {
      context = iteratee;
      iteratee = isSorted;
      isSorted = false;
    }
    if (iteratee != null) iteratee = cb(iteratee, context);
    var result = [];
    var seen = [];
    for (var i = 0, length = getLength(array); i < length; i++) {
      var value = array[i],
          computed = iteratee ? iteratee(value, i, array) : value;
      if (isSorted) {
        if (!i || seen !== computed) result.push(value);
        seen = computed;
      } else if (iteratee) {
        if (!_.contains(seen, computed)) {
          seen.push(computed);
          result.push(value);
        }
      } else if (!_.contains(result, value)) {
        result.push(value);
      }
    }
    return result;
  };

iterateeが与えられた時のseenは、iterateeで各値を評価したものがpushされていく。

} else if (iteratee) {
        if (!_.contains(seen, computed)) {
          seen.push(computed);
          result.push(value);
        }
} 

参考

jashkenas/underscore

【Underscore.js】_.intersectionを読んだ

jashkenas/underscore_.intersectionを読んだ。

概要

_.intersection(*arrays) 

第一引数の配列の各値の中で、第二引数以降のすべての配列に含まれている値を返す。

var x = _.intersection([1, 10, 10, 3], [3 ,190, 10], [1, 545, 3, 10]);
console.log(x); // [10, 3]

ソースコード

_.intersection = function(array) {
    var result = [];
    var argsLength = arguments.length;
    for (var i = 0, length = getLength(array); i < length; i++) {
      var item = array[i];
      if (_.contains(result, item)) continue;
      for (var j = 1; j < argsLength; j++) {
        if (!_.contains(arguments[j], item)) break;
      }
      if (j === argsLength) result.push(item);
    }
    return result;
  };

第一引数の各値が、第二引数以降の配列にも含まれているかを_.containを使って調べている。

for (var j = 1; j < argsLength; j++) {
        if (!_.contains(arguments[j], item)) break;
      }
      if (j === argsLength) result.push(item);

}

上記の処理は、すべての配列にitem(第一引数の値)が含まれているかを調べている部分。 引数で与えられた配列の数だけjがインクリメントされていれば、すべての配列に値が含まれている。

参考

jashkenas/underscore

【Underscore.js】_.unionを読んだ

jashkenas/underscore_.unionを読んだ。

概要

_.union(*arrays)

引数で与えられた配列を一つにまとめる。 その際に重複した値は削除され、重複した値がない配列となって返される。

var x = _.union([1, 10, 10, 3], [3 ,190]);
console.log(x); // [1, 10, 3, 190]

ソースコード

  _.union = function() {
    return _.uniq(flatten(arguments, true, true));
  };



 _.uniq = _.unique = function(array, isSorted, iteratee, context) {
    if (!_.isBoolean(isSorted)) {
      context = iteratee;
      iteratee = isSorted;
      isSorted = false;
    }
    if (iteratee != null) iteratee = cb(iteratee, context);
    var result = [];
    var seen = [];
    for (var i = 0, length = getLength(array); i < length; i++) {
      var value = array[i],
          computed = iteratee ? iteratee(value, i, array) : value;
      if (isSorted) {
        if (!i || seen !== computed) result.push(value);
        seen = computed;
      } else if (iteratee) {
        if (!_.contains(seen, computed)) {
          seen.push(computed);
          result.push(value);
        }
      } else if (!_.contains(result, value)) {
        result.push(value);
      }
    }
    return result;
  };

flattenで与えられた引数の入れ子をなくし、_.uniqで重複した値を削除している。

参考

jashkenas/underscore

【Underscore.js】_.withoutを読んだ

jashkenas/underscore_.withoutを読んだ。

概要

_.without(array, *values) 

arrayからvaluesを除いた配列を返す。

var list = [1, 10, 7, 190, 43];
var x = _.without(list, 7, 43 ,190);
console.log(x); // [1, 10]

ソースコード

_.without = function(array) {
    return _.difference(array, slice.call(arguments, 1));
  };

_.difference = function(array) {
    var rest = flatten(arguments, true, true, 1);
    return _.filter(array, function(value){
      return !_.contains(rest, value);
    });
  };

flattenで、第二引数以降の値を取り出している。 その後、.filterと.containsを用いて、配列に値が含まれているかをチェックしている。

参考

jashkenas/underscore