前置条件:函数的调用者的担保
后置条件:保证函数调用的结果
partial(条件,结果) 和 compose(结果,结果)
1. partial
import _ from 'underScore'
import Utils from '../lib/utils.js'
const zero = Utils.validator('cant not be zero', function(n){ return 0 === n})
const number = Utils.validator('arg must be a number', _.isNumber)
function sqr(n) {
// if(!number(n)) throw new Error(number.message)
// if(zero(n)) throw new Error(zero.message)
if(!number(n)) return console.log(number.message)
if(zero(n)) return console.log(zero.message)
return n * n
}
console.log('=====sqr======')
console.log(sqr(10))
console.log(sqr(''))
console.log(sqr(0))
function condition(...validators) {
return function(fun, arg) {
// let errors = Utils.mapCat(function(isValid) {
// return isValid(arg) ? [] : [isValid.message]
// }, validators)
let errors = validators.flatMap(function(isValid) {
return isValid(arg) ? [] : [isValid.message]
})
// if(!_.isEmpty(errors)) throw Error(errors.join(', '))
if(!_.isEmpty(errors)) return console.log(errors.join(', '))
return fun(arg)
}
}
let sqrPre = condition(
Utils.validator('arg must be zero', Utils.complement(zero)),
Utils.validator('arg must be a number', _.isNumber),
)
console.log('======sqrPre=======')
console.log(
sqrPre(_.identity, 10),
sqrPre(_.identity, ''),
sqrPre(_.identity, 0)
)
const unCheckedSqr = n => n * n
// sqrPre 前者条件
// unCheckedSqr 执行结果
// 利用部分柯里化来合并条件,从而生成更多不同条件的函数,实现条件函数的组合。
const checkedSqr = Utils.partial(sqrPre, unCheckedSqr)
console.log('=====checkedSqr========')
console.log(checkedSqr(10))
console.log(checkedSqr(''))
console.log(checkedSqr(0))
const isEven = n => n % 2 === 0
let sillySquare = Utils.partial(Utils.condition(Utils.validator('should be even', isEven)), checkedSqr)
console.log('========sillySquare=============')
console.log(sillySquare(10))
console.log(sillySquare(9))
const validateCommand = condition(
Utils.validator('arg must be a map', _.isObject),
Utils.validator('arg must have the correct keys', Utils.hasKeys('msg', 'type')),
)
const createCommand = Utils.partial(validateCommand, _.identity)
// const createCommand = Utils.partial(validateCommand)
// console.log(createCommand({}))
// console.log(createCommand(21))
console.log('========createCommand=============')
console.log(createCommand({msg: '', type: ''}))
这里的调用过程很绕,每次调用都会存储一些备用的函数或者变量,并返回一个函数。
主要会混淆的地方在于调用的返回函数时传入的参数容易忘记和之前函数的关系。
分析createCommand的调用过程:
- createCommand({msg: ‘’, type: ‘’}) // 函数调用
- const createCommand = Utils.partial(validateCommand, _.identity) // 返回柯里化函数
- validateCommand 作为 fun
- _.identity 赋值给作为 arg = […arg, …params] 的第一个参数arg
- {msg: ‘’, type: ‘’} 作为 params 传入。组合参数后的结果为 [_.identity, {msg: ‘’, type: ‘’}]。最后要执行fun.apply(fun, arg),这时候我们需要去找fun是什么。
- 所以回头来看 validateCommand,validateCommand 是 condition 调用后返回的函数(这个函数是什么?)
- condition 传入两个验证器,返回一个匿名函数(假设函数名为func),匿名函数func接受两个参数(fn, arg)。这就找到partial的fun。
- 然后回到 partial 执行 fun.apply(fun, arg) -> 待入 fun就是func,arg就是 [_.identity, {msg: ‘’, type: ‘’}]。
- 结果形成 func(fn, arg), fn = _.identify, arg = {msg: ‘’, type: ‘’}。
- 验证通过,则执行最后的函数调用 fn(arg),所以最后返回了自己。
整个倒退过程十分的绕,需要将参数一层层代入,中间还容易出现理解断层。
小结:
- 像这样的调用分析主要是要找出生成的函数是什么(像这里是condition的返回函数),这样明白目的后才能够理解每个函数的作用和连接关系(可以使用api语义化去搭建关系)。
- 分析方法:使用正推去搭建关系,找到生成函数。倒推容易产生理解断层。
2. compose
柯里化和部分应用函数都只能按照参数来组合,这是柯里化的局限性。
compose 应用:按照结果来组合
// eg1.
// function isntString(str) {
// return !_.isString(str)
// }
// console.log(isntString(1))
// eg2.
// const isntString = _.compose(x => !x, _.isString)
// console.log(isntString(1))
// compose接收从右到左的函数执行结果,等于是流从右到左执行表达式并传递结果。
// eg3.
function not(v) {
return !v
}
const isntString = _.compose(not, _.isString)
console.log(isntString(1))
// 组合验证条件和执行结果
const sqrPost = condition(
Utils.validator('ret must be a number', _.isNumber), // 数字
Utils.validator('ret should not be zero', Utils.complement(zero)), // 非0
Utils.validator('ret should be positive', Utils.greaterThan(0)) // 正数
)
console.log('========sqrPost==========')
console.log(sqrPost(_.identity, 0))
console.log(sqrPost(_.identity, -1))
console.log(sqrPost(_.identity, ''))
console.log(sqrPost(_.identity, 100))
const megaCheckedSqr = _.compose(Utils.partial(sqrPost, _.identity), checkedSqr)
console.log('========megaCheckedSqr==========')
console.log(megaCheckedSqr(10))
console.log(megaCheckedSqr(0))
版权声明:本文为junjiahuang原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。