首页 > JS函数式编程【译】5.2 函子 (Functors)

JS函数式编程【译】5.2 函子 (Functors)

函子(Functors)

态射是类型之间的映射;函子是范畴之间的映射。可以认为函子是这样一个函数,它从一个容器中取出值, 并将其加工,然后放到一个新的容器中。这个函数的第一个输入的参数是类型的态射,第二个输入的参数是容器。

函子的函数签名是这个样子

// myFunctor :: (a -> b) -> f a -> f b

意思是“给我一个传入a返回b的函数和一个包含a(一个或多个)的容器,我会返回一个包含b(一个或多个)的容器”

创建函子

要知道我们已经有了一个函子:map(),它攫取包含一些值的容器(数组),然后把一个函数作用于它。

[1, 4, 9].map(Math.sqrt); // Returns: [1, 2, 3]

然而我们要把它写成一个全局函数,而不是数组对象的方法。这样我们后面就可以写出简洁、安全的代码。

// map :: (a -> b) -> [a] -> [b]
var map = function(f, a) { return arr(a).map(func(f));
}

这个例子看起来像是个故意弄的封装,因为我们只是把map()函数换了个形式。但这有它的目的。 它为映射其它类型提供了一个模板。

// strmap :: (str -> str) -> str -> str
var strmap = function(f, s) { return str(s).split('').map(func(f)).join('');
}

数组和函子

数组是函数式JavaScript使用数据的最好的方式。

是否有一种简单的方法来创建已经分配了态射的函子?有,它叫做arrayOf。 当你传入一个以整数为参数、返回数组的态射时,你会得到一个以整数数组为参数返回数组的数组的态射。

它自己本身不是函子,但是它让我们能够用态射建立函子。

// arrayOf :: (a -> b) -> ([a] -> [b])
var arrayOf = function(f) { return function(a) { return map(func(f), arr(a));}
}

下面是如何用态射创建函子

var plusplusall = arrayOf(plusplus); // plusplus是函子
console.log( plusplusall([1,2,3]) ); // 返回[2,3,4]
console.log( plusplusall([1,'2',3]) ); // 抛出错误

函数组合,重访(revisited)

函数也是一种我们能够用函子来创建的原始类型,这个函子叫做“fcompose”。我们对函子是这样定义的: 它从容器中取一个值,并对其应用一个函数。如果这个容器是一个函数,我们只需要调用它并获取里面的值。

我们已经知道了什么是函数组合,不过让我们来看看在范畴论驱动的环境里它们能做些什么。

函数组合就是结合(associative,中学数学中学到的“结合律”中的“结合”)。如果你的高中代数老师也像我这样的话那她只告诉了你函数组合的定律有什么,而没有没教你用它能做些什么。在实践中,组合就是结合律所能够做的。

(a × b) × c = a × (b × c)

(f g) h = f (g h)


f g ≠ g f

我们可以任意进行内部组合,无所谓怎样分组。交换律也没有什么可迷惑的。f g 不总等于 g f。比如说,一个句子的第一个单词被反转并不等同于一个被反转的句子的第一个单词。

总的来说意思就是哪个函数以什么样的顺序被执行是无所谓的,只要每个函数的输入来源于上一个函数的输出。不过,等等,如果右边的函数依赖于左边的函数,不就是只有一个固定的求值顺序吗?从左到右?是的,如果把它封装起来,我们就可以按照我们感觉合适的方式来控制它。这就使得在JavaScript中可以实现惰性求值。

(a × b) × c = a × (b × c)

(f g) h = f (g h)

我们来重写函数组合,不作为函数原型的扩展,而是作为一个单独的函数,这样我们就可以的到更多的功能。基本的形式是这样的:

var fcompose = function(f, g) { return function() { return f.call(this, g.apply(this, arguments));};
};

不过我们还得让它能接受任意数量的输入。

var fcompose = function() { // 首先确保所有的参数都是函数var funcs = arrayOf(func)(arguments);  //译注:这句有问题,见下面注释// 返回一个作用于所有函数的函数return function() { var argsOfFuncs = arguments;for (var i = funcs.length; i > 0; i -= 1) { argsOfFuncs  = [funcs[i].apply(this, args)];}return args[0];};
};// 例:
var f = fcompose(negate, square, mult2, add1);
f(2); // 返回: -36

