Created
February 7, 2015 05:59
-
-
Save masahirompp/88c9f53a768eb6649693 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/// <reference path="../../../tsd/underscore/underscore.d.ts" /> | |
/** | |
* | |
* @param thing | |
*/ | |
function fail(thing) { | |
throw new Error(thing); | |
} | |
/** | |
* 存在確認 | |
* @param x | |
* @returns {boolean} | |
* @description nullとundefinedのみfalseとなる。false,0,''はtrueとなる。 | |
* @example | |
* existy(null) => false | |
* existy(undefind) => false | |
* existy(false) => true | |
* existy(0) => true | |
* truthy('') => true | |
*/ | |
function existy(x) { | |
return x != null; | |
} | |
/** | |
* 真偽値確認 | |
* @param x | |
* @returns {boolean} | |
* @description 0と''もtrueとなる。null,undefined,falseのみfalseとなる。 | |
* @example | |
* truthy(null) => false | |
* truthy(undefind) => false | |
* truthy(false) => false | |
* truthy(0) => true | |
* truthy('') => true | |
*/ | |
function truthy(x) { | |
return (x !== false) && existy(x); | |
} | |
/** | |
* カンマ区切りの引数を期待する関数に、配列で引数を渡すようにする。 | |
* @param fun | |
* @returns {Function} | |
* @example | |
* fun(1,2,3) => A | |
* splat(fun)([1,2,3]) => A | |
*/ | |
function splat(fun: Function) { | |
return function(array) { | |
return fun.apply(null, array); | |
}; | |
} | |
/** | |
* 引数として配列を期待する関数に、カンマ区切りで引数を渡すように変換する | |
* @param fun | |
* @returns {Function} | |
* @example | |
* fun([1,2,3]) => A | |
* unsplat(fun)(1,2,3) => A | |
*/ | |
function unsplat(fun: Function) { | |
return function() { | |
return fun.call(null, _.toArray(arguments)); | |
}; | |
} | |
/** | |
* condがtrueの場合のみactionを実行する | |
* @param cond | |
* @param action | |
* @returns {*} | |
*/ | |
function doWhen(cond, action: Function) { | |
if (truthy(cond)) { | |
return action(); | |
} | |
return void 0; | |
} | |
/** | |
* 逆を返す関数を返す | |
* @param pred | |
* @returns {Function} | |
*/ | |
function complement(pred: Function) { | |
return function() { | |
return !pred.apply(null, _.toArray(arguments)); | |
}; | |
} | |
/** | |
* 真偽値反転 | |
* @param x | |
* @returns {boolean} | |
*/ | |
function not(x) { | |
return !x; | |
} | |
/** | |
* 配列の連結 | |
* @args [[*]...] | |
* @returns {*} | |
* @example | |
* cat([1,2], [3,4], [5,6,7]) | |
* => [1,2,3,4,5,6,7] | |
*/ | |
function cat(...args) { | |
var head = _.first(args); | |
if (existy(head)) { | |
return head.concat.apply(head, _.rest(arguments)); | |
} | |
return []; | |
} | |
/** | |
* mapしてから配列の連結 | |
* @param fun mapの高階関数 | |
* @param coll [[*]|*...] 配列また要素の配列 | |
* @returns {*} | |
* @example | |
* mapcat(e => construct(e, ['a']), [1,2,3])) | |
* => [1,'a',2,'a',3,'a'] | |
*/ | |
function mapcat(fun, coll) { | |
return cat.apply(null, _.map(coll, fun)); | |
} | |
/** | |
* 配列の先頭に追加 | |
* @param head | |
* @param tail | |
* @returns {*} | |
* @example | |
* construct(42, [1,2,3]) | |
* => [42,1,2,3] | |
*/ | |
function construct(head, tail: any[]) { | |
return cat([head], _.toArray(tail)); | |
} | |
/** | |
* 特定のフィールドを返す関数を返す | |
* @param field | |
* @returns {Function} | |
*/ | |
function plucker(field: string) { | |
return function(obj) { | |
return (obj && obj[field]); | |
}; | |
} | |
/** | |
* 最も適切な値を返す。maxやminなどの抽象。 | |
* @param fun : x => y => boolean 比較関数 | |
* @param coll | |
* @returns {*} | |
*/ | |
function best(fun: Function, coll: any[]) { | |
return _.reduce(coll, function(x, y) { | |
return fun(x, y) ? x : y; | |
}); | |
} | |
/** | |
* 特定回数繰り返す | |
* @param times | |
* @param fun | |
* @returns {*|Array} | |
*/ | |
function repeatedly(times: number, fun) { | |
return _.map(_.range(times), fun); | |
} | |
/** | |
* 常に同じ値(関数)を返す関数を返す | |
* @param value | |
* @returns {Function} | |
*/ | |
function always(value) { | |
return function() { | |
return value; | |
}; | |
} | |
/** | |
* 対象オブジェクト(target)でメソッドを実行する関数を返す。 | |
* @param name | |
* @param method | |
* @returns {Function} | |
* @example | |
* var rev = invoker('reverse',Array.prototype.reverse); | |
* _.map([[1,2,3],[4,5]],rev) | |
* => [[3,2,1],[5,4]] | |
*/ | |
function invoker(name: string, method: Function) { | |
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); | |
}); | |
}; | |
} | |
/** | |
* 引数の既定値を設定した関数を返す | |
* @param fun | |
* @returns {Function} | |
*/ | |
function fnull(fun: Function /* ,defaults... */ ) { | |
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); | |
}; | |
} | |
/** | |
* ポリモーフィックな関数を生成 | |
* @returns {Function} | |
* @example | |
* 引数の型(StringとArray)によって計算方法を変える関数を返す例 | |
* var str = dispatch(invoker('toString', Array.prototype.toString), | |
* invoker('toString', String.prototype.toString)); | |
* str('a') | |
* => 'a' | |
* str([1,2,3,4]) | |
* => '1,2,3,4' | |
*/ | |
function dispatch(...funs) { | |
var size = funs.length; | |
/* このtargetはinvokerのtargetに相当する */ | |
return function(target, ...args) { | |
var result; | |
for (var i = 0; i < size; i++) { | |
var fun = funs[i]; | |
result = fun.apply(fun, construct(target, args)); | |
if (existy(result)) { | |
return result; | |
} | |
} | |
return result; | |
}; | |
} | |
/** | |
* 引数1つのカリー化 | |
* @param fun | |
* @returns {Function} | |
*/ | |
function curry(fun: Function) { | |
return function(arg) { | |
return fun(arg); | |
}; | |
} | |
/** | |
* 引数2つのカリー化(右から) | |
* @param fun | |
* @returns {Function} | |
*/ | |
function curry2(fun: Function) { | |
return function(secondArg) { | |
return function(firstArg) { | |
return fun(firstArg, secondArg); | |
}; | |
}; | |
} | |
/** | |
* 引数3つのカリー化(右から) | |
* @param fun | |
* @returns {Function} | |
*/ | |
function curry3(fun: Function) { | |
return function(thirdArg) { | |
return function(secondArg) { | |
return function(firstArg) { | |
return fun(firstArg, secondArg, thirdArg); | |
}; | |
}; | |
}; | |
} | |
/* | |
*/ | |
/** | |
* より大きい | |
* @type {Function} | |
* @example | |
* var greaterThan10 = greaterThan(10); | |
* greaterThan10(15) => true; | |
*/ | |
var greaterThan = curry2(function(l, r) { | |
return l > r; | |
}); | |
/** | |
* より小さい | |
* @type {Function} | |
* @example | |
* lessThan(10)(11) => false | |
*/ | |
var lessThan = curry2(function(l, r) { | |
return l < r; | |
}); | |
/** | |
* 部分適用 | |
* function.bindで代用可能 | |
* @param fun | |
* @returns {Function} | |
*/ | |
function partial(fun: Function, ...pargs) { | |
return function( /* args... */ ) { | |
var args = cat(pargs, _.toArray(arguments)); | |
return fun.apply(fun, args); | |
}; | |
} | |
/** | |
* 検証関数 | |
* @param message | |
* @param fun | |
* @returns {Function} | |
*/ | |
function validator(message: string, fun: Function) { | |
var f = function( /* args... */ ) { | |
return fun.apply(fun, arguments); | |
}; | |
f['message'] = message; | |
return f; | |
} | |
/** | |
* condition1の「1」は引数1つ(=arg)の意味 | |
* argに対してvalidatorsを実施し、エラーがなければfunを実行する関数を返す。 | |
* @returns {Function} | |
*/ | |
function condition1( /* validators */ ) { | |
var validators = _.toArray(arguments); | |
return function(fun: Function, arg) { | |
var errors = mapcat(function(isValid) { | |
return isValid(arg) ? [] : [isValid.message]; | |
}, validators); | |
if (!_.isEmpty(errors)) { | |
return fail(errors.join(' ')); | |
} | |
return fun(arg); | |
}; | |
} | |
/** | |
* 配列の再帰的なフラット化 | |
* @param array | |
* @returns {*[]} | |
* @example | |
* flat([[1,2],[3,4,[5,[6],[7,8]]]]) => [1,2,3,4,5,6,7,8] | |
*/ | |
function flat(array) { | |
if (_.isArray(array)) { | |
cat.apply(cat, _.map(array, flat)); | |
} | |
return [array]; | |
} | |
/** | |
* | |
* @param mapFun | |
* @param resultFun | |
* @param array | |
* @returns {*} | |
*/ | |
function visit(mapFun, resultFun, array) { | |
if (_.isArray(array)) { | |
return resultFun(_.map(array, mapFun)); | |
} | |
return resultFun(array); | |
} | |
/** | |
* | |
* @param fun | |
* @param ary | |
* @returns {*} | |
*/ | |
function postDepth(fun: Function, ary: any[]) { | |
return visit(partial(postDepth, fun), fun, ary); | |
} | |
/** | |
* | |
* @param fun | |
* @param ary | |
* @returns {*} | |
*/ | |
function preDepth(fun: Function, ary: any[]) { | |
return visit(partial(postDepth, fun), fun, fun(ary)); | |
} | |
/** | |
* 元のデータを変更しないextend | |
* @returns {void|*} | |
*/ | |
function merge(...args) { | |
return _.extend.apply(null, construct({}, args)); | |
} | |
class LazyChain { | |
private _calls = []; | |
private _target; | |
/** | |
* 遅延評価メソッドチェーン | |
* @param obj | |
* @constructor | |
* @example | |
* new LazyChain([2,1,3]).invoke('sort').force(); | |
* => [1,2,3] | |
*/ | |
constructor(obj) { | |
var isLC = (obj instanceof LazyChain); | |
this._calls = isLC ? cat(obj._calls, []) : []; | |
this._target = isLC ? obj._target : obj; | |
} | |
/** | |
* 実行するメソッドを登録 | |
* @param methodName | |
* @returns {LazyChain} | |
*/ | |
invoke(methodName: string, ...args) { | |
this._calls.push(function(target) { | |
var meth = target[methodName]; | |
return meth.apply(target, args); | |
}); | |
return this; | |
} | |
/** | |
* 評価 | |
* @returns {*} | |
*/ | |
force() { | |
return _.reduce(this._calls, function(target, thunk: Function) { | |
return thunk(target); | |
}, this._target); | |
} | |
/** | |
* _.tapと同じ位置づけ | |
* @param fun | |
* @returns {LazyChain} | |
*/ | |
tap(fun: Function) { | |
this._calls.push(function(target) { | |
fun(target); | |
return target; | |
}); | |
return this; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment