Java中使用Map and Fold进行功能性编程

在函数式编程中,Map和Fold是两个非常有用的运算符,它们属于每种函数式语言。 如果Map和Fold运算符是如此强大且必不可少,那么您如何解释说即使Java编程语言缺少这两个运算符,我们也可以使用Java来完成工作? 事实是,使用Java进行编码时,您已经进行了Map和Fold,只是每次都使用循环手动进行操作。

免责声明:我不是函数式编程的参考,本文不过是一个简短的介绍; FP爱好者可能不太喜欢它。

您已经熟悉了

想象一下不含增值税的数量的List <Double>。 我们希望将此列表转换为包含增值税的另一个相应列表。 首先,我们定义一种将增值税加到一个单一金额中的方法:

public double addVAT(double amount, double rate) {return amount * (1 + rate);}

现在让我们将此方法应用于列表中的每个金额:

public List<Double> addVAT(List<Double> amounts, double rate){final List<Double> amountsWithVAT = new ArrayList<Double>();for(double amount : amounts){amountsWithVAT.add(addVAT(amount, rate));}return amountsWithVAT;
}

在这里,我们创建了另一个输出列表,对于输入列表的每个元素,我们对其应用了addVAT()方法,然后将结果存储到大小完全相同的输出列表中。 恭喜,正如我们刚刚手工完成的,在方法addVAT()的输入列表上有一个Map。 让我们再做一次。

现在我们要使用货币汇率将每个金额转换为另一种货币,因此我们需要一种新的方法:

公共double convertCurrency(double

public double convertCurrency(double amount, double currencyRate){return amount / currencyRate;}

现在,我们可以将此方法应用于列表中的每个元素:

public List<Double> convertCurrency(List<Double> amounts, double currencyRate){final List<Double> amountsInCurrency = new ArrayList<Double>();for(double amount : amounts){amountsInCurrency.add(convertCurrency(amount, currencyRate));}return amountsInCurrency;
}

注意,除了在步骤2处调用的方法外,两种接受列表的方法是如何相似的:

  1. 创建一个输出列表,
  2. 为输入列表中的每个元素调用给定方法 ,并将结果存储到输出列表中
  3. 返回输出列表。

您经常在Java中执行此操作,而这恰恰是Map运算符:将给定方法someMethod (T):T应用于list <T>的每个元素,这将为您提供另一个相同大小的list <T>。

功能语言认识到这种特殊需求(在集合的每个元素上应用一种方法)非常普遍,因此他们将其直接封装到内置的Map运算符中。 这样,给定addVAT(double,double)方法,我们可以使用Map运算符直接编写如下代码:

List amountsWithVAT = map (addVAT, amounts, rate)

是的,第一个参数是一个函数,因为函数是函数语言中的一等公民,因此可以将它们作为参数传递。 与for循环相比,使用Map运算符更简洁,更不易出错,其意图也更加明确,但是我们在Java中没有它……

因此,这些示例的要点是,您已经不熟悉甚至不知道函数式编程的关键概念:Map运算符。

现在是Fold运算符

回到金额列表,现在我们需要将总金额计算为每个金额的总和。 超级容易,让我们循环执行一下:

public double totalAmount(List<Double> amounts){double sum = 0;for(double amount : amounts){sum += amount;}return sum;
}

基本上,我们只是在列表上进行了折叠,使用函数“ + =”将每个元素折叠成一个元素,这里是一个递增的数字,一次折叠一个。 这类似于Map运算符,除了结果不是列表而是单个元素(标量)。

这又是您通常用Java编写的那种代码,现在您已经用功能语言为其命名:“ Fold ”或“ Reduce”。 Fold运算符通常在函数式语言中是递归的,因此我们在此不再对其进行描述。 但是,我们可以使用某种可变状态在迭代之间累加结果,从而以迭代形式实现相同的目的。 在这种方法中,Fold采用一种内部可变状态的方法,该方法期望一个元素,例如someMethod(T),并将其反复应用于输入列表<T>中的每个元素,直到我们得到一个单个元素T,即折叠操作的结果。

与Fold一起使用的典型函数是求和,逻辑与和或,List.add()或List.addAll(),StringBuilder.append(),max或min等。Fold的思维方式类似于SQL中的聚合函数 。

形状思考

直观地思考(带有草率的图片),Map接收一个大小为n的列表,并返回另一个大小相同的列表:

另一方面,Fold获取大小为n的列表,并返回单个元素(标量):

您可能还记得我以前关于谓词的文章 ,这些文章通常用于将集合过滤为元素较少的集合。 实际上,此过滤器运算符是在大多数功能语言中补充Map和Fold的第三种标准运算符。

Eclipse模板

由于Map和Fold很常见,因此有必要为它们创建Eclipse模板,例如Map:

在Java中更接近地图和折叠

Map和Fold是期望函数作为参数的构造,而在Java中,传递方法的唯一方法是将其包装到接口中。

在Apache Commons Collections中,有两个接口对于我们的需求特别有趣: Transformer (具有一个方法transform(T):T )和Closure (具有一个方法execute(T):void) 。 类CollectionUtils提供了collect(Iterator,Transformer)方法(它基本上是Java集合的穷人Map运算符)以及forAllDo()方法,该方法可以使用闭包来模拟Fold运算符。

使用Google Guava, Iterables类提供了静态方法transform(Iterable,Function) ,该方法基本上是Map运算符。

List<Double> exVat = Arrays.asList(new Double[] { 99., 127., 35. });Iterable<Double> incVat = Iterables.transform(exVat, new Function<Double, Double>() {public Double apply(Double exVat) {return exVat * (1.196);}});System.out.println(incVat); //print [118.404, 151.892, 41.86]

类似的变换()方法,也可在类解释为解释和地图为地图。

要在Java中模拟Fold运算符,可以使用Closure接口,例如Apache Commons Collection中的Closure接口,仅使用一个带有一个参数的单一方法,因此您必须在内部保留当前的-mutable-状态,就像'+ =确实。

不幸的是,尽管Guava中没有Fold,尽管它经常被要求提供 ,甚至没有类似闭包的函数,但是创建自己的函数并不难,例如,您可以使用以下方法实现上述总计:

// the closure interface with same input/output type
public interface Closure<T> {T execute(T value);
}// an example of a concrete closure
public class SummingClosure implements Closure<Double> {private double sum = 0;public Double execute(Double amount) {sum += amount; // apply '+=' operatorreturn sum; // return current accumulated value}
}// the poor man Fold operator
public final static <T> T foreach(Iterable<T> list, Closure<T> closure) {T result = null;for (T t : list) {result = closure.execute(t);}return result;
}@Test // example of use
public void testFold() throws Exception {SummingClosure closure = new SummingClosure();List<Double> exVat = Arrays.asList(new Double[] { 99., 127., 35. });Double result = foreach(exVat, closure);System.out.println(result); // print 261.0
}

不仅用于收藏:可折叠在树木和其他建筑物上

Map and Fold的功能不仅限于简单的集合,还可以扩展到任何可导航的结构,尤其是树和图。

想象一棵使用节点类及其子节点的树。 将深度优先搜索和广度优先搜索(DFS和BFS)编码为两个接受Closure作为单个参数的通用方法,可能是一个好主意:

public class Node ...{...public void dfs(Closure closure){...}public void bfs(Closure closure){...}
}

我过去经常使用这种技术,我可以说它可以大大减少类的大小,仅使用一种通用方法,而不是许多看起来相似的方法(每个方法都会重做自己的树遍历)。 更重要的是,可以使用模拟闭包对遍历本身进行单元测试。 每个封盖也可以独立进行单元测试,所有这些都使您的生活变得更加简单。

访客模式可以实现非常相似的想法,您可能已经很熟悉。 我在我的代码和其他几个团队的代码中多次看到,Visitor非常适合在遍历数据结构期间累积状态。 在这种情况下,Visitor只是闭合中传递给折叠的一种特殊情况。

Map-Reduce上的一个字

您可能听说过Map-Reduce模式,是的,其中的“ Map”和“ Reduce”一词指的是我们刚刚看到的相同的函数运算符Map和Fold(也称为Reduce)。 即使实际应用更加复杂,也很容易注意到Map 令人尴尬地是并行的,这对并行计算有很大帮助。

参考: 与我们的JCG合作伙伴一起 在您的日常Java中使用Map和Fold进行函数式编程的思考   Cyrille Martraire 博客上的Cyrille Martraire 。


翻译自: https://www.javacodegeeks.com/2012/03/functional-programming-with-map-and.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/373566.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 文件压缩解压缩

文章来自&#xff1a;http://www.xuexiyuan.cn/article/detail/53.html *.tar格式 解包1&#xff1a;$ tar -xvf FileName.tar解包2&#xff1a;$ tar -xvf FileName.tar -C DirName# tar解压缩到指定目录打包&#xff1a;$ tar -cvf FileName.tar DirName# tar是打包&#x…

Mysql 分页语句Limit用法

Mysql 分页语句Limit用法 1、Mysql的limit用法 在我们使用查询语句的时候&#xff0c;经常要返回前几条或者中间某几行数据&#xff0c;这个时候怎么办呢&#xff1f;不用担心&#xff0c;mysql已经为我们提供了这样一个功能。 Sql代码 SELECT * FROM table LIMIT [offset,] r…

sqlmap指定cookie_利用SQLMap进行cookie注入

SQLMap被称为注入神器&#xff0c;N多大神都使用SQLmap来进行注入测试&#xff0c;我等小菜当然也会用来装一下A*C&#xff0c;用了N久SQLMAP了&#xff0c;但是极少用 到cookie注入&#xff0c;一遇到cookie注入就去使用注入中转工具&#xff0c;比较麻烦。刚好今天群里的USB问…

c语言else匹配问题

1 #include <stdio.h>2 #include <stdlib.h>3 4 //实现 依次输入三个递增的数 然后正确输出5 6 //为什么得不到我们想要的结果呢 这就是else匹配的问题 当然了 在编译器里面他会自动给你匹配7 //但是如果没有了编译器 笔试的时候呢。。。。8 //原因为&#xff1a;e…

Java:伪造工厂的闭包以创建域对象

最近&#xff0c;我们想要创建一个域对象&#xff0c;该对象需要具有外部依赖关系才能进行计算&#xff0c;并且希望能够在测试中解决该依赖关系。 最初&#xff0c;我们只是在领域类中新建依赖项&#xff0c;但这使得无法在测试中控制其值。 同样&#xff0c;我们似乎不应该将…

利用scp 远程上传下载文件/文件夹

利用scp传输文件 1、从服务器下载文件 scp usernameservername:/path/filename /tmp/local_destination 例如scp codinglog192.168.0.101:/home/kimi/test.txt 把192.168.0.101上的/home/kimi/test.txt 的文件下载到 /tmp/local_destination 2、上传本地文件到服务器 scp /…

KEIL编译错误总结:

1 expected an identifier&#xff1a;#define宏定义常量后&#xff0c;如果再用前面定义的常量作为枚举常量就会报错&#xff0c;方法&#xff0c;去掉#define宏定义 2 ERROR L118: REFERENCE MADE TO ERRONEOUS EXTERNAL 定义的变量和外部声明调用的变量存储类型不一致&#…

视觉平衡与物理平衡_设计中的平衡理论为什么这么重要?

原标题&#xff1a;设计中的平衡理论为什么这么重要&#xff1f;在平面设计中很重要的理论就是关于平衡的应用。无论在logo设计还是网页设计还是海报设计中&#xff0c;一个好的设计一定会兼顾视觉的平衡。今天123标志网就跟大家一起看看平衡的力量。构图平衡主要意味着调整设计…

Tomcat、JDK 历史版本下载地址

Tomcat 历史版本下载地址http://archive.apache.org/dist/tomcat/ JDK 历史版本下载地址 https://www.oracle.com/technetwork/java/javase/archive-139210.html 个人博客&#xff1a;学习园 原文地址&#xff1a;http://www.xuexiyuan.cn/article/detail/190.html

JavaFX移动应用程序最佳实践,第2部分

警告&#xff1a;我在这里给出的技巧对于JavaFX Mobile的当前版本是正确的&#xff0c;该版本是JavaFX 1.1 SDK的一部分。 在将来的版本中&#xff0c;行为将改变&#xff0c;上述工件的当前不良性能将被优化或至少得到显着改善。 我在这里写的所有内容都是快照&#xff0c;不应…

14软件工程第一次作业

你认为一些军事方面的软件系统采用什么样的开发模型比较合适&#xff1f; 我认为设计军事方面的软件采用螺旋式的开发模型比较好。因为螺旋模型减少了过多测试或者是测试不足所带来的风险&#xff0c;能够使软件在无法排除重大风险时有机会停止&#xff0c;减少损失。对于军事方…

波纹扩散_C4D_动画amp;RS波纹扩散效果J_014

C4D-效果扩散效果&#xff0c;Redshift混合冰材质&#xff1b;利用顶点贴图扩散效果制作&#xff0c;RS混合调用顶点贴图。视频教程时长22分钟。对象为可编辑对象才能用顶点贴图。冰结域的扩展是这个动画的重点&#xff0c;在这个模式下&#xff0c;权重会根据半径向外扩展&…

软件测试工程师简历项目经验怎么写?--9999个已成功入职的软件测试工程师真实简历

简历是我们求职的第一步&#xff0c;也是非常重要的一步。 青云叔叔看过太多简历&#xff0c;最快3秒就淘汰一份简历&#xff0c;因为其实我们每天要收到很多简历进行筛选&#xff0c;那么面试官其实也是会很快进行对简历进行判断的&#xff0c;如果你对简历写的一塌糊涂&…

【poj2464】树状数组

这道题。。太特么多细节了。。 题意&#xff1a;在平面直角坐标系中给你N个点&#xff0c;stan和ollie玩一个游戏&#xff0c;首先stan在竖直方向上画一条直线&#xff0c;该直线必须要过其中的某个点&#xff0c;然后ollie在水平方向上画一条直线&#xff0c;该直线的要求是要…

mkdir -p命令

如果要创建目录A并创建目录A的子目录B&#xff0c;没有用-p的情况下mkdir 逐个的创建目录(mkdir A && mkdir A/B); 如果用-p 可以直接创建2个目录 mkdir -p A/B(如果父目录A不存在就创建); 来自个人博客&#xff1a; http://www.xuexiyuan.cn/article/detail/182.html

Eclipse在过去十年中的主要成就

正如我所写的那样 &#xff0c;Eclipse在11月庆祝了10年来的开源和社区。 Eclipse社区已经形成了许多里程碑 &#xff0c;但是主要成就是什么&#xff1f; Eclipse为实际改变软件行业做了什么&#xff1f; 这是Eclipse的一些关键成就。 1.主导的Java IDE。 Eclipse最初是一个非…

azure git怎么使用_Azure(一)Azure Traffic Manager为我们的Web项目提供负载均衡

一&#xff0c;引言上一篇讲到我们将自己的Net Core Web 项目部署到 Azure 的 Web App 的一项 pass 服务&#xff0c;假如随着项目的日益增长的访问量&#xff0c;之前部署到单节点的应用可能无法保证其稳定性&#xff0c;可能会导致系统宕机等等问题&#xff0c;这个时候&…

hiho1257 Snake Carpet

题目链接&#xff1a;http://hihocoder.com/problemset/problem/1257 题目大意&#xff1a;有n条蛇 编号为1-n 每条蛇的长度跟编号相等 奇数编号的蛇必须拐奇数次&#xff08;除了第一条&#xff09;偶数编号的蛇必须拐偶数次&#xff08;除了第二条&#xff09;问能不能在这种…

POJ 3680_Intervals

题意&#xff1a; 给定区间和该区间对应的权值&#xff0c;挑选一些区间&#xff0c;求使得每个数都不被K个区间覆盖的最大权值和。 分析&#xff1a; 如果K1&#xff0c;即为区间图的最大权独立集问题。可以对区间所有端点排序后利用动态规划的方法&#xff0c;设dp[i]为只考虑…

MongoDB 数据类型查询——$type使用

在MongoDB中根据字段的数量类型来查询数据使用$type操作符来实现&#xff0c;具体使用法语&#xff1a;1db.集合名.find({$type:类型值}) //这里的类型值能使用Number也能使用alias举个例子&#xff1a;12db.person.find({address:{$type:2}}) //查询address字段数据…