函数式编程(三)

news/2024/7/1 2:29:34

前面两节介绍了纯函数和高阶函数,在函数式编程中,函数就是我们的砖块。我们编写一些可以完成非常具体任务的函数,然后像乐高积木一样将他们搭建起来。

这就是所谓的函数组合。

函数组合

先看两个纯函数

let add10 = value => value + 10;
let mult5 = value => value * 5;let mult5AfterAdd10 = value => mult5(add10(value)) 

这样写代码很容易写出h(g(f(e(x)))),这样层层相套的代码,一层一层拨开它的心。
为了解函数嵌套的问题,我们可以先预定义一个组合函数

var compose = function(f,g) {return function(x) {return f(g(x));};
};

然后改写上面的例子

let mult5AfterAdd10 = compose(mult5, add10)
mult5AfterAdd10(5)

这样做的可读性远远高于嵌套一大堆的函数调用。

Point Free

把一些对象自带的方法转化为纯函数,不要命名转瞬即逝的中间变量

网上有很多关于Point Free的案例,我认为都不怎么能说明问题,下面这个例子是我精心准备的机密,一般不外传。

const f = str => str.toUpperCase().split('');
const toUpperCase = word => word.toUpperCase();
const split = x => (str => str.split(x));const f = compose(split(''), toUpperCase)f("aa vv")
//我们没有使用到str变量

我们不需要指定冗余的参数。由于不必指定参数,所以也就不必考虑为它们命名。其次,由于更简短使得更容易阅读。Pointfree 的本质就是使用一些通用的函数,组合出各种复杂运算。上层运算不要直接操作数据,而是通过底层函数去处理。这就要求,将一些常用的操作封装成函数。所以说这种方式慎用。

看到这里你是不是以为自己就了解函数式编程了呢?nonono?下面我有个问题想考考你。

let add = (value, y) => value + y;
let mult5 = value => value * 5;

我想改写上面的mult5AfterAdd10函数,把它转成更通用的mult5AfterAdd,你想到用上面学到的姿势,

let mult5AfterAdd = compose(mult5, add) 

显然这个不行的,因为add函数需要接收两个参数,实际只传递了一个参数,所以它会将一个错误的结果传递给mult5。这最终会产生一个错误的结果。
我们怎么解决这个问题?事实证明有一种方法,它就是柯里化(Currying)

下面我们来看看函数的柯里化。
有这样一道题目,实现一个函数,实现如下功能:

var result = sum(1)(2)(3);
console.log(result);//6

以下是一种实现方式

function add(a){var sum = 0;sum += a;return b => {sum += b;return c => {sum += c;return sum;}}
}
add(1)(2)(3);//6

我们来解决上面遗留的问题,通过改写add函数

let add = x => y => x + y
let compose = (f, g) => x => f(g(x));
let mult5AfterAdd10 = compose(mult5, add(10));

我们就是将简单常见的add函数转化成了柯里化函数,这样add函数就变得更加自由灵活了。我们先将第1个参数10输入,而当mult5AfterAdd10函数被调用的时候,最后1个参数才有了确定的值。

柯里化

柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后,部分应用参数,并返回一个更具体的函数接受剩下的参数,中间可嵌套多层这样的接受部分参数函数,逐步缩小函数的适用范围,逐步求解,直至返回最后结果。

一个简单的通用模块的函数柯里化封装

const curry = fn => {const _args = [];return function cb() {if(arguments.length === 0) {return fn.apply(this, _args);}Array.prototype.push.apply(_args, [...arguments]);return cb;}
}

函数柯里化是一种预加载函数的方法,通过传递较少的参数,得到一个已经记住了这些参数的新函数,某种意义上来讲,这是一种对参数的“缓存”,是一种非常高效的编写函数的方法。⚠️参数顺序是能否最大程度利用柯里化的关键所在。


http://lihuaxi.xjx100.cn/news/242191.html

相关文章

[elixir! #0007] [译] 理解Elixir中的宏——part.5 重塑AST by Saša Jurić

上一章我们提出了一个基本版的deftraceable宏,能让我们编写可跟踪的函数。宏的最终版本有一些剩余的问题,今天我们将解决其中的一个——参数模式匹配。 今天的练习表明我们必须仔细考虑宏可能接收到的输入。 问题 正如我上一次暗示的那样,当前…

验证(verification)和确认(validation)

验证:看软件产品是否符合需求文档 确认:看软件产品是否满足用户需求 整个软件测试做的事是验证 但是确认似乎才应该是做软件的人的终极目标

【如何快速的开发一个完整的iOS直播app】(推流篇)

前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,肯定需要流媒体服务器,本篇主要讲解直播中流媒体服务器搭建,并且讲解了如何利用FFMPEG编码和推…

随机存取:fseek(),ftell()

随机存取:fseek(),ftell() fseek(fp,offset,pos):  文件指针定位,fp指向被打开的文件,offset为相对当前pos位置的偏移量,正数表示             向文件尾部偏移,负数表示向文件头部偏移。pos有三种状态, …

软件生命周期中出现的文档名称(cont.)

需求相关:需求规格说明书 测试相关:测试计划书,测试报告

ui设计怎样做出有效果的视觉层级?

作为一名UI设计师,大家应该清楚的了解到每一款产品都有不同的风格和设计,但是每一款UI设计元素都是有通风之处的,如何能够做出有效的视觉层级,对用户的体验有着十分积极的影响。本期UI设计培训教程就为大家详细的介绍一下ui设计怎…

js脚本冷知识

js中有个很恶心的写法。比如这个var adsf(function(){})();这样的写法,主要是原生的,能在dom元素加载完之后实现自动加载这个脚本文件到里面去。可以验证这个(function(){.......)();在.......里面可以直接…

静态测试与测试计划

文章目录1 静态测试2 评审2.1 what2.2 why2.3 形式2.4 分类2.4.1 属于软件测试的部分2.4.2 属于软件质量保证的部分:3 需求测试3.1 why3.2 需求中可能存在的问题3.3 需求文档检查要点3.3.1 完整性3.3.2 正确性3.3.3 一致性3.3.4 可行性3.3.5 无二义型3.3.6 健壮性3.…