当前位置: 首页 > 编程日记 > 正文

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

相关文章:

[转]Introduction of iSCSI Target in Windows Server 2012

Introduction of iSCSI Target in Windows Server 2012 源地址:http://blogs.technet.com/b/filecab/archive/2012/05/21/introduction-of-iscsi-target-in-windows-server-2012.aspx The iSCSI Target made its debut as a free download for Windows 2008 R2 in A…

全国移动联通基站数据升级包(2013年1月基站升级包).rar

“全国移动联通基站数据升级包(2013年1月基站升级包).rar” 已经上传到CNBLOGS 地址:http://files.cnblogs.com/topwang-com/%E5%85%A8%E5%9B%BD%E7%A7%BB%E5%8A%A8%E8%81%94%E9%80%9A%E5%9F%BA%E7%AB%99%E6%95%B0%E6%8D%AE%E5%8D%87%E7%BA%A7%E5%8C%85(2013%E5%B9%…

php自动计算增长率,如何写sql计算增长率?

问题已有数据表(假定表名为t)time sale1999 4844904672000 651413668.92001 13713710082002 18177416252003 25053320952004 37654384862005 48177203842006 6083322598需要产生如下的数据表time sale …

我先了解一下博客园创建随笔/文章/日记的过程与三者的区别(隐私等级,是否审核等)...

我先了解一下博客园创建随笔/文章/日记的过程与三者的区别(隐私等级,是否审核等)转载于:https://www.cnblogs.com/Totooria-Hyperion/p/5260289.html

构建Java并发模型框架

2002 年 2 月 22 日 Java的多线程特性为构建高性能的应用提供了极大的方便,但是也带来了不少的麻烦。线程间同步、数据一致性等烦琐的问题需要细心的考虑,一不小心就会出现一些微妙的,难以调试的错误。另外,应用逻辑和线程逻辑纠缠…

Unity Note 1

1.把开始时间设定到播放完成的时间点,作为倒放的起点 animation["clip"].timeanimation["clip"].clip.length; animation["clip"].speed-1; animation.Play("clip"); 2.寻找场景中物体var door GameObject.Find(…

基于matlab的硅晶体模型,基于Matlab的图像处理技术识别硅太阳电池的缺陷

第 44 卷 第 7 期  2010 年 7 月 上 海 交 通 大 学 学 报 JOURNAL OF SHANGHAI J IAOTON G UNIVERSITY Vol. 44 No. 7   Jul. 2010   收稿日期 :20090908 作者简介 :柳效辉(19852) ,男 ,江西九江人 ,硕士生 ,主要从事光伏检测与光伏系统方面的研究. 徐  林(联系人) ,男 ,副…

spark- PySparkSQL之PySpark解析Json集合数据

PySparkSQL之PySpark解析Json集合数据 数据样本 12341234123412342|asefr-3423|[{"name":"spark","score":"65"},{"name":"airlow","score":"70"},{"name":"flume",&quo…

cmd库的导入Java,在cmd命令窗口导入第三方jar包来运行java文件

在cmd命令窗口导入第三方jar包来运行java文件,以下测试都是基于window环境,Linux环境没有测试。1、编译使用命令javac -cp或者javac -classpath本机测试:如下图所示,java文件路径为D:\workspace\demo,StringUtilsTest.java依赖了第…

JQuery 动态创建表单,并自动提交

前言:写这个是为了实现使用cookie进行自动登录的功能, 下面的代码是一个元素一个元素进行创建和赋值的, (可以尝试下将所有的html代码(form、input)全部拼好以后放到${ } 中,再进行提交。) submit的时候注意下写法&…

(转)利用ArcScene进行三维地形模拟

本文摘自:http://www.sunzx.net/archive/1109.html 在ArcGIS Desktop中,可用于三维场景展示的程序为ArcGlobe和ArcScene,由于两者的差别,在三维场景展示中适用的情况有所不同。ArcScene是一个适合于展示三维透视场景的平台&#x…

Android使用自定义View时:Error inflating class错误的原因。

当在布局文件里使用自定义的View的时候,出现Error inflating class错误的原因: 1、没有定义inflate需要的默认构造函数; eg:自定义View为TestView,需要定义TestView(Context context),TestView(Context context,AttributeSet set); 2、这是个…

oracle的表几种连接比较,几种表连接方式的使用场景

1)nested loopnested loop,指的是两个表连接时, 通过两层嵌套循环来进行依次的匹配, 最后得到返回结果集的表连接方法.select t1.owner,t1.object_name,t2.OBJECT_IDfrom test_tab1 t1,test_tab2 t2where t1.OBJECT_ID t2.OBJECT_IDand ROWNUM select *from test_t…

Ajax 完整教程 (转)

Ajax 完整教程第 1 页 Ajax 简介Ajax 由 HTML、JavaScript™ 技术、DHTML 和 DOM 组成,这一杰出的方法可以将笨拙的 Web 界面转化成交互性的 Ajax 应用程序。本文的作者是一位 Ajax 专家,他演示了这些技术如何协同工作 —— 从总体概述到细节的讨论 ——…

.Net中如何操作IIS(源代码)

http://www.daima.com.cn/Info/3/Info20453/转载于:https://www.cnblogs.com/luoyuan/archive/2005/09/17/238986.html

Enterprise Library Configuration DAAB的使用

1.要试用DAAB,首先要引用两个类库 第一个是Enterprise Library Shared Library 这个类库是所有Enterprist Library都必须引用的类库,它提供所需的结构类型. 第二个是Enterprist Library Data Access Application Block 这个就是daab的核心类库. 2试用DAAB的第一个步骤就是配置a…

安装oracle后在cmd,在WINDOWS上安装ORACLE RAC的注意事项

在WINDOWS上安装ORACLE RAC的注意事项1、检查防火墙和杀毒软件如果不关掉防火墙,在安装CRS时,在"Oracle Clusterware Configuration Assistant"界面会提示(1)OUI-25031错误(2)dddb1 service OracleCSService in improper PENDING state, err(9…

Tessellation (曲面细分) Displacement Mapping (贴图置换)

DirectX 11 Tessellation (曲面细分)—什么是 Tessellation (曲面细分) ?它为什么可以起到如此关键的数据?随着近期人们对 DirectX 11 的议论纷纷,你可能已经听说了有关 DirectX 11 最大新特性 Tessellation (曲面细分) 的大量介绍。作为一个概念。 Tessellation …

java 第12课

/*Java是面向对象的程序设计语言.面向对象的思想是将客观事物都作为实体,而对象通过实体抽象得到.所谓实体抽象,就是对实体的某些特征进行概括,使其数字化、符号化;比如:李四同学,就是一个实体,我们关心他的这些特征:姓名、性别、年龄、身高、体重等特征,就会有李四、男、21、1…

鸽巢原理(The Pigeonhole Principle)(抽屉原理)

简单形式&#xff1a;若n1个物体放进n个盒子&#xff0c;那么至少有一个盒子包含两个或更多的物体。 应用&#xff1a;给定m个整数A1,A2,...,Am,存在整数k和l&#xff0c; 0 < k < l < m,使得Ak1 Ak2 &#xff0b; ... Al能够被m整除。即在A1&#xff0c;A2&…

oracle10g删除asm组,Oracle 10G RAC 删除已有节点

如果现在在RAC集群中有三个节点c1、c2、c3&#xff1a;如果想要卸载c3节点。1、在c1或者c2上删除c3实例运行dbca然后选择Oracle Real Application Clusters database选择Instance Management选择Delete an instance选择实例&#xff0c;填写用户名密码&#xff0c;Next选择c3: …

嵌入式linux学习笔记1—内存管理MMU之虚拟地址到物理地址的转化

一.内存管理基本知识 1.S3C2440最多会用到两级页表&#xff1a;以段的方式进行转换时只用到一级页表&#xff0c;以页的方式进行转换时用到两级页表。页的大小有三种&#xff1a;大页&#xff08;64KB&#xff09;&#xff0c;小页&#xff08;4KB&#xff09;&#xff0c;极小…

C# 最快的逐一打印斐波那契结果数列的算法

用这种方法就无需将数列中的每一个元素都计算一遍了&#xff01; 说多无谓&#xff0c;直接上代码吧&#xff01; private void button5_Click(object sender, EventArgs e) { FiBoNaQi f new FiBoNaQi(); f.numberToCount (Int16)numericUpDown1.Value; f.DoFiB…

WSS 代码执行的权限提升

WSS 代码执行的权限提升 概述: WSS 默认使用身份模拟执行代码&#xff0c;也就是说用当前登录的用户身份执行Web Part或者自定义应用程序的代码访问。在大多数情况下&#xff0c;这种机制能够准确并严格地控制了标准权限的用户他对特定网站资源和敏感数据的访问&#xff0c;这也…

Oracle数据库联邦,使用联邦数据库将oracle表迁移到DB2(9.7)中的脚本说明

由于兄弟项目组要测试&#xff0c;需要将oracle中的表迁移到db2中&#xff0c;操作步骤如下&#xff1a;#1 在windows数据库中建联邦数据库服务器\用户映射connect to sampleCREATE WRAPPER DRDA LIBRARY db2drda.dll;--创建DB2包装器CREATE WRAPPER NET8 LIBRARY db2net8.dll;…

HDU 5047 Sawtooth 高精度

题意&#xff1a; 给出一个\(n(0 \leq n \leq 10^{12})\)&#xff0c;问\(n\)个\(M\)形的折线最多可以把平面分成几部分。 分析&#xff1a; 很容易猜出来这种公式一定的关于\(n\)的一个二次多项式。 不妨设\(f(n)an^2bnc\)。 结合样例我们可以列出\(3\)个方程&#xff1a;\(f(…

poj1129Channel Allocation

http://poj.org/problem?id1129 四色定理 最多有四色 从1到四搜 View Code 1 #include <iostream>2 #include<cstdio>3 #include<cstring>4 #include<stdlib.h>5 using namespace std;6 int n,w[100][100],co[100],mi,flag;7 void dfs(int x,int v)…

WCF 第二章 契约

在原子和金钱世界中&#xff0c;契约是两个或多个组织以一个已知的价格提供商品和服务的合同。在比特和服务的世界中&#xff0c;契约有类似的功能:它是两个或多个组织之间确定消息交换和消息条款及条件的合同。 契约是由服务终结点发送或接收的消息的描述。每一个终结点都由AB…

织梦 新建 php arclist,织梦arclist按照自定义字段来调用相关文章

织梦arclist按照自定义字段来调用相关文章&#xff0c;这对于想要在首页调用某个自定义字段的文章的同学来讲&#xff0c;非常不错&#xff0c;接下来看教程打开 include aglibrclist.lib.php 找到&#xff1a;//时间限制(用于调用最近热门文章、热门评论之类)&#xff0c;这里…

提高php编程效率的小结

1.如果将类的方法定义为&#xff1a;static,它的执行效率将提升为近4倍 2.php中数组的元素调用&#xff0c;使用关联数组优于索引数组 3.使用each快于print. 4.尽量使用foreach()替代for(). 5.销毁那些不用的变量尤其是大数组&#xff0c;如&#xff1a;unset().以便释放内存 6…