前言
当垃圾回收成为主流时,它消除了所有类别的难以调试的问题,使运行时能够为开发人员管理复杂的、容易出错的进程。函数式编程旨在为您编写的算法实现同样的优化,这样您就可以从一个更高的抽象层面开展工作,同时运行时执行复杂的优化。
Java 下一代语言并不都占用从命令式到函数式的语言频谱的同一位置,但都展现出函数功能和习语。函数式编程技术有明确定义,但语言有时为相同的函数式概念使用不同的术语,使得我们很难看到相似之处。在本期文章中,我比较了 Scala、Groovy 和 Clojure 的函数式编码风格并讨论了它们的优势。
命令式处理
我要首先探讨一个常见问题及其命令式解决方案。假如给定一个名称列表,其中一些名称包含一个字符。系统会要求您在一个逗号分隔的字符串中返回名称,该字符串中不包含单字母的名称,每个名称的首字母都大写。实现该算法的 Java 代码如清单 1 所示。
清单 1. 命令式处理
public class TheCompanyProcess {
public String cleanNames(List<String> listOfNames) {
StringBuilder result = new StringBuilder();
for(int i = 0; i < listOfNames.size(); i++) {
if (listOfNames.get(i).length() > 1) {
result.append(capitalizeString(listOfNames.get(i))).append(",");
}
}
return result.substring(0, result.length() - 1).toString();
}
public String capitalizeString(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1, s.length());
}
}
由于您必须处理整个列表,解决清单 1 中问题最简单的方式是使用一个命令式循环。对于每个名称,都需要进行检查,确认其长度是否大于 1,然后(如果长度大于 1)将首字母大写的名称附加到 result 字符串,并在后面加逗号。最终字符串中的最后一个名称不应包含逗号,所以我将它从最后返回值中移走。
在命令式编程中,建议您在较低级上别执行操作。在 清单 1 中的 cleanNames() 方法中,我执行了三个任务:我筛选 列表以消除单字符,将列表中每个名称的首字母变换 为大写,然后将列表转化 为一个字符串。在命令式语言中,我不得不为三个任务都使用同一低级机制(对列表进行迭代)。函数式语言将筛选、变换和转化视为常见操作,因此它们提供给您从不同视角解决问题的方式。
函数式处理
函数编程语言与命令式语言的问题分类方式不同。筛选、变换和转化逻辑类别表现为函数。那些函数实现低级变换并依赖于开发人员来编写作为参数传递的函数,进而定制函数的行为。我可以用伪代码将 清单 1 中的问题概念化为:
listOfEmps -> filter(x.length > 1) -> transform(x.capitalize) ->
convert(x, y -> x + "," + y)
利用函数式语言,您可以建模这一概念性解决方案,无需担心实现细节。
Scala 实现
清单 2 使用 Scala 实现 清单 1 中的处理示例。它看起来就像是前面的伪代码,包含必要的实现细节。
清单 2. Scala 处理
val employees = List("neal", "s", "stu", "j", "rich", "bob")
val result = employees
.filter(_.length() > 1)
.map(_.capitalize)
.reduce(_ + "," + _)
对于给定的名称列表,我首先筛选它,剔除长度不大于 1 的所有名称。然后将该操作的输出提供给 map() 函数,该函数对集合的每个元素执行所提供的代码块,返回变换后的集合。最后,来自 map() 的输出集合流向 reduce() 函数,该函数基于代码块中提供的规则将每个元素结合起来。
在本例中,我将每对元素结合起来,用插入的逗号连接它们。我不必考虑三个函数调用中参数的名称是什么,所以我可以使用方便的 Scala 快捷方式,也就是说,使用 _ 跳过名称。reduce() 函数从前两个元素入手,将它们结合成一个元素,成为下一个串接中的第一个元素。在 “浏览” 列表的同时,reduce() 构建了所需的逗号分隔的字符串。
我首先展示 Scala 实现是因为我对它的语法比较熟悉,而且 Scala 分别为筛选、变换和转化概念使用了行业通用的名称,即 filter、map 和 reduce。
Groovy 实现
Groovy 拥有相同的功能,但对它们进行命名的方式与脚本语言(比如 Ruby)更加一致。清单 1 中处理示例的 Groovy 版本如清单 3 所示。
清单 3. Groovy 处理
class TheCompanyProcess {
public static String cleanUpNames(Lis疒7vj*c6WjA$nR4(6Tj>ck/Rǒ
">r^>GRj*
Bj>GB;w6T&M3r4(6T{:34(4(驅4)I啕((4(х锤4(||4(6T6T^j"rZW*"FZWB;N7w6jnB#j3rRǒ"GB#jN7kcb&^>Rǖr3"Cnkj4(vBGFjGFcfG7Rjǖv_/7Rz7vjkR*SJ0k>V>Cv{:k"rV6z"_J3b'nB#bj:v>j&N7:4():crha50Bz?zjr'rja50zkjZWr
j0~;bjV6zzcrjj>c6J3o'N7R4(屔v4(4(&r':B"[*[/z[/"CrvG>"[" ):jr'^B3jB7J33rrrGMJ0
j7Z;4(brZj3ro疒j惚r'&*rokkR2 |