1.函数组合的作用是什么?
如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并为一个函数
function compose(f,g){
return function(x){
return f(g(x))
}
}
function first(arr){
return arr[0]
}
function reverse(arr){
return arr.reverse
}
//从右到左执行
let last=compose(first,reverse)
console.log(last([1,2,3,4]))
注意:函数组合默认从右到左执行
lodash中组合函数flowRight(),可以组合多个函数
const f=require('lodash')
const toUpper=s=>s.toUppercase()
const reverse=arr=>arr.reverse()
const first=arr=>arr[0]
const fun=f.flowRight(toUpper,first,reverse)
console.log(['one','two','three'])
2.如何模拟实现lodash的flowRight方法
//多函数组合
function compose(...fns){
return function(value){
return fns.reverse().reduce(funciton(acc,fn)=>{
return fn(acc)
},value)
}
}
//ES6
const compose=(...fns)=>value=>fns.reverse.reduce((acc,fn)=>fn(arr),value)
3.函数的组合要满足什么?
满足结合律,既可以把g和h组合,也可以把f和g组合,结果一样
4.如何调试组合函数
const f=_.flowRight(_.toUpper,_.first,_.reverse)
console.log(f(['one','two','three']))
const _=require('lodash')
const trace=_.curry((tag,v)=>{
console.log(tag,v)
return v
})
const split=_.curry((sep,str)=>_.split(str,sep))
const join=_.curry((sep,array)=>_.join(array,sep))
const map=_.curry((fn,array)=>_.map(array,fn))
const f=_.flowRight(join('-'),trace('map之后'),map(_.toLower),
trace('split之后'),split(' '))
console.log('NEVER SAY DIE')
5.Lodash的fp模块
提供了自动柯里化,函数优先,数据滞后
const fp=require('lodash/fp')
const f=fp.flowRight(fp.join('-'),fp.map(fp.toLower),fp.split(' '))
console.log(f('NEVER SAY DIE'));
6.Lodash-map方法的小问题
// const _=require('lodash')
// console.log(_.map(['23','8','10'],parseInt)); //[ 23, NaN, 2 ]
// parseInt()
const fp=require('lodash/fp')
console.log(fp.map(parseInt,['23','8','10'])); //[ 23, 8, 10 ]
7.Point Free
Point Free的特点:
- 不需要指明处理的数据
- 只需要合成运算过程
- 需要定义一些辅助的基本运算
//案例-1
const fp=require('lodash/fp')
const f=fp.flowRight(fp.replace(/\s+/g,'_'),fp.toLower)
console.log(f('hello World'))
//案例-2
const fp=require('lodash/fp')
// const firstLetterToUpper=fp.flowRight(fp.join('. '),fp.map(fp.first),fp.map(fp.toUpper),fp.split(' '))
const firstLetterToUpper=fp.flowRight(fp.join('. '),fp.map(fp.flowRight(fp.first,fp.toUpper)),fp.split(' '))
console.log(firstLetterToUpper('world wild web')) //W. W. W
8.为什么要学函子?
在编程式函数中如何把副作用控制在可控的范围内、异常处理和异步操作。
9.什么是函子
容器:包含值和值的变形关系(这个变形关系就是函数)
函子: 是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系)
// 一个容器,包裹一个值
class Container {
// of 静态方法,可以省略 new 关键字创建对象
static of (value) {
return new Container(value)
}
constructor (value) {
this._value = value
}
// map 方法,传入变形关系,将容器里的每一个值映射到另一个容器
map (fn) {
return Container.of(fn(this._value))
}
}
// 测试
Container.of(3)
.map(x => x + 2)
.map(x => x * x)
总结:
- 函数式编程的运算不直接操作值,而是由函子完成
- 函子就是一个实现了map契约的对象
- 我们可以把函子想象成一个盒子,这个盒子里封装了一个值
- 想要处理盒子中的值,我们需要给盒子的map方法传递一个处理值的函数(纯函数),由这个函数来对值进行处理
- 最终map方法返回一个包含新值的盒子(函子)
10.Maybe函子
作用:对外部的空值(null和undefined)进行处理
class MayBe{
static of(value){
return new MayBe(value)
}
constructor (value){
this._value=value
}
map(fn){
return this.isNothing()?MayBe.of(null):MayBe.of(fn(this._value))
}
isNothing(){
return this._value===null||this._value===undefined
}
}
// let r=MayBe.of('Hello world')
// .map(x=>x.toUpperCase())
// console.log(r);
let r=MayBe.of(null)
.map(x=>x.toUpperCase())
console.log(r);//MayBe { _value: null }
11.Either函子如何实现?作用是什么?
作用:异常会让函数变得不纯,它可以用来做异常处理
class Left{
static of(value){
return new Left(value)
}
constructor(value){
this._value=value
}
map(fn){
return this
}
}
class Right{
static of(value){
return new Right(value)
}
constructor(value){
this._value=value
}
map(fn){
return Right.of(fn(this._value))
}
}
// let r1=Right.of(12).map(x=>x+2)
// let r2=Left.of(12).map(x=>x+2)
// console.log(r1);
// console.log(r2);
function parseJSON(str){
try {
return Right.of(JSON.parse(str))
} catch (error) {
return Left.of({error:error.message})
}
}
// let r=parseJSON('{name:zs}')
// console.log(r); //Left { _value: { error: 'Unexpected token n in JSON at position 1' } }
let r=parseJSON('{"name":"zs"}')
.map(x=>x.name.toUpperCase())
console.log(r); //Right { _value: 'ZS' }
12.IO函子内部做了那些操作?
IO函子中的_value是一个函数,这里把函数作为值来处理
IO函子把不纯的动作储存在_value中,延迟执行这个不纯的操作(惰性行为),保证当前的操作纯
把不纯的操作交给调用者来处理
const fp=require('lodash/fp')
class IO{
static of(value){
return new IO(function(){
return value
})
}
constructor (fn){
this._value=fn
}
map(fn){
return new IO(fp.flowRight(fn,this._value))
}
}
// 调用
let r=IO.of(process).map(p=>p.execPath)
console.log(r) //IO { _value: [Function (anonymous)] }
console.log(r._value()) //C:\Program Files\nodejs\node.exe
13.folkfale如何使用?
folkfale是一个标准的函数式编程库,只提供一些函数式处理的操作
//folktale中的compose、curry
const {compose,curry}=require('folktale/core/lambda')
const {toUpper,first}=require('lodash')
let f=curry(3,(x,y,z)=>{
return x+y+z
})
console.log(f(1,2,3)); //6
console.log(f(1)(2)(3)); //6
let f=compose(toUpper,first)
console.log(f(['one','two']));//ONE
14.Task异步执行
folktale中的Task用来实现异步任务
//Task 处理异步任务
const fs=require('fs')
const {task}=require('folktale/concurrency/task') //当前使用的是folktale(2.3.2)
const {split,find} =require('lodash/fp')
function readFile(filename){
return task(resolver=>{
fs.readFile(filename,'utf-8',(err,data)=>{
if(err) resolver.reject(err)
resolver.resolve(data)
})
})
}
readFile('package.json')
.map(split('\n'))
.map(find(x=>x.includes('version')))
.run()
.listen({
onRejected:err=>{
console.log(err);
},
onResolved:value=>{
console.log(value); //"version": "1.0.0",
}
})
15.Pointed函子的作用?如何使用?
pointed函子是实现了of静态方法的函子
of方法是为了避免使用new来创建对象,更深层次的是of方法用来把值放到上下文Context中(把值放到容器中,使用map来处理值)
class Container{
static of(value){
return new Container(value)
}
......
}
Container.of(2)
.map(x=>x+5)
16.Monad(单子)
- monad函子是可以变扁的Pointed函子,IO(IO(x))
- 一个函数子如何具有join和of两个方法并遵守一些定律就是一个Monad
//使用IO时(会出现函子嵌套问题)
const fs=require('fs')
const fp=require('lodash/fp')
class IO{
static of(value){
return new IO(function(){
return value
})
}
constructor (fn){
this._value=fn
}
map(fn){
return new IO(fp.flowRight(fn,this._value))
}
}
let readFile=function(filename){
return new IO(function(){
return fs.readFileSync(filename,'utf-8')
})
}
let print=function(x){
return new IO(function(){
console.log(x);
return x
})
}
let cat=fp.flowRight(print,readFile)
//IO(IO(x))
let r=cat('package.json')._value()._value()
console.log(r);//package.json文件内容
相关链接: