1. 函数柯里化
函数柯里化:是把接受多个参数的函数转变为接受一个参数的函数。 大家好,我是EchoTX,这篇文章主要记录对柯里化函数的理解与实现。实现方式采用递归算法《红宝书》---函数通过名称调用自己。个人感觉:不考虑性能,大部分项目中遇到的算法问题都可以采用递归的方式来解决。这里记录柯里化递归实现,以及一些使用递归解决的问题。
涉及的知识点:
- function
- arguments
- this
- 闭包
1.1 加法器基础
需求1: 实现一个加法器
arguments
对象不是一个Array
。它类似于Array
,但除了length属性和索引元素之外没有任何Array
属性。 --> 来自MDN
//1. 单参数
function add(a, b){
return a + b;
}
add(1, 2);
//2. 多参数
function add(...arg){
return arg.reduce((sum, num) => sum + num);
}
add(1, 2, 3);
//3. 使用arguments
function add(){
var args = [].slice.call(arguments)
return args.reduce((sum, num) => sum + num);
}
add(1, 2, 3);
调用 add(1, 2) 算出 1 + 2 的结果为:3。
注: 多参数使用 ...arg 展开数组其实是实例化了一个Array,而arguments是一个类数组。
通常使用[...arguments]、[].slice.call(arguments)、Array.from(arguments)的方法将类数组转换为Array
1.2 柯里化加法器
需求2: 柯里化实现加法器
//1. 两个参数
function add(a){
return function(b) {
return a + b;
}
}
add(1)(2)
//2. 三个参数
function add(a){
return function(b) {
return function(c) {
return a + b + c
}
}
}
add(1)(2)(3)
问题:需求1实现的方式直接比需求2方式实现简单多了 使用柯里化感觉更麻烦了呢,那么需求3来了。
需求3: 加法器计数,具有存储功能。
//1. 两个参数
function add(a){
return function(b) {
return a + b
}
}
var IncreaseFirst = add(1)
var IncreaseSecond = add(6)
IncreaseFirst(2) // 3
IncreaseSecond(2) // 8
//1. 三个参数
function add(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
var IncreaseFirst = add(1); //存第一个
var IncreaseSecond = IncreaseFirst(1); //存第二个
var IncreaseThird = IncreaseSecond(1); //存第三个
console.log(IncreaseThird); //3
上面的例子就利用了柯里化 闭包的性质,保存了参数,最后需要的时候再进行调用。
1.3 柯里化实现
需求4: 从上面已经认识到了函数的柯里化,那么请实现一个简单版的柯里化函数。
function currying(fn) {
var count = arguments.length;
return function curried() {
var _this = this
var args = [...arguments];
if (count < 0) {
return fn.apply(_this, args);
}
count--;
return (arg) => {
return curried.apply(_this, [...args, arg])
};
};
}
var fun = currying(function add(a, b, c) {
console.log([a, b, c]);
});
fun(2)(1)(3);
2. 递归练习1: 斐波那契数列实现
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 特别指出:第0项是0,第1项是第一个1。从第三项开始,每一项都等于前两项之和。
//代码实现
var fib = function(n) {
if(n < 2) return n;
return fib(n - 1) + fib(n - 2);
};
fib(7)
3. 递归练习2: 树型化结构扁平
需求1: 将以下树形数据进行扁平化
var treeList = [
{
id: 1,
name: 'A',
children: [
{
id: 2,
name: 'B',
children: [
{
id: 8,
name: 'H'
}
]
},
{
id: 3,
name: 'C',
}
]
},{
id: 4,
name: 'D',
children: [
{
id: 5,
name: 'E',
},
{
id: 6,
name: 'F',
}
]
},{
id: 7,
name: 'G',
}
]
3.1 初步实现
function treeToList(data, treeList = []){
var list = treeList
for(let k in data){
var {children, ...other} = data[k]
list.push(other)
if(children){
treeToList(children, list)
}
}
return treeList
}
var result = treeToList(treeList)
从上面代码及效果图 可见已经实现了树型化数据扁平了。
仔细看看,好像总觉得缺少了什么
最后发现: 数据是扁平了但是数据之间没有关联。(树型化数据扁平化
-- 扁平数据树型化
可以相互转换的)
3.2 最终实现
从初步实现中,缺少子级与父级的关系,那么可以声明一个pid 和父级 id相对应的关系进行数据扁平化。 如下:
function treeToList(data, treeList = [], pid = 0){
var list = treeList
for(let k in data){
var {children, ...other} = data[k]
if(children){
other.pid = pid
list.push(other)
treeToList(children, list, data[k].id)
}else{
var _children = JSON.parse(JSON.stringify(data[k]))
_children.pid = pid
list.push(_children)
}
}
return treeList
}
var result = treeToList(treeList)
4. 递归练习3: 扁平化数据树型化
上述已经讲到了树型化结构扁平化,这里就再利用递归的方法再实现实现扁平化数据转为树型化吧
4.1 递归实现
function listToTree(data){
var pNode = data.filter(item => item.pid === 0); //求出最外层的父节点
var cNode = data.filter(item => item.pid !== 0); //求出其余子节点
// pid 和 id的关系进行关系匹配
toTree(pNode, cNode)
return pNode
function toTree(parent, child){
parent.map(pItem => {
child.map(cItem => {
if(cItem.pid === pItem.id){
var _child = JSON.parse(JSON.stringify(child))
_child.splice(0, 1)
toTree([cItem], _child)
if(pItem.children){
pItem.children.push(cItem)
}else{
pItem.children = [cItem]
}
}
})
})
}
}
listToTree(list)
4.2 实现思路
通过上面代码即可实现扁平化数据转树型化数据。实现思路:
- 将祖先节点和子节点区分开
- 通过子节点的pid和父节点的id进行匹配
- 父节点没有children键名,就进行添加,有的话就push
- cItem不是数组递归和赋值时 需变为数组
- 通过splice删除已确认关系的元素, 减少循环次数
5 写在最后
快乐学习每一天