Created
November 22, 2015 14:39
-
-
Save chuck0523/b3926fe9c207577a8569 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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