Open mileOfSunshine opened 5 years ago
命令式编程
基于原型的面向对象编程
元编程
函数式编程
原理:接收一个谓词,并返回一个反转谓词结果。
function complement (pred) {
return function() {
return !pred.apply(null, _.toArray(arguments))
}
}
// 示例
_.filter(['a', 'b', 3, 'd'], complement(_.isNumber)) // => ['a', 'b', 'd']
原理:接收一定数量的参数,并连接它们的函数。
function cat () {
var head = _.first(arguments)
if (existy(head)) {
return head.concat.apply(head, _.rest(arguments))
} else {
return []
}
}
// 示例
cat([1, 2, 3], [4, 5], [6, 7, 8]) // => [1, 2, 3, 4, 5, 6, 7, 8]
原理: 接收一个元素和一个数组,并用cat将元素放置在数组前方。
function construct (head, tail) {
return cat([head], _.toArray(tail))
}
// 示例
construct(42, [1, 2, 3]) // => [42, 1, 2, 3]
[ ] 组合子
[ ] 引用透明
function repeatedly(times, fun) {
return _.map(_.range(times, fun))
}
function iterateUtil(fun, check, init) {
var ret = []
var result = fun(init)
while (check(result)) {
ret.push(result)
result = fun(result)
}
return ret
}
原理:检查进来的参数是否是null或undefined, 如果是则用默认值来替换,然后再调用函数。
function fnull(fun) {
var defaults = _.rest(arguments)
return function() {
var args = _.map(arguments, function(e, i) {
return e != null ? e : defaults[i]
})
return fun.apply(null, args)
}
}
原理:接收一个方法,并在任何给定的对象上调用它
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)
})
}
}
原理:接收一组谓词函数(返回true或false的函数),并返回一个验证函数。返回的验证函数在给定对象上执行每个谓词,并对每个返回false的谓词增加一个特殊的错误字符串到一个数组中。如果所有的谓词返回true,那么最终返回的结果是一个空数组;否则,结构为错误消息的数据。
function checker (/* validators */) {
var validators = _.toArray(arguments)
return function (obj) {
return _.reduce(validators, function (errs, check) {
if (check(obj))
return errs
else
return _.chain(errs).push(check.message).value()
}, [])
}
}
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)))
gonnaFail(100) //=> ["ZOMG!"]
[ ] 多态函数
[ ] 特化参数
[ ] 柯里化
function dispatch () {
var funs = _.toArray(arguments)
var size = funs.length
return function (target) {
var ret = undefined
var args = _.rest(arguments)
for (var funIndex = 0; funIndex < size; funIndex++) {
var fun = funs[funIndex]
ret = fun.apply(fun, construct(target, args))
if (existy(ret)) return ret
}
return ret
}
}
// 示例
function stringReverse(s) {
if (!_.isString(s)) return undefined;
return s.split('').reverse().join('')
}
var rev = dispatch(invoker('reverse', Array.prototype.reverse), stringReverse)
rev([1,2,3]) // => [3, 2, 1]
rev('abc') // => 'cba'
function isa (type, action) {
return function (obj) {
if (type === obj.type) {
return action(obj)
}
}
}
var performCommand = dispatch(
isa('notify', function (obj) { return notify(obj.message) }),
isa('join', function (obj) { return changeView(obj.target) }),
function (obj) { alert(obj.type) }
)
// 使用dispatch来扩展performCommand
var performAdminCommand = dispatch(
isa('kill', function (obj) { return shutdown(obj.hostname) }),
performCommand
)
// use
performAdminCommand({ type: 'kill', hostname: 'localhost' }) // => does the shutdown action
performAdminCommand({ type: 'flail' }) // => alert box pop up
performAdminCommand({ type: 'join', target: 'foo' }) // => does the changeView action
函数组合的精华:使用现有的零部件来建立新的行为,这些新行为同样也成为了已有的零部件。
柯里化函数为每一个逻辑参数返回一个新函数。 柯里化函数逐渐返回消耗参数的函数,直到所有参数耗尽。
推荐: 从最右边的参数向左柯里化,_.curry
确定是否应该使用柯里化的通用规则是:API是否要利用高阶函数?如果答案是肯定的,那么至少一个参数的柯里化函数是合适的。
关系:柯里化函数在FUN运行之前需要三次级联调用(例如curried(3)(2)(1)),而部分应用函数已准备好调用,只需要一次带两个参数的调用(例如partially(2, 3))
原理:接收一个函数和一定数量的参数并返回一个固定的第一个参数的函数。
function partial (fun /*, pargs*/) {
var pargs = _.rest(argments)
return function (/* arguments */) {
var args = cat(pargs, _.toArray(arguments))
return fun.apply(fun, args)
}
}
前置和后置条件之间的关系:给出函数能处理的数据,那么就能确保符合条件的结果。 一切后置条件的失败永远都是函数的提供者的错 利用
_.compose
组合前置条件和后置条件
原理:接收一定数量的函数并将它们从右至左拼接。
[ ] &&, "短路"
[ ] 惰性:直到需要的时候才会计算。
[ ] 鲁棒性:健壮性
[ ] 蹦床原理:使用蹦床展平调用,而不是深度嵌套的递归调用。
规则:
用递归遍历图
var influence = [
['Lisp', 'Smalltalk'],
['Lisp', 'Scheme'],
['Smalltalk', 'Self'],
['Scheme', 'JavaScript'],
['Scheme', 'Lua'],
['Self', 'Lua'],
['Self', 'JavaScript']
]
function nexts (graph, node) {
if (_.isEmpty(graph)) return []
var pair = _.first(graph)
var from = _.first(pair)
var to = second(pair)
var more = _.rest(graph)
if (_.isEqual(node, from)) {
return construct(to, nexts(more, node))
} else {
return nexts(more, node)
}
}
// 示例
nexts(influence, 'Lisp') // => ['Smalltalk', 'Scheme']
原理: 优先访问最左侧分支, 然后再访问右侧分支上的节点.
function depthSearch (graph, nodes, seen) {
if (_.isEmpty(nodes)) return rev(seen) // 结果反转
var node = _.first(nodes)
var more = _.rest(nodes)
if (_.contains(seen, node)) {
return depthSearch(graph, more, seen)
} else {
return depthSearch(
graph,
cat(nexts(graph, node), more),
construct(node, seen)
)
}
}
// 递归
function myLength (ary) {
if (_.isEmpty(ary)) return 0
else return 1 + myLength(_.rest(ary))
}
// 尾递归
function tcLength (ary, n) {
var len = n ? n : 0
if (_.isEmpty(ary)) return len
else return tcLength(_.rest(ary), len + 1)
}
function andify (/* preds */) {
var preds = _.toArray(arguments)
return function (/* args */) {
var args = _.toArray(arguments)
var everything = function (ps, truth) {
if (_.isEmpty(ps))
return truth
else
return _.every(args, _.first(ps)) && everything(_.rest(ps), truth)
}
return everything(preds, true)
}
}
// 示例
var evenNums = andify(_.isNumber, isEven)
evenNums(1, 2) // => false
evenNums(2, 4, 6, 8) // => true
function orify(/* preds */) {
var preds = _.toArray(arguments)
return function (/* args */) {
var args = _.toArray(arguments)
var something = function(ps, truth) {
if (_.isEmpty(ps))
return truth
else
return _.some(args, _.first(ps)) || something(_.rest(ps), truth)
}
return something(preds, false)
}
}
// 示例
var zeroOrOdd = orify(isOdd, zero)
zeroOrOdd() // => false
zeroOrOdd(0, 2, 4, 6) // => true
function flat (array) {
if (_.isArray(array)) {
return cat.apply(cat, _.map(array, flat))
else
return [array]
}
}
function deepClone (obj) {
if (!existy(obj) || !_.isObject(obj)) {
return obj
}
var temp = new obj.constructor()
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = deepClone(obj[key])
}
}
return temp
}
// 示例
var x = [{ a: [1, 2, 3], b: 42 }, { c: { d: [] } }]
var y = deepClone(x)
_.isEqual(x, y) // => true
y[1]['c']['d'] = 42
_.isEqual(x, y) // => false
trampoline 所做的是不断调用函数的返回值,直到它不再是一个函数。
function trampoline (fun /*, args */) {
var result = fun.apply(fun, _.rest(arguments))
while (_.isFuntion(result)) {
result = result()
}
return result
}
function generator (seed, current, step) {
return {
head: current(seed),
tail: function () {
console.log('forced')
return generator(step(seed), current, step)
}
}
}
function genHead (gen) { return gen.head }
function genTail (gen) { return gen.tail() }
缺陷:尾部元素在被访问之前都不会被计算,也意味着每次访问都需要重新计算一次。
function genTake (n, gen) {
var doTake = function (x, g, ret) {
if (x === 0) {
return ret
} else {
return partial(doTake, x-1, genTail(g), cat(ret, genHead(g)))
}
return trampoline(doTake, n, gen, [])
}
}
// 示例
genTake(10, ints) // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
含义:执行无数次后还具有相同的效果。
第一章
提示(错误、警告、通知)信息的抽象
定义事物的存在 existy
Javascript中有两个值表示不存在——null 和 undefined
判断一个对象是否应该被认为是true的同义词
在某个条件为真的情况下执行某个操作,否则返回undefined
nth