实习面试时,字节老师疯狂用函数柯理化CPU我,我麻了

234 阅读2分钟

概念

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

嗯.... 看不懂,听不懂💥 那就看例子。看例子肯定能看出来点什么

理解

2个数求和

function add(a, b) {
  return a + b;
}
console.log(add(1, 2));
function curryAdd(fn, a) {
  return function (b) {
    return fn(a, b);
  };
}
console.log(curryAdd(add, 1)(2)); // 3

3个数求和

function addThree(a, b, c) {
  return a + b + c;
}
function curryAddThree(fn, a) {
  return function (b) {
    return function (c) {
      return fn(a, b, c);
    };
  };
}
console.log(curryAddThree(addThree, 1)(2)(3)); // 6

4个数求和

function addFour(a, b, c, d) {
  return a + b + c + d;
}
console.log(addFour.length);
function curryAddFour(fn, a) {
  return function (b) {
    return function (c) {
      return function (d) {
        return fn(a, b, c, d);
      };
    };
  };
}
console.log(curryAddFour(addFour, 1)(2)(3)(4)); // 10

enen。。。 经过上面的锻炼,我总算看出来的点什么了

闭包中:利用闭包收集参数,参数够了,运行fn,参数不够,递归,继续收集

手撕curry

/**
 * @Author: Kongjingjing
 * @Date: 2022-09-19 15:40:59
 * @Description: 柯理化
 */
function curry(fn, ...args) {
  return function (...restArgs) {
    let allArgs = [...args, ...restArgs]; // 所有参数
    if (fn.length <= allArgs.length) {
      // 参数达标
      return fn.apply(this, allArgs);
    } else {
      return curry(fn, ...allArgs); // 参数不达标-递归
    }
  };
}
function add(a, b) {
  return a + b;
}
let addCurry = curry(add);
console.log(addCurry(1)(2));

手撕2,只是参数的处理有点区别,思路一模一样

function curry2(fn, args) {
  let len = fn.length; // 参数个数
  args = args || []; // 没有参数,就是空数组初始化
  return function () {
    let _args = args.slice(0); // 拷贝参数
    for (let index = 0; index < arguments.length; index++) {
      _args.push(arguments[index]);
    }
    if (len <= _args.length) {
      return fn.apply(this, _args);
    } else {
      return curry2(fn, _args);
    }
  };
}
function add(a, b) {
  return a + b;
}
let addCurry2 = curry2(add);
console.log(addCurry2(1)(2));

手撕3, es6一句话搞定

function curry3(fn, ...args) {
  return fn.length <= args.length
    ? fn(...args)
    : curry3.bind(null, fn, ...args); // 这里为什么是bind,值得思考
}
function add(a, b, c) {
  return a + b + c;
}
let addCurry3 = curry3(add);
console.log(addCurry3(1)(2)(4));

字节面试

算法题4:JS实现add(1)(2)(3)(4)的调用方式

var add = function (m) {
  var _temp = function (n) {
    return add(m + n);
  };
  _temp.toString = function () {
    return m;
  };
  return _temp;
};
console.log(add(3)(4)(5)+1); // 13

应用场景

我还没有彻底理解柯理化。嗯,觉得网上那些说的应用把,很一般把,体会不出来好处

自己比较能接受这个使用

补充给个案例 这里使用了柯里化保存参数,并且使用了promise来封装,方便后续使用

这是一个操作mysql数据库的方法

// db.js 只是负责连接数据库, 向外暴露一个连接信息
const mysql = require('mysql')

// 创建连接池
const db = mysql.createPool({
  host: 'localhost',
  port: 3306,
  user: 'root',
  password: 'root',
  database: 'root'
})

exports.db = db

函数柯理化

const { db } = require('./db')

const select = function (sql) {
  return function (params = []) {
    return new Promise((resolve, reject) => {
      db.query(sql, params, (err, data) => {  
        if (err) return reject(err)
        resolve(data)
      })
    })
  }
}

使用

const db = require('./db_crud')

const selectUser = db.select('SELECT * FROM `student` LIMIT ?, 10')
const deleteById = db.delete('DELETE FROM `users` WHERE `id`=?')

module.exports = {
  selectUser: selectUser,
  deleteById: deleteById
}


 使用时,直接导入该模块 
 const abc=require(...)  
 acc.selectUser([参数]).then(res=>{console.log(res}) 即可

小知识点

在学习函数柯理化的时候,我意识到了fn还有length,我以为这是什么反人类行为

现在我才知道是我知识的欠缺

fn的length属性,返回的是参数个数

function a(x, y) {}

a.length; // 2

function b(x, y = 2, z) {}

b.length; // 1

function c(x, ...args) {}

c.length; //1

length 是JS函数对象的一个属性值,该值是指 “该函数有多少个必须要传入的参数”,即形参的个数

形参的数量不包括剩余参数个数,仅包括 “第一个具有默认值之前的参数个数”

参考👀

函数柯里化的理解和实现

函数柯里化以及实现

理解科里化函数(currying)及应用场景