本文作者:包子也沉默

JavaScript ES6函数式编程(二):柯里化、偏应用、组合、管道

包子也沉默 3年前 (2019-10-23) ( 10-23 ) 866 0条评论
摘要: 我们要求一个函数只做一件事。下面看下如何应用compose函数://通过组合计算字符串单词个数letsplitIntoSpaces=(str)=>str.split("");//分割成数组letcount=(array)=>array.length;//计算长度constcountWords=compose(count,splitIntoSpaces);countW

上一篇介绍了闭包和高阶函数,这是函数式编程的基础核心。这一篇来看看高阶函数的实战场景。

首先强调两点:

lure +=USERID    //登陆失败日志  注:#disable= yes此字段为yes或者no都不是决定能否使用root登陆的条件(此处被度娘搜索到的帖子误导了)&nb

  • 注意闭包的生成位置,清楚作用域链,知道闭包生成后缓存了哪些变量
  • 高阶函数思想:以变量作用域作为根基,以闭包为工具来实现各种功能

柯里化(curry)

定义:柯里化是把一个多参数函数转换为一个嵌套的一元函数的过程

用Telnet连接一下试试看吧!请使用普通用户,root权限还没开,如果不行请按照步骤检查或reboot。[图8][root@azeroth~]#telnet192.168.11.130Trying1

先看个简单的例子,这是一个名为 add 的函数:const add = (x, y) => x + y;调用该函数 add(1, 1)、add(1, 2)、add(1, 3)...很普通,缺乏灵活性。

y(null,args)}}这样,我们就能处理多个参数的函数了。比如:constmultiply=(x,y,z)=>x*y*z;constcurryMul=curry(multiply);con

下面是柯里化实现版本:

n)=>fn(acc),value);下面看下如何应用pipe函数://通过管道计算字符串单词个数letsplitIntoSpaces=(str)=>str.split("&qu

const addCurried = x => y => x + y;

如果我们用一个单一的参数调用 addCurried,const add1 = addCurried(1)它返回一个函数fn = y => 1 + y,在其中 x 值通过闭包缓存下来。接下来,我们继续传参add1(1); add1(2); add1(3),有没有感觉比上面的 add 灵活。

应用函数参数。实际上,偏应用是为一个多元函数预先提供部分参数,从而在调用时可以省略这些参数。比如我们要在每10ms做一组操作。可以通过setTimeout函数以如下方式实现:setTimeout(()

上面的实现只是针对接收两个参数相加的柯里化函数,接下来正是开始实现个基础的通用的接收两个参数的柯里化函数:

分配密码的用户也没登陆上去)。{}部分#disable= yes          //我理解的意思大概是说{} 内的字段默认情况下是不被使用的(这个理解似乎有问题,原文:&rdq

const curry = (binaryFn) => {
    return function (firstArg) {
        return function (secondArg) {
            return binaryFn (firstArg, secondArg) ;  // 为啥要嵌套那么多呢?基于什么思路呢?思考一下...
        };
    };
};

现在可以用如下方式通过 curry 函数把 add 函数转换成一个柯里化版本:

的作用是把一个多参数函数转换为一个嵌套的一元函数的过程。随着闭包的产生,我们可以灵活的调用。组合和管道类似,都是串行处理数据。传入一个初始数据,通过一系列特定顺序的纯函数处理成我们希望得到的数据。参考

const autoCurriedAdd = curry(add)
autoCurriedAdd(1)(1)  // 2

这里我们已经体会到柯里化的好处了,那么柯里化是怎样实现的呢?看上面 curry 的实现很容易发现,先传入一个接受二元函数,然后返回一个一元函数,当这个一元函数执行后,再返回一个一元函数,再次执行返回的一元函数时,触发最开始那个二元函数的执行。

是要还的!  有不明的问题的时候,都来博客园转转,总能找到答案或者灵感,开博3个月都没发一篇帖(不晓得管理员有何感想,不会封我的号吧),不能只是索取没有付出。小白一枚琢磨了半天才扒拉明白Telnet服

这里有一个点很重要——执行时机,接收够两个参数(add 函数接收的参数数量)立即执行,也就是说接收够被柯里化函数的参数数量时触发执行

o都不是决定能否使用root登陆的条件(此处被度娘搜索到的帖子误导了)    其实,Setup3说了这么多,做了很多次实验和测试,此配置文件和是否能使用root登陆Telnet并没

