函数式编程七: IO,task,Pointed,monad函子的学习

2023-10-20 14:20

本文主要是介绍函数式编程七: IO,task,Pointed,monad函子的学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

IO函子

  • IO函子中的_value是一个函数,这里把函数作为值来处理。
  • IO函子可以把不纯的动作存储到_ value中,延迟执行这个不纯的操作(惰性执行),包装当前的操作纯。
  • 把不纯的操作交给调用者来处理。
const fp = require("lodash/fp");
class IO {static of(x) {return new IO(function () {return x;});}constructor(fn) {this._value = fn;}map(fn) {//把当前的value和传入的fn组合成一个新的函数return new IO(fp.flowRight(fn, this._value));}
}

这个of方法接收的是一个数据,在of方法的里面再去返回一个io函子,调用io的构造函数传递一个函数,这个函数其实把传过来的值x包裹起来了,通过of可以看出,io函数最终还是想把这个值给返回,io函子的value保存的是一个函数,而这个函数返回的是一个值,把求值的过程做了延迟处理,想用这个值的时候再调 用io函子中的value这个函数。
io函子中的map方法,map方法还是接收一个fn函数,在map方法里通过调用io的构造函数来创建一个io的函子。

let r = IO.of(process).map((p) => p.execPath);
console.log(r);

在这里插入图片描述
结果返回的是一个io函子,io函子的value保存的是一个function,现在这个函数还没有被执行,可以直接执行r.value()。打印出当前执行node的路径。、
总结一下,io函子内部帮我们包装了一些函数,当我们在传递函数的时候有可能这个函数是一个不纯的函数,那我们不管这个函数是不是纯函数,io函子在执行的过程中,返回的结果始终是一个纯的操作。

Folktale异步执行

  • 异步任务的实现过于复杂,可以用folktale中的Task来演示。
  • folktale一个标准的函数式编程库
    • 和lodash,ramda不同的是,他没有提供很多功能函数。
    • 只提供了一些函数式处理的操作,例如:compose,curry等,一些函子Task\Either\MayBe等

先安装folktale这个库npm i folktale.

const { compose, curry } = require("folktale/core/lambda");
const { toUpper, first } = require("lodash/fp");
let f = compose(toUpper, first);
console.log(f(["one", "two"]));

在这里插入图片描述
Folktale这个库中的task函子来处理异步任务,用2.x版本的folktale来演示一个读取文件的例子。fs是读取文件

//Task处理异步任务
const fs = require("fs");
const { task } = require("folktale/concurrency/task");
function readFile(filename) {return task((resolver) => {fs.readFile(filename, "utf-8", (err, data) => {if (err) {resolver.reject(err);} else {resolver.resolve(data);}});});
}

调用一下上面的函数。

readFile("package-lock.json").run().listen({onRejected: (err) => {console.log(err);},onResolved: (value) => {console.log(value);},});

在这里插入图片描述
读取到了package-lock.json文件,如果想把lockfileVersion这个值解析出来,在onResolved我们拿到了这个value,如果直接在这里处理value的话会特别的麻烦,也不是函数式编程了,因为我们在readFile的时候,它返回的是一个task函子,而所有的函子都有一个map方法,所以在run之前可以调用一下这个函子的map方法,在这个方法里可以处理拿到的结果。
Package.json里其他就是一行一行的内容,可以先用换行对文本进行切割得到一个数组,然后再去寻找数组中带有lockfileVersion的这个数组。

const { split, find } = require("lodash/fp");readFile("package-lock.json").map(split("\n")).map(find((x) => x.includes("lockfileVersion"))).run().listen({onRejected: (err) => {console.log(err);},onResolved: (value) => {console.log(value);},});

在这里插入图片描述
Pointed函子,一点不陌生,因为我们一直在使用

  • Pointed函子是实现了 of静态方法的函子。
  • of方法是为了避免使用new来创建对象,更深层的含义是of方法用来把值放到了上下文 Context(把值放到容器中,使用map来处理值)。
class Container {static of(value) {return new Container(value);}// ......
}
Container.of(2).map((x) => x + 5);

接下去再学习最后一个函子,monad函子(单子),单细胞动物的意思
在使用IO函子的时候,如果之前写过如下代码

const fp = require("lodash/fp");
const fs = require("fs");
class IO {static of(value) {return new IO(function () {return value;});}constructor(fn) {this._value = fn;}map(fn) {return new IO(fp.flowRight(fn, this._value));}
}

我们先来看一个io函子的一个问题
在Linux5有一个cat命令,作用是读取文件的内容,并且把这个内容打印出来,写一个函数来模拟这个命令,先写一个读取文件的函数,再写一个打印的函数,组合成一个cat函数。

let readFile = function (filename) {return new IO(function () {return fs.readFileSync(filename, "utf-8");});
};
let print = function (x) {return new IO(function () {console.log(x);return x;});
};
let cat = fp.flowRight(print, readFile);
//IO(IO(x))
let r = cat("package-lock.json")._value()._value();
console.log(r);

最外面的IO是print返回的函子,最里面的io是readFile返回的函子。
在这里插入图片描述
这里面有一个问题,我们在嵌套函子的函数的时候非常的不方便,如果函子有嵌套的话,我们想要嵌套函子中的函数,需要._value()再.value(),虽然这样也可以去实现,但是这种api的风格看起来一般,可以改造一下。

  • Monad函子是可以变扁的PointedPointed函子,IO(IO(x)).
  • 一个函子如果具有join和 of两个方法并遵守一些定律就是一个 Monad.

把这个IO类改造成Monad函子,join()方法用来返回value结果,经常会把map和join联合起来使用。flatMap()的作用就是同时去调用map和join,上面的例子改造如下。

const fp = require("lodash/fp");
const fs = require("fs");
class IO {static of(value) {return new IO(function () {return value;});}constructor(fn) {this._value = fn;}map(fn) {return new IO(fp.flowRight(fn, this._value));}join() {return this._value();}flatMap(fn) {return this.map(fn).join();}
}let readFile = function (filename) {return new IO(function () {return fs.readFileSync(filename, "utf-8");});
};
let print = function (x) {return new IO(function () {console.log(x);return x;});
};
// let cat = fp.flowRight(print, readFile);
// //IO(IO(x))
// let r = cat("package-lock.json")._value()._value();
let r = readFile("package-lock.json").flatMap(print).join();
console.log(r);

再来复习一下monad函数,就是具有一个静态的io方法,并且具有一个join方法的函子,什么时候使用monad?当一个函数返回一个函子的时候,就可以使用monad来解决嵌套的问题,合并一个函数并且这个函数返回一个值,这时候可以调 用map方法,当我们想要合并一个函数,但是这个函数返回一个函子,这时可以用flatMap方法。

这篇关于函数式编程七: IO,task,Pointed,monad函子的学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/247676

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学