前置条件:函数的调用者的担保
后置条件:保证函数调用的结果
partial(条件,结果) 和 compose(结果,结果)
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的调用过程:
小结:
柯里化和部分应用函数都只能按照参数来组合,这是柯里化的局限性。
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))