好的,我们已经实现了一个基础的柯里化函数。不过,这个 柯里化函数有很大的局限性——只能用于接收两个参数的函数。我们需要的是被柯里化函数的参数可以任意数量,怎么办呢?还好我们已经知道了被柯里化函数的执行时机——接收够被柯里化函数的参数数量时触发执行。下面我们来实现更复杂的柯里化:

@azeroth~]#telnet192.168.11.130Trying192.168.11.130...Connectedto192.168.11.130.Escapecharacteris

// 柯里化函数
const curry = (fn) => {
  if (typeof fn !== "function") {
    throw Error("No function provided")
  }

  return function curriedFn (...args) {
    if (fn.length > args.length) {  // 未达到触发条件,继续收集参数
      return function () {
        return curriedFn.apply(null, args.concat([].slice.call(arguments)))
      }
    }
    return fn.apply(null, args)
  }
}

这样,我们就能处理多个参数的函数了。比如:

*y*z;constcurryMul=curry(multiply);constresult=curryMul(1)(2)(3);//1*2*3=6偏应用(partial)偏应用,又称作部分应用,它允

const multiply = (x, y, z) => x*y*z;

const curryMul = curry(multiply);
const result = curryMul(1)(2)(3); // 1*2*3 = 6

偏应用(partial)

偏应用,又称作部分应用,它允许开发者部分地应用函数参数。实际上,偏应用是为一个多元函数预先提供部分参数,从而在调用时可以省略这些参数

rreadingaboutcomposition");//5总结通过这一节的学习,我们知道了高阶函数的一些应用——柯里化、偏应用、组合和管道,每种应用都有特定的应用场景。其中,柯里化是最常用

比如我们要在每10ms做一组操作。可以通过 setTimeout 函数以如下方式实现:

sp;?#socket_type=stream      // 网络套接字类型#user       =root&nbs

setTimeout( () => console.log("Do X task"), 10);
setTimeout( () => console.log("Do Y tash"), 10);

很显然,我们可以用上面的 curry 函数包装成柯里化函数,实现灵活调用:

edhat系列系统的安全防护重点,不论配置什么应用建议都不要随意关闭,毕竟安全问题不能忽略。[图7][root@azeroth~]#firewall-cmd--add-port=23/tcp--per

// 实现一个二元函数,用于柯里化
const setTimeoutWrapper = (time, fn) => {
    setTimeout(fn, time);
}

// 使用 curry 函数封装 setTimeout 来实现一个10ms延迟
const delayTenMs = curry(setTimeoutWrapper)
delayTenMs( () => console.log("Do X task") );
delayTenMs( () => console.log("Do Y task") );

很棒,也能实现灵活调用。但问题是我们不得不创建 setTimeoutWrapper 一样的封装器,这也是一种开销。下面我们看看偏应用的实现:

;  完成以上步骤,基本实现了Telnet服务的搭建,快到CMD里面使用Telnet连接一下试试看吧!请使用普通用户,root权限还没开,如果不行请按照步骤检查或reboot。[图

// 偏应用函数
const partial = (fn, ...partialArgs) => {
  let args = partialArgs
  return (...fullArguments) => {
    let count = 0
    for (let i = 0; i < args.length && count < fullArguments; i++) {
      if (args[i] === undefined) {
        args[i] = fullArguments[count++]
      }
    }
    return fn.apply(null, args)
  }
}

下面用偏应用解决上面的延时10ms问题:

ncat([].slice.call(arguments)))}}returnfn.apply(null,args)}}这样,我们就能处理多个参数的函数了。比如:constmultiply=(x,y,

let delayTenMs = partial(setTimeout, undefined, 10);  // 注意此处,让我们少创建了一个 setTimeoutWrapper 封装器
delayTenMs( () => console.log("Do X task") )
delayTenMs( () => console.log("Do Y task") );

现在我们对柯里化有了更清晰的认识。创建偏应用函数时,第一个参数接收一个函数,剩余参数是第一个传入函数所需参数。剩余参数待传入的用undefined占位,执行偏应用函数时填充undefined

ray)=>array.length;//计算长度constcountWords=pipe(splitIntoSpaces,count);//注意此处的传参顺序countWord("h

组合(compose)

在了解什么是函数式组合之前,让我们理解组合的概念。

希望有大神指引一下)#flags    =REUSE        //没有理解这里的意思是什么,请大神帮忙解答“标识= 可重复使用&rd

符合“|”被称为管道,它允许我们通过组合一些函数去创建一个能够解决问题的新函数。大致来说,“|”将最左侧的函数输出作为输入发送给最右侧的函数!从技术上讲,该处理过程称为“管道”。

nbsp;    =root          //使用root用户运行服务#server    &

compose 函数:

h~]#firewall-cmd--add-port=23/tcp--permanentsuccess[root@azeroth~]#firewall-cmd--reloadsuccessSetup5

const compose = (a, b) => (c) => a(b(c))

compose 函数会首先执行 b 函数,并将 b 的返回值作为参数传递给 a。该函数调用的方向是从右至左的(先执行 b,再执行 a)。

helloyourreadingaboutcomposition");//5上面的compose只能实现两个函数的组合。如何组合更多个函数呢?这就需要借助reduce的威力了://组合多个函

可以看到,组合函数 compose 就是传入一些函数。对于传入的函数,我们要求一个函数只做一件事

n(...args){if(fn.length>args.length){//未达到触发条件,继续收集参数returnfunction(){returncurriedFn.apply(null,

下面看下如何应用 compose 函数:

lue);下面看下如何应用pipe函数://通过管道计算字符串单词个数letsplitIntoSpaces=(str)=>str.split("");//分割成数组letco

// 通过组合计算字符串单词个数
let splitIntoSpaces = (str) => str.split(" ");   // 分割成数组
let count = (array) => array.length;  // 计算长度

const countWords = compose(count, splitIntoSpaces);

countWord("hello your reading about composition"); // 5

上面的 compose 只能实现两个函数的组合。如何组合更多个函数呢?这就需要借助reduce的威力了:

mctlstarttelnet.socket[root@azeroth~]#systemctlstatustelnet.socket●telnet.socket-TelnetServerActivat

// 组合多个函数 composeN
const composeN = (...fns) => 
    (value) => 
        fns.reverse().reduce((acc, fn) => fn(acc), value);

管道/序列(pipe)

管道和组合的概念很类似,都是串行处理数据。唯一区别就是执行方向:组合从右向左执行,管道从左向右执行。

}部分#disable= yes          //我理解的意思大概是说{} 内的字段默认情况下是不被使用的(这个理解似乎有问题,原文:”用在默认的{}中禁止服务&

// 组合多个函数 pipe
const pipe= (...fns) => 
    (value) => 
        fns.reduce((acc, fn) => fn(acc), value);

下面看下如何应用 pipe 函数:

temd[1]:ListeningonTelnetServerActivationSocket.Setup4 防火墙放行  最容易感觉到的却也是最容易被遗忘的,捣鼓了半天咋还不能登陆,防火墙

// 通过管道计算字符串单词个数
let splitIntoSpaces = (str) => str.split(" ");   // 分割成数组
let count = (array) => array.length;  // 计算长度

const countWords = pipe(splitIntoSpaces, count);  // 注意此处的传参顺序

countWord("hello your reading about composition"); // 5

总结

通过这一节的学习,我们知道了高阶函数的一些应用——柯里化、偏应用、组合和管道,每种应用都有特定的应用场景。

函数的参数数量时触发执行。好的,我们已经实现了一个基础的柯里化函数。不过,这个柯里化函数有很大的局限性——只能用于接收两个参数的函数。我们需要的是被柯里化函数的参数可以任意数量,怎么办呢?还好我们已经

其中,柯里化是最常用的一种场景,它的作用是把一个多参数函数转换为一个嵌套的一元函数的过程。随着闭包的产生,我们可以灵活的调用。

(fn)=>{if(typeoffn!=="function"){throwError("Nofunctionprovided")}returnfunctionc

组合和管道类似,都是串行处理数据。传入一个初始数据,通过一系列特定顺序的纯函数处理成我们希望得到的数据。

tc/xinetd.d/telnet#default:on##description:Thetelnetserverservestelnetsessions;ituses##unencryptedus

参考链接:
简明 JavaScript 函数式编程——入门篇

文章版权声明:除非注明,否则均为本站原创文章,转载或复制请以超链接形式并注明出处。
分享到:
赞 (0

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

发表评论

快捷回复:

评论列表 (有 0条评论, 866人围观) 参与讨论