给原著勘误:如果你copy上面的代码执行的话现在肯定看到报错了,上面这段代码里的错误还真不少……

首先会得到一个错误:“Uncaught TypeError: Error: Array expected, something else given.”。 哪个数组没通过类型验证呢?是fcompose里的arguments。我在最新版本的chrome和火狐里得到arguments的字符串是[object Arguments], 而且arguments并没有继承Array,也就没有map之类的方法,所以这里需要先把arguments转换成数组,把fcompose函数体第一句改成这样就行:

var funcs = arrayOf(func)(Array.prototype.slice.call(arguments));

然后第二个错误,低级错误,argsOfFuncs和args是一个东西,统一成一个变量名就行了。比如说把argsOfFuncs都改成args吧。 顺便说一下这里的意思,首先把初始参数赋给args,然后遍历组合函数的数组,每执行一个函数就把返回值赋给args, 这样下一个函数就能把上一个函数的执行结果作为输入参数了。注意每次的返回值都放到了数组里,是为了符合apply的参数形式, 而最后返回时只要取args里的第一个(也是唯一一个)值就行了。

第三个错误,还是低级错误,遍历funcs的时候计数写成了length到1,而实际上我们需要length-1到0。 顺便说下为什么计数要从大到小呢?因为组合的函数要从右往左执行。

最后,上正确的代码:

var fcompose = function() { var funcs = arrayOf(func)(Array.prototype.slice.call(arguments));return function() { var args = arguments;for (var i = funcs.length-1; i >= 0; i -= 1) { args  = [funcs[i].apply(this, args)];}return args[0];};
};

现在我们封装好了这些函数并可以控制它们了。我们重写了组合函数使得每一个函数接受另一个函数作为输入, 存储起来,并同样返回一个对象。这里并不是接受一个数组作为输入处理它,而是对每一个操作返回一个新的数组, 我们可以在源头上让每一个元素接受一个数组,把所有操作合到一起执行(所有map、filter等等组合到一起), 最终把结果存到一个新数组里。这就是通过函数组合实现的惰性求值。这里我们没有理由重新造轮子, 许多库对于这个概念都有很好的实现,包括Lazy.js、Bacon.js以及wu.js等库。

利用这一不同模式的结果,我们可以做更多事情:异步迭代、异步事件处理、惰性求值甚至自动并行。

自动并行?在计算机科学界有一个词叫做:IMPOSSIBLE。但是这真的不可能吗? 摩尔定律的下一个飞跃没准是一个能够将我们的代码并行化的编译器,函数组合能做到吗? 不,这行不通。JavaScript引擎实现并行化并不是自动的,而是依靠精心设计的代码。 函数组合只是提供了切分成并行进程的机会。但是它本身已经足够酷了。
下一节 单子(Monads)
? Functional Programming in Javascript 主目录第五章 范畴论

转载于:https://www.cnblogs.com/tolg/p/5258029.html

更多相关:

  • 草色新雨中, 松声晚窗里。之前我们学习 Power Query 都是用鼠标就完成了很多复杂的操作。虽然 PowerQuery 已经将大部分常用功能内置成到功能区。基本能完成我们大部分的报表自动化功能。但是总有些复杂的或者个性化的问题是开发团队没有预先想到的,这时我们就需要学习 M 语言。一、M 语言在哪里?M语言的函数公式有三个地...

  • 前言从2020年3月份开始,计划写一系列文档--《小白从零开始学编程》,记录自己从0开始学习的一些东西。第一个系列:python,计划从安装、环境搭建、基本语法、到利用Django和Flask两个当前最热的web框架完成一个小的项目第二个系列:可能会选择Go语言,也可能会选择Vue.js。具体情况待定,拭目以待吧。。。基本概念表达式表...

  • 1.1函数1.1.1什么是函数函数就是程序实现模块化的基本单元,一般实现某一功能的集合。函数名:就相当于是程序代码集合的名称参数:就是函数运算时需要参与运算的值被称作为参数函数体:程序的某个功能,进行一系列的逻辑运算return 返回值:函数的返回值能表示函数的运行结果或运行状态。1.1.2函数的作用函数是组织好的,可重复使用的,用来...

  • 原标题:基于Python建立深度神经网络!你学会了嘛?图1 神经网络构造的例子(符号说明:上标[l]表示与第l层;上标(i)表示第i个例子;下标i表示矢量第i项)单层神经网络图2 单层神经网络示例神经元模型是先计算一个线性函数(z=Wx+b),接着再计算一个激活函数。一般来说,神经元模型的输出值是a=g(Wx+b),其中g是激活函数(...

  • 在学习MySQL的时候你会发现,它有非常多的函数,在学习的时候没有侧重。小编刚开始学习的时候也会有这个感觉。不过,经过一段时间的学习之后,小编发现尽管函数有很多,但是常用的却只有那几个。今天小编就把常用的函数汇总一下,为大家能够能好的学习MySQL中的函数。MySQL常使用的函数大概有四类。时间函数、数学函数、字符函数、控制函数。让我...

  • 原文出处: 韩昊    1 2 3 4 5 6 7 8 9 10 作 者:韩 昊 知 乎:Heinrich 微 博:@花生油工人 知乎专栏:与时间无关的故事   谨以此文献给大连海事大学的吴楠老师,柳晓鸣老师,王新年老师以及张晶泊老师。   转载的同学请保留上面这句话,谢谢。如果还能保留文章来源就更感激不尽了。 我保证这篇文章...

  • 原文出处: 韩昊   我保证这篇文章和你以前看过的所有文章都不同,这是 2012 年还在果壳的时候写的,但是当时没有来得及写完就出国了……于是拖了两年,嗯,我是拖延症患者…… 这篇文章的核心思想就是: 要让读者在不看任何数学公式的情况下理解傅里叶分析。 傅里叶分析不仅仅是一个数学工具,更是一种可以彻底颠覆一个人以前世界观的思维...

  • 很多Linux高手都喜欢使用screen命令,screen命令可以使你轻松地使用一个终端控制其他终端。尽管screen本身是一个非常有用的工具,byobu作为screen的增强版本,比screen更加好用而且美观,并且提供有用的信息和快捷的热键。 想象一下这样一个场景:你通过Secure Shell(ssh)链接到一个服务器,并...

  • NarrowbandPrimary Synchronization Signal时域位置每1个SFN存在一个NPSSSFNSubframeSymbol长度每个SFN5最后11个symbol11个symbols频域位置NB-IOT下行带宽固定180kHz,一个PRB,12个子载波。...

  •  [h1]反斜杠只能够阻止一个字符  [h2]位于键盘的左上角,和~公用一个键。...

  • 英语的重要性,毋庸置疑!尤其对广大职场人士,掌握英语意味着就多了一项竞争的技能。那,对于我们成人来说,时间是最宝贵的。如何短时间内在英语方面有所突破,这是我们最关心的事情。英语学习,到底有没有捷径可以走,是否可以速成?周老师在这里明确告诉大家,英语学习,没有绝对的捷径走,但是可以少走弯路。十多年的教学经验告诉我们,成功的学习方法可以借...

  • 展开全部 其实IDLE提供了一个显32313133353236313431303231363533e78988e69d8331333365663438示所有行和所有字符的功能。 我们打开IDLE shell或者IDLE编辑器,可以看到左下角有个Ln和Col,事实上,Ln是当前光标所在行,Col是当前光标所在列。 我们如果想得到文件代码...

  • 前言[1]从 Main 方法说起[2]走进 Tomcat 内部[3]总结[4]《Java 2019 超神之路》《Dubbo 实现原理与源码解析 —— 精品合集》《Spring 实现原理与源码解析 —— 精品合集》《MyBatis 实现原理与源码解析 —— 精品合集》《Spring MVC 实现原理与源码解析 —— 精品合集》《Spri...

  • 【本文摘要】【注】本文所述内容为学习Yjango《学习观》相关视频之后的总结,观点归Yjango所有,本文仅作为学习之用。阅读本节,会让你对英语这类运动类知识的学习豁然开朗,你会知道英语学习方面,我们的症结所在。学习英语这类运动类知识,需要把握四个原则第一,不要用主动意识。第二,关注于端对端第三,输入输出符合实际情况第四,通过多个例子...

  • 点云PCL免费知识星球,点云论文速读。文章:RGB-D SLAM with Structural Regularities作者:Yanyan Li , Raza Yunus , Nikolas Brasch , Nassir Navab and Federico Tombari编译:点云PCL代码:https://github.co...