Skip to content

Instantly share code, notes, and snippets.

@chuck0523
Created November 22, 2015 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chuck0523/b3926fe9c207577a8569 to your computer and use it in GitHub Desktop.
Save chuck0523/b3926fe9c207577a8569 to your computer and use it in GitHub Desktop.
(function() {
var _ = require('./underscore.js');
// util
var
log = function(x) { console.log(x); },
existy = function(x) { return x != null; },
truthy = function(x) { return (x !== false) && existy(x) },
fail = function(thing) { throw new Error(thing); },
doWhen = function(cond, action) {
if(truthy(cond))
return action();
else
return undefined;
},
plucker = function(FIELD) {
return function(obj) {
return (obj && obj[FIELD]);
}
},
cat = function(/* some arays */) {
var head = _.first(arguments);
if(existy(head))
return head.concat.apply(head, _.rest(arguments));
else
return [];
};
// data
var simpleArray = [1, 2, 3, 4, 5];
var people = [
{
name : 'chuck',
age : 24
},
{
name : 'someone',
age : 50
},
{
name : 'another man',
age : 52
}
];
// _.map, _.reduce, _.filter -> 高階関数の定義を満たす
/*
4.1.1 : 関数を渡すことを考える(max, finder, best)
*/
// log(_.max(simpleArray)); // 5
// maxは第二引数に関数を取ることが可能
// log(_.max(people, function(p){
// return p.age;
// }));
// より良い関数を作成する
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;
});
}
// log(finder(_.identity, Math.max, simpleArray)); // 5
// log(finder(plucker('age'), Math.max, people)); // { name: 'another man', age: 52 }
// log(finder(
// plucker('name'),
// function(x, y) { return (x.charAt(0) === 'c') ? x: y},
// people
// )); // { name: 'chuck', age: 24 }
// finderを更に改良
function best(fun, coll) {
return _.reduce(coll, function(x, y) {
return fun(x, y) ? x : y;
});
}
// log(best(function(x, y) { return x > y; }, simpleArray)); // 5
/*
4.1.2 関数を渡すことをさらに考える(repeat, repeatedly, iterateUntil)
*/
function repeat(times, VALUE) {
return _.map(_.range(times), function() { return VALUE; });
}
// log(repeat(4, 'hey')); // [ 'hey', 'hey', 'hey', 'hey' ]
/*
4.1.2.1 値ではなく、関数を使え
*/
function repeatedly(times, fun) {
return _.map(_.range(times), fun);
}
// log(repeatedly(3, function() {
// return Math.floor((Math.random() * 10) + 1);
// })); // [ 10, 3, 1 ] *値はランダム
/*
4.1.2.2 値ではなく、関数を使え、その2
*/
// フィードワード関数
function iterateUntil(fun, check, init) {
var ret = [];
var result = fun(init);
while(check(result)) {
ret.push(result);
result = fun(result);
}
return ret;
}
// log(iterateUntil(function(n) { return n + n; },
// function(n) { return n <= 1024; },
// 1
// )); // [ 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ]
/*
他の関数を返す関数
*/
// kと呼ばれる関数
function always(VALUE) {
return function() {
return VALUE;
}
}
var f = always(function(){});
// log(f() === f()); // true
// 関数は常にユニークな値を生成する。ただし、関数のVALUEにバインドされている戻り値は常に同じ。
var g = always(function(){});
// log(g() === f()); // false
// 新しく生成されたクロージャは異なる値を返す。
// log(repeatedly(3, always('yey!'))); // [ 'yey!', 'yey!', 'yey!' ]
// ここでalwaysはコンビネータと言われる。
function invoker(NAME, METHOD) {
return function(target /* 任意の数の引数 */) {
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);
// log(_.map([[1,2,3]], rev)); // [ [ 3, 2, 1 ] ]
/*
4.2.1 引数を高階関数に確保する
*/
/*
4.2.2 大義のために変数を確保する
*/
// ユニーク文字列を生成する普通の関数 
function uniqueString(len) {
return Math.random().toString(36).substr(2, len);
}
// log(uniqueString(10)); // ez3nvwqkud
// 連番を降る
function makeUniqueStringFunction(start) {
var COUNTER = start;
return function(prefix) {
return [prefix, COUNTER++].join('');
}
}
var uniqueString = makeUniqueStringFunction(0)
// log(uniqueString('dari')); // dari0
// log(uniqueString('dari')); // dari1
/*
存在しない状態に対する防御のための関数:fnull
*/
var nums = [1, 2, 3, null, 5];
_.reduce(nums, function(total, n) {
return total * n;
}); // 0 !?
function fnull(fun /*, */) {
var defaults = _.rest(arguments);
return function(/* args */) {
var args = _.map(arguments, function(e, i) {
return existy(e) ? e : defaults[i];
});;
return fun.apply(null, args);
};
}
var safeMult = fnull(function(total, n) { return total * n; }, 1, 1);
// log(_.reduce(nums, safeMult)); // 30
/*
4.3 すべてを結集:オブジェクトバリデータ
*/
function checker(/* 1個以上の検証関数 */) {
var validates = _.toArray(arguments);
return function(obj) {
return _.reduce(validates, function(errs, check) {
if(check(obj))
return errs;
else
return _.chain(errs).push(check.message).value();
}, []);
};
}
var alwaysPasses = checker(always(true), always(true));
// log(alwaysPasses({})); // []
var fails = always(false);
fails.message = "人生における過ち";
var alwaysFails = checker(fails);
// log(alwaysFails({})); // [ '人生における過ち' ]
function validator(message, fun) {
var f = function(/* args */) {
return fun.apply(fun, arguments);
};
f['message'] = message;
return f;
}
var gonnaFail = checker(validator("ZOMG!", always(false)));
// log(gonnaFail(100)); // ['ZOMG!']
function aMap(obj) {
return _.isObject(obj);
}
var checkCommand = checker(validator('マップデータである必要があります。', aMap));
// log(checkCommand({})); // []
function hashKeys() {
var KEYS = _.toArray(arguments);
var fun = function(obj) {
return _.every(KEYS, function(k) {
return _.has(obj, k)
});
};
fun.message = cat(["これらのキーが存在する必要があります : "], KEYS).join(" ");
return fun;
}
var checkCommand = checker(validator('マップデータである必要があります。', aMap),
hashKeys('msg', 'type'));
// log(checkCommand({msg: "blah", type: "display"})); // []
// log(checkCommand(32)); // ['マップデータである必要があります。']
// log(checkCommand({})); // [ 'これらのキーが存在する必要があります : msg type' ]
log('# 各関数の実行結果を見るには、適宜コメントアウトを外すこと');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment