当前位置: 首页 > news >正文

柯里化入门:拆拆拆,拆出函数式编程的优雅

💡什么是柯里化?

想象你在做饭,需要盐、酱油、葱花这些配料。普通函数就像一次性把所有材料都丢进锅里;而柯里化更像“先放盐,尝一口;再放酱油,尝一口;最后放葱花”。它把原本需要多个参数的函数,拆解成一个接一个的单参数函数,每次只处理一个参数,直到所有材料(参数)都齐了,才开始正式“开煮”。


👨‍🔬柯里化的起源

柯里化这个名字,来自逻辑学家 Haskell Curry,他的研究让函数式编程有了坚实的理论地基。现在,在像 Haskell、Scala 这样的函数式编程语言里,柯里化几乎是日常操作;在 JavaScript 中,它同样被用来写出更优雅、可复用、可组合的代码。


🚀为什么要学柯里化?

  1. 参数复用:先给函数“喂”一部分参数,返回的新函数可以在需要时继续使用。

  2. 延迟执行:函数可以只配置参数,不立即执行,直到真正需要的时候才调用。

  3. 函数组合:把多个小函数像乐高一样组合起来,形成强大的功能模块。


💻实际应用场景

无论是优化 Redux 的 Selector,处理复杂数据流,还是对深度嵌套函数进行优雅封装,柯里化都能让代码变得更灵活、更清晰、更优雅。

🍔 例子:用柯里化制作汉堡

假设你在做汉堡,需要选择面包、肉饼、酱料三个部分:

function makeBurger(bread, patty, sauce) {return `你的汉堡:${bread} + ${patty} + ${sauce}`;
}

如果使用柯里化:

function curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn.apply(this, args);} else {return function(...nextArgs) {return curried.apply(this, args.concat(nextArgs));};}};
}const curriedMakeBurger = curry(makeBurger);console.log(curriedMakeBurger('芝麻面包')('牛肉饼')('番茄酱'));
// 输出:你的汉堡:芝麻面包 + 牛肉饼 + 番茄酱

🍟 更有趣的调用方式

你甚至可以先选好面包,后面再慢慢决定其他配料:

const withBread = curriedMakeBurger('全麦面包');
const withPatty = withBread('鸡肉饼');
console.log(withPatty('黑椒酱'));
// 输出:你的汉堡:全麦面包 + 鸡肉饼 + 黑椒酱

🔧 为什么这很酷?

  • 参数固定:你可以先固定常用参数(例如固定成“全麦面包+牛肉饼”的健康汉堡)。

  • 分步配置:根据用户选择一步步生成,常用于表单、多步骤配置页面。

  • 可组合:结合其他函数式工具,如 compose、pipe,构建强大的数据处理流。

👨‍🔧 1. 从零实现柯里化

让我们写一个万能 curry 函数,把任何多参数函数,变成可以逐步调用的“厨师函数”。

function curry(fn) {return function curried(...args) {if (args.length >= fn.length) {// 参数够了,就开饭return fn.apply(this, args);} else {// 参数不够,再返回一个函数接着收return function(...args2) {return curried.apply(this, args.concat(args2));};}};
}

🔬 2. 它是怎么工作的?

  1. curry 接收目标函数(比如做饭函数)

  2. curried 函数检测参数数量:够就执行,不够就继续收集

  3. 递归调用:不停返回新函数,直到收齐全部参数

  4. 执行目标函数,返回结果

💻 3. 测试:乘法示例

function multiply(a, b, c) {return a * b * c;
}const curriedMultiply = curry(multiply);console.log(curriedMultiply(2)(3)(4)); // 24
console.log(curriedMultiply(2, 3)(4)); // 24
console.log(curriedMultiply(2)(3, 4)); // 24

🎉 多灵活! 你可以一步步传,也可以分批传,最后总会算出结果。

🌈 4. 柯里化的优点

(1) 参数复用

先固定一部分参数,像提前腌好的鸡肉,随时可用。

function greet(greeting, name) {return `${greeting}, ${name}!`;
}const sayHello = curry(greet)("Hello");
console.log(sayHello("Alice")); // Hello, Alice!
console.log(sayHello("Bob"));   // Hello, Bob!


(2) 延迟执行

柯里化可以“先接收参数,后执行”,就像提前预约:

const add = curry((a, b) => a + b);
const addFive = add(5);
console.log(addFive(10)); // 15

(3) 函数组合

把多个函数组合成数据处理管道,逻辑清晰,读起来就像在看流水线

const compose = (f, g) => (x) => f(g(x));
const addOne = (x) => x + 1;
const double = (x) => x * 2;const addOneThenDouble = compose(double, addOne);
console.log(addOneThenDouble(3)); // 8


💡 5. 为什么柯里化这么强大?

它的核心理念就是:

  • 一步做一件事,函数变得简单、纯粹、好测试。

  • 没有副作用,只依赖参数,不影响全局,代码更安全可靠。


🚀 6. 实际应用场景

🛒 (1) Redux Selector 优化

在 Redux 的 reselect 中,柯里化用于高性能 Selector。

const selectCart = (state) => state.cart;
const selectCartItems = createSelector(selectCart,(cart) => cart.items
);const selectCartTotal = createSelector(selectCartItems,(items) => items.reduce((total, item) => total + item.price * item.quantity, 0)
);console.log(`Cart Total: $${selectCartTotal(state)}`);

🌟 好处:避免重复计算,让状态派生更快。


💬 (2) 数据管道:敏感词过滤 + 格式化 + 统计字数
const filterSensitiveWords = curry((words, text) => words.reduce((acc, word) => acc.replace(new RegExp(word, 'gi'), '***'), text)
);
const formatText = (text) => text.trim().toLowerCase();
const countWords = (text) => text.split(/\s+/).length;const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);const processTextPipeline = compose(countWords,formatText,filterSensitiveWords(['badword', 'offensive'])
);const result = processTextPipeline(" This is a BadWord example! Badword is offensive. ");
console.log(result); // 输出处理后的字数

🔗 (3) 深度嵌套函数优化:带 Token 的 fetch
const fetchWithAuth = curry((authToken, endpoint) => fetch(endpoint, { headers: { Authorization: `Bearer ${authToken}` } })
);const fetchUserData = fetchWithAuth('my-secure-token');Promise.all([fetchUserData('/api/user/1'),fetchUserData('/api/user/2')
]).then(([user1, user2]) => Promise.all([user1.json(), user2.json()])).then(console.log).catch(console.error);

http://www.xdnf.cn/news/1106641.html

相关文章:

  • OSPFv3-一二类LSA
  • Qt:QCustomPlot类介绍
  • Qt窗口:菜单栏
  • 【攻防实战】记一次DC2攻防实战
  • 华为 GaussDB :技术特性、应用局限与市场争议
  • Java使用Langchai4j接入AI大模型的简单使用(二)
  • windows电脑远程win系统服务器上的wsl2
  • OneCode 3.0架构升级:注解驱动与开放接口生态详解
  • 数据结构栈的实现(C语言)
  • 《Java Web程序设计》实验报告五 Java Script学习汇报
  • MS Azure Eventhub 发送 AD log 到cribl
  • 李宏毅(Deep Learning)--(三)
  • Raft 代码分析
  • 人工智能之数学基础:多元逻辑回归算法的矩阵参数求导
  • stack和queue的使用和模拟实现以及了解deque
  • Java基础:泛型
  • 以数据为核心,以业务为导向,漫谈数据可视化应用
  • Leet code 每日一题
  • 【LeetCode】算法详解#8 ---螺旋矩阵
  • 粒子滤波|粒子滤波的相关算法理论介绍
  • 引入了模块但没有使用”,会不会被打包进去
  • STP生成树划分实验
  • 智能制造——解读50页智能工厂系统集成总体解决方案【附全文阅读】
  • Capsule Networks:深度学习中的空间关系建模革命
  • XML 指南
  • 每日一SQL 【 超过 5 名学生的课】
  • TCP的socket编程
  • 【学习新知识】用 Clang 提取函数体 + 构建代码知识库 + AI 问答系统
  • 【Modern C++ Part10】Prefer-scoped-enum-to-unscoped-enums
  • 【Java八股文总结 — 包学会】(二)计算机网络