柯里化(currying)与部分应用(partial application) / 优化嵌套的条件语句 / 运用存储加速递归

JavaScript的那些奇技淫巧

马跃Marico4 天前

一. No1.柯里化(currying)与部分应用(partial application)
柯里化(currying)
柯里化是使一个函数
f: X * Y -> R
转变为
f’: X -> (Y -> R)
与用两个参数调用f不同,我们用一个参数运行f’。返回的结果是一个函数,然后用第二个参数调用此函数,得到结果。
如此,如果未柯里化的函数f这样调用
f(3,5)
柯里化后的函数f’是这样调用的
f(3)(5)
比如: 未柯里化的函数add()
function add(x, y) { return x + y;}add(3, 5); // returns 8

柯里化后的add()
function addC(x) { return function (y) { return x + y; }}addC(3)(5); // returns 8

柯里化的规则
柯里化将一个二元函数,转变为一元函数,这个函数将返回另一个一元函数。
curry: (X × Y → R) → (X → (Y → R))
Javascript Code:
function curry(f) { return function(x) { return function(y) { return f(x, y); } }}

部分应用(partial application)
部分应用将一个函数
f: X * Y -> R
的第一个参数固定而产生一个新的函数
f`: Y -> R
f’与f不同,只需要填写第二个参数,这也是f’比f少一个参数的原因。
比如:将函数add的第一个参数绑定为5来产生函数plus5。
function plus5(y) { return 5 + y;}plus5(3); // returns 8

部分应用的规则
部分应用使用一个二元函数和一个值产生了一个一元函数。
partApply : ((X × Y → R) × X) → (Y → R)
Javascript Code:
function partApply(f, x) { return function(y) { return f(x, y); }}

No2.优化嵌套的条件语句
我们怎样来提高和优化javascript里嵌套的if语句呢?
if (color) { if (color === 'black') { printBlackBackground(); } else if (color === 'red') { printRedBackground(); } else if (color === 'blue') { printBlueBackground(); } else if (color === 'green') { printGreenBackground(); } else { printYellowBackground(); }}

一种方法来提高嵌套的if语句是用switch语句。虽然它不那么啰嗦而且排列整齐,但是并不建议使用它,因为这对于调试错误很困难。这告诉你为什么。
switch(color) { case 'black': printBlackBackground(); break; case 'red': printRedBackground(); break; case 'blue': printBlueBackground(); break; case 'green': printGreenBackground(); break; default: printYellowBackground();}

如果可以重构的话,我们可以试着简化函数。比如不需要为每个颜色写一个函数,而是将颜色作为函数的参数。
function printBackground(color) { if (!color || typeof color !== 'string') { return; }}

但是如果不能重构的话,我们必须避免过多的条件检查,避免过多使用switch。我们必须考虑最有效率的方法,使用object。
let colorObj = { 'black': printBlackBackground, 'red': printRedBackground, 'blue': printBlueBackground, 'green': printGreenBackground, 'yellow': printYellowBackground};if (color in colorObj) { colorObjcolor;}

No3.运用存储加速递归
大家对斐波那契(Fibonacci)数列都很熟悉。我们可以再20秒内写出下面这样一个方法。
var fibonacci = function(n){ return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2);}

它可以运行,但并不高效。它做了太多重复的运算,我们可以通过存储这些运算结果来使其加速。
var fibonacci = (function() { var cache = [0, 1]; // cache the value at the n index return function(n) { if (cache[n] === undefined) { for (var i = cache.length; i <= n; ++i) { cache[i] = cache[i-1] + cache[i-2]; } } return cache[n]; }})()

我们也可以定义一个高阶函数,它接收一个方法作为参数,返回一个该方法运用存储后的新方法。
var memoize = function(func){ var cache = {}; return function(){ var key = Array.prototype.slice.call(arguments).toString(); return key in cache ? cache[key] : (cache[key] = func.apply(this,arguments)); }}fibonacci = memoize(fibonacci);

ES6版本的memoize函数如下:
var memoize = function(func){ const cache = {}; return (...args) => { const key = [...args].toString(); return key in cache ? cache[key] : (cache[key] = func(...args)); }}fibonacci = memoize(fibonacci);

我们可以将memoize()用在很多其他地方

  • GCD(最大公约数)
    var gcd = memoize(function(a,b){ var t; if (a < b) t=b, b=a, a=t; while(b != 0) t=b, b = a%b, a=t; return a;})gcd(27,183); //=> 3

  • 阶乘运算
    var factorial = memoize(function(n) { return (n <= 1) ? 1 : n * factorial(n-1);})factorial(5); //=> 120

Original:[Js Tips - A JavaScript tip per day!*
") 0px 2px / cover;">*
](Js Tips - A JavaScript tip per day!*
") 0px 2px / cover;">*
)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1.函数参数的默认值 (1).基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
    赵然228阅读 4,064评论 0 0
  • 原文链接 Haskell和scala都支持函数的柯里化,JavaScript函数的柯里化还与JavaScript的...
    dreamapple阅读 7,325评论 0 24
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile丽语阅读 9,307评论 0 6
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 14,357评论 0 33
  • 今天,是不平静的一天,没有遇到意外,也没有身体不舒服,是内心好久以来没有这样像过电影一样回想起往日的种种场景,...
    佳贝健康阅读 1,270评论 0 1

友情链接更多精彩内容