You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
常用的高阶函数:
foreach map filter every some find/findindex/reduce/sort...
const map = (array,fn) => {
let results = []
for(let value of array){
results.push(fn(value))//让fn处理数组里的每一个元素,也就是v*v
}
return results
}
//test
let arr = [1,2,3,4]
//求平方
arr = map(arr, v => v * v)
console.log(arr)
const every = (array, fn) => {
let result = true
for(let value of array) {
result = fn(value)
//fn()返回bool类型值
if(!result){
break
}
}
return result
//判断数组中每个元素是否满足某条件(fn内容)
}
let arr = [11,12,14]
let r = every(arr, v => v > 10)
const some = (array,fn) => {
let result = false
for(let value of array){
result = fn(value)
if(result){
break
}
}
return result
}
let arr = [1,2,4,9]
let r = some(arr, v => v & 2 === 0)
console.log(r)
//r = true
//数组里还是有一偶数的
let mini = 18
function checkAge(age){
return age >= mini
}
//因为全局变量是可改变的,所以并不是纯函数
//改造(硬编码,可以通过柯里化解决)
function checkAge(age){
let mini = 18
return age >= mini
}
//普通纯函数,不再依赖外部变量,也没有硬编码
function checkAge(mini, age){
return age >= mini
}
console.log(checkAge(18,20))
console.log(checkAge(18,22))
console.log(checkAge(18,23))
//18经常使用的话,就可以使用闭包避免18重复
function checkAge(mini){
return function(age){
return age >= mini
}
}
let checkAge18 = checkAge(18) //返回一个新的函数,新的基准值就是18
let checkAge20 = checkAge(20)
console.log(checkAge18(20))
console.log(checkAge18(22))
console.log(checkAge18(23))
//这样就是函数的柯里化!
function compose(f, g){
//返回的函数,需要接收一个参数value.它相当于一个管道,需要输入n个参数,并且处理输出1个结果
return function(value){
return f(g(value))
//先右到左处理函数
}
}
//求数组最后一个元素
function reverse(array){
return array.reverse()
}
function first(array){
return array[0]
}
const last = compose(first,reverse)
console.log(last([1,2,3,4]))
Ⅲ、loadsh中的组合函数
_.flow()从左到右运行
_.flowRight()从右到左运行
const _ = require('loadsh')
const reverse = arr => arr.reverse()
const first = arr => arr[0]
const toUpper = s => s.toUpperCase()
const f = _.flowRight(toUpper,first,reverse)
//从右到左:翻转、取1、转换大写
Ⅳ、满足结合律
即,可以把g和f结合,还可以把f和g结合,结果应一样
//结合律 (associativty)
let f = compose(f,g,h)
let associative = compose(compose(f,g),h) == compose(f,compose(g,h))
//true
const f = _.flowRight(_.toUpper,_.first,_.reverse)
const f = _.flowRight(_.flowRight(_.toUpper,_.first),_.reverse)
// 函子
class Container{
constructor(value){
this._value = value
}
//构造函数
map(fn){
return new Container(fn(this._value))
//调用fn去处理内部值,然后传给一个新的对象,返回一个新的函子对象
}
}
let r = new Container(5)
.map(x => x + 1)//返回一个新的函子
.map(x => x * x)//
console.log(r)
//=======================使用of改造函子更函数式编程=======================
class Container{
static of (value) {
return new Container(value)
}
//避免进行new,将new封装到静态方法里
constructor(value){
this._value = value
}
//构造函数
map(fn){
console.log(fn(this._value))
return Container.of(fn(this._value))
//调用fn去处理内部值,然后传给一个新的对象,返回一个新的函子对象
}
}
let r = Container.of(5)
.map(x => x + 2)
.map(x => x * x)
//可以不停地点map,链式编程
console.log(r)
//r并不是一个值,而是一个函子对象,值在函子对象里
//我们不碰这个值,而是通过函子里的方法去使用它,也可以在方法里打印它
一、介绍
functional programming , FP是编程范式之一,编程范式也包括面向过程编程、面向对象编程
面向对象:把事物抽象成程序中的类和对象,通过封装、继承和多态演示事物联系(抽象事物)
函数式编程:把事物和事物之间的联系抽象到程序世界(抽象运算)
程序本质 :根据输入通过某种运算获得相应的输出
函数式编程中函数指的不是程序中的函数/方法,而是数学中的函数即映射关系:y = sin(x),y和x的关系
相同的输入始终要得到相同的输出(纯函数)
作用:函数式编程就是用来描述数据之间的映射
相同输入-》相同输出
优点:代码重用
特点:
Vue3、React框架
可以抛弃this
打包过程中可以更好地利用tree shaking过滤无用代码
方便测试、方便并行处理
函数式开发库:lodash、underscore、ramda
二、函数式的特性
Ⅰ、函数是一等公民 first-class function
1.函数可以存储在变量中
//把函数赋值给变量
2.函数作为参数(高阶函数)
3.函数作为返回值(高阶函数)
Ⅱ、高阶函数 higher-oder function
1.可以把函数作为参数传递给另一个函数
优点:灵活、不需要考虑函数内部如何实现
2.可以把函数作为另一个函数的返回值
意义:
抽象可以屏蔽细节,只需要关心目标(封装内容,使用时并不需要关注怎么实现的)
高阶函数用来抽象通用的问题‘
常用的高阶函数:
foreach map filter every some find/findindex/reduce/sort...
总结:可以让函数变得更加灵活
Ⅲ、闭包(closure)
定义:函数和周围的状态(词法环境)的引用捆绑在一起形成闭包
可以在另一个作用域中调用一个函数的内部函数并访问到该函数的作用域中的成员
闭包的本质:函数执行的时候会放到一个执行栈上,当函数执行完毕之后会从执行栈上移除,但是栈上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问到外部函数的成员
三、纯函数
Ⅰ、介绍
“纯函数”是相对于函数式编程的概念。即,相同的输入永远会得到相同的输出,且没有副作用。
类似于数学中的函数,映射关系y = f(x)
功能库:lodash 提供了对数组,数字,对象,字符串,函数等一些方法
纯函数:slice,截取数组元素。返回数组中的指定部分,不会改变原数组(相同的输入得到相同的输出)
不纯的函数:splice,对数组操作返回该数组,会改变原数组(会移除数组中的元素)
纯函数必须有输入和输出!
函数式编程不会保留计算中间的结果,所以变量不可变(无状态的)
可以把一个函数的执行结果交给另一个函数处理
Ⅱ、优势
可缓存:因为纯函数对相同的输入始终有相同的结果,所以可以把纯函数的结果缓存起来。
可测试
我们只需要给函数一个输入,然后只看输出结果就可以了
并行处理
纯函数只依赖于参数,是一个封闭的空间
Ⅲ、副作用
函数依赖外部作用来决定输出结果,就会带来副作用
来源:配置文件、数据库、获取用户的输入……
缺点:
使得方法通用性下降,不适合扩展和可重用性
给程序带来安全隐患和不确定性
但不可能完全禁止,尽可能控制它们在可控范围内发生
四、柯里化(Haskell Brooks Curry)
柯里化(currying):
调用一个函数只传递部分的参数,并且返回新的函数,新函数去接受剩余的参数,并且返回相应的结果。
//函数的柯里化,ES6箭头函数
let checkAge = mini => (age => age >= mini)
//箭头函数如果只有一句代码的话相当于直接返回结果
Ⅰ、概念:
当一个函数有多个参数的时候,先传递一部分参数调用它(这部分参数以后永远不变)
然后返回一个新的函数接收剩余的参数,返回结果
Ⅱ、Lodash里的柯里化:_.curry(func)
//本身就是一个纯函数
功能:该函数接收一个或者多个func的参数,如果func所需要的参数都被提供则执行func并返回执行的结果,否则继续返回该函数并等待接收剩余的参数。
参数:需要柯里化的函数
返回值:柯里化后的函数
Ⅲ、柯里化示例
生成提取字符串空格(某个特定正则规则)的函数
这样就可以最大程度地重用函数
Ⅳ、总结:
让一个函数传递较少的参数,得到一个已经记住了某些固定参数的新函数
对函数参数的一种"缓存"(闭包)
函数灵活、粒度小(组合函数)
多元函数转换成一元函数,可以组合使用函数
五、函数组合
纯函数和柯里化容易写出洋葱代码h(g(f(x)))
函数组合可以让我们把细粒度的函数重新组合成一个新的函数
Ⅰ、管道
Ⅱ、概念
函数组合(compose):如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并成一个函数
函数就像是数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果
函数组合默认是从右到左执行
Ⅲ、loadsh中的组合函数
_.flow()从左到右运行
_.flowRight()从右到左运行
Ⅳ、满足结合律
即,可以把g和f结合,还可以把f和g结合,结果应一样
注意,输出结果和fllowRight内部函数运行顺序有关!
Ⅴ、调试
六、loadsh/fp
Ⅰ、介绍
不可变的,auto-curried iteratee-first data-last的方法
Ⅱ、map方法的区别
七、Point Free
一种编程的风格,具体实现是函数的组合,更抽象
Ⅰ、介绍
把数据处理的过程定义成与数据无关的合成运算,不需要用到代表数据的那个参数,只要把简单的运算步骤合在一起(函数的组合),在使用此模式之前我们需要定义一些辅助的基本运算函数
不需要指明处理的数据
只需要合成运算的过程
需要定义一些辅助的基本运算函数
Ⅱ、案例
八、函子 (Functor)
Ⅰ、介绍
通过函控制副作用,也可以进行异常处理、异步操作等
容器:包含值和值的变形关系
函子:是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系)
和面向对象的编程有何区别????
函数式编程一般约定,函子有一个of方法,用来生成新的容器
Ⅱ、总结
函数式编程的运算不直接操作值,而是由函子完成
函子就是一个实现了map契约的对象
函子就是一个盒子,盒子封装了一个值
处理盒子的值,需要给map方法传递一个处理值得函数(纯函数)
最终map方法会返回一个包含新值的盒子(函子)
Ⅲ、MayBe函子——函子处理空值问题
MayBe函子的作用就是对外部的空值情况做处理(控制副作用在允许的范围)
Ⅳ、Either函子
Either两者中的任何一个,类似于if...else...
异常让函数变得不纯,Either函子可以用来做异常处理
Ⅴ、IO函子
IO函子中的_value是一个函数,这里是把函数作为值来处理
IO函子可以把不纯的动作存储到_value中,延迟执行这个不纯的操作(惰性执行),包装当前的操作纯
把不纯的操作交给调用者来处理
Ⅵ、folktale
一、Task异步执行
folktale一个标准的函数式编程库
和lodash、ramda不同的是,他没有提供很多功能函数
只提供了一些函数式处理的操作,例如:compose、curry等,一些函子Task、Either、MayBe等
演示compose和curry:
二、Task函子(处理异步任务)
Ⅶ、Pointed函子(概念,一直在用)
Pointed函子是实现了of静态方法的函子
of方法是为了避免使用new来创建对象,of方法用来把值放到上下文Context(把值放到容器中,使用map来处理值)
Ⅷ、Monad(单子)
可以延迟函数的使用,把纯函数借助IO函子封装起来,避免副作用
Ⅸ、Monad函子
Monad函子是可以变扁的Pointed函子,IO(IO(x)) 解决函子嵌套的问题(函数嵌套就用组合函数解决)
一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad
The text was updated successfully, but these errors were encountered: