Java函数式编码结构-好程序员( 二 )


在本例中 , 我将每对元素结合起来 , 用插入的逗号连接它们 。 我不必考虑三个函数调用中参数的名称是什么 , 所以我可以使用方便的Scala快捷方式 , 也就是说 , 使用_跳过名称 。 reduce()函数从前两个元素入手 , 将它们结合成一个元素 , 成为下一个串接中的第一个元素 。 在“浏览”列表的同时 , reduce()构建了所需的逗号分隔的字符串 。
我首先展示Scala实现是因为我对它的语法比较熟悉 , 而且Scala分别为筛选、变换和转化概念使用了行业通用的名称 , 即filter、map和reduce 。
Groovy实现
Groovy拥有相同的功能 , 但对它们进行命名的方式与脚本语言(比如Ruby)更加一致 。 清单1中处理示例的Groovy版本如清单3所示 。
清单3.Groovy处理
class TheCompanyProcess {
public static String cleanUpNames(List listOfNames) {
listOfNames
.findAll {it.length() > 1}
.collect {it.capitalize()}
.join(',')
}
}
尽管清单3在结构上类似于清单2中的Scala示例 , 但方法名称不同 。 Groovy的findAll集合方法应用所提供的代码块 , 保留代码块为true的元素 。 如同Scala , Groovy包含一个隐式参数机制 , 为单参数代码块使用预定义的it隐式参数 。 collect方法(Groovy的map版本)对集合的每个元素执行所提供的代码块 。 Groovy提供一个函数(join()) , 使用所提供的分隔符将字符串集合串联为单一字符串 , 这正是本示例中所需要的 。
Clojure实现
Clojure是一个使用reduce、map和filter函数名的函数式语言 , 如清单4所示 。
清单4.Clojure处理示例
(defn process [list-of-emps]
(reduce str (interpose ","
(map clojure.string/capitalize
(filter #(< 1 (count %)) list-of-emps)))))
Clojure的thread-first宏
thread-last宏使集合的处理变得更加简单 。 类似的Clojure宏thread-first可简化与JavaAPI的交互 。 例如普遍的Java代码语句person.getInformation().
getAddress().getPostalCode() , 这体现了Java违反迪米特法则的倾向 。 这种类型的语句给Clojure编程带来一些烦恼 , 迫使使用JavaAPI的开发人员不得不构建由内而外的语句 , 比如(getPostalCode(getAddress(getInformationperson))) 。 thread-first宏消除了这一语法困扰 。 你可以使用宏将嵌套调用编写为(->persongetInformationgetAddressgetPostalCode) , 想嵌套多少层都可以 。
如果你不习惯查看Clojure , 可以使用清单4中的代码 , 其结构可能不够清晰 。 Clojure这样的Lisp是“由内而外”进行工作的 , 所以必须从最后的参数值list-of-emps着手 。 Clojure的(filter)函数接受两个参数:用于进行筛选的函数(本例中为匿名函数)和要筛选的集合 。
你可以为第一个参数编写一个正式函数定义 , 比如(fn[x](<1(countx))) , 但使用Clojure可以更简洁地编写匿名函数 。 与前面的示例一样 , 筛选操作的结果是一个较少的集合 。 (map)函数将变换函数接受为第一个参数 , 将集合(本例中是(filter)操作的返回值)作为第二个参数 。 Clojure的(map)函数的第一个参数通常是开发人员提供的函数 , 但接受单一参数的任何函数都有效;内置capitalize函数也符合要求 。
最后 , (map)操作的结果成为了(reduce)的集合参数 。 (reduce)的第一个参数是组合函数(应用于(interpose)的返回的(str)) 。 (interpose)在集合的每个元素之间(除了最后一个)插入其第一个参数 。
当函数嵌套过多时 , 即使最有经验的开发人员也会倍感头疼 , 如清单4中的(process)函数所示 。 所幸的是 , Clojure包含的宏支持你将结构“调整”为更可读的顺序 。 清单5中的功能与清单4中的功能一样 。
清单5.使用Clojure的thread-last宏


推荐阅读