西安达内培训讲师表示,面向对象的程序员倾向于不断创建新数据结构和附带的运算。毕竟,构建新类和在它们之间传递的消息是主要的语言模式。但是构建如此多的定制结构,会使在最低层级上构建可重用代码变得很困难。Java中组合函数式编程语言引用一些核心代码结构并构建优化的机制来理解它们。
以下是一个示例。清单1给出了来自 Apache Commons 框架的 indexOfAny() 方法,该框架为 Java 编程提供了大量帮助器。
清单1. 来自 Apache Commons 的 indexOfAny()
// From Apache Commons Lang, http://commons.apache.org/lang/
public static int indexOfAny(String str, char[] searchChars) {
if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) {
return INDEX_NOT_FOUND;
}
int csLen = str.length();
int csLast = csLen - 1;
int searchLen = searchChars.length;
int searchLast = searchLen - 1;
for (int i = 0; i < csLen; i++) {
char ch = str.charAt(i);
for (int j = 0; j < searchLen; j++) {
if (searchChars[j] == ch) {
if (i < csLast && j < searchLast && CharUtils.isHighSurrogate(ch)) {
if (searchChars[j + 1] == str.charAt(i + 1)) {
return i;
}
} else {
return i;
}
}
}
}
return INDEX_NOT_FOUND;
}
清单1中 1/3 的代码负责边缘检查和实现嵌套迭代所需的变量的初始化。我将逐步将此代码转换为 Clojure。作为第一步,我将删除边角情形,如清单2所示。
清单2. 删除边角情形
public static int indexOfAny(String str, char[] searchChars) {
when(searchChars) {
int csLen = str.length();
int csLast = csLen - 1;
int searchLen = searchChars.length;
int searchLast = searchLen - 1;
for (int i = 0; i < csLen; i++) {
char ch = str.charAt(i);
for (int j = 0; j < searchLen; j++) {
if (searchChars[j] == ch) {
if (i < csLast && j < searchLast && CharUtils.isHighSurrogate(ch)) {
if (searchChars[j + 1] == str.charAt(i + 1)) {
return i;
}
} else {
return i;
}
}
}
}
return INDEX_NOT_FOUND;
}
}
Clojure 会智能地处理 null 和 empty 情形,拥有 (when ...) 等智能函数,该函数仅在字符存在时返回 true。Clojure 具有动态(且强)类型,消除了在使用前声明变量类型的需求。因此,我可以删除类型声明,获得清单3中所示的代码。
清单3. 删除类型声明
indexOfAny(str, searchChars) {
when(searchChars) {
csLen = str.length();
csLast = csLen - 1;
searchLen = searchChars.length;
searchLast = searchLen - 1;
for (i = 0; i < csLen; i++) {
ch = str.charAt(i);
for (j = 0; j < searchLen; j++) {
if (searchChars[j] == ch) {
if (i < csLast && j < searchLast && CharUtils.isHighSurrogate(ch)) {
if (searchChars[j + 1] == str.charAt(i + 1)) {
return i;
}
} else {
return i;
}
}
}
}
return INDEX_NOT_FOUND;
}
}
for 循环 (命令式语言的主要元素)允许依次访问每个元素。函数式语言倾向于更多地依靠集合方法,这些方法已理解(或避免)了边角情形,所以我可删除 isHighSurrogate()(它检查字符编码)等方法和索引指针的操作。此转换的结果如清单4所示。
清单4. 一个用于替换最里面的 for 的 when 子句
// when clause for innermost for
indexOfAny(str, searchChars) {
when(searchChars) {
csLen = str.length();
for (i = 0; i < csLen; i++) {
ch = str.charAt(i);
when (searchChars(ch)) i;
}
}
}
在清单4中,我将代码折叠到一个方法中,该方法会检查受欢迎的字符是否存在,在找到这些字符时,它会返回其索引。尽管我既未使用 Java 也未使用 Clojure,而是提供了一段陌生的伪代码,但这个 when 方法并不总是存在。但 Clojure 中还有 (when ) 方法,此代码会慢慢变成该方法。
接下来,我将最顶层的 for 循环替换为一种更加简洁的代码,使用 for comprehension: 一个结合了集合的访问和过滤(等)的宏。演变后的代码如清单5所示。
清单5. 添加一个 comprehension
// add comprehension
indexOfAny(str, searchChars) {
when(searchChars) {
for ([i, ch] in indexed(str)) {
when (searchChars(ch)) i;
}
}
}
要理解清单5中的 for comprehension,首先您必须理解一些部件。Clojure 中的 (indexed ...) 函数接受一个 Sequence 并返回一个包含编号的元素的序列。例如,如果我调用 (indexed '(a b c)),返回值为 ([0 a] [1 b] [2 c])。(单个撇号告诉 Clojure,我想要一个字符的文字序列,但并不希望执行一个包含两个参数的 (a )。)for comprehension 在我的搜索字符上创建这个序列,然后应用内部的 when 来查找匹配字符的索引。
此转换的最后一步是将代码转换为合适的 Clojure 语法,还原真实函数和语法的外观,如清单6所示。
清单6. Clojure 化的代码
// Clojure-ify
(defn index-filter [pred coll]
(when pred
(for [[index element] (indexed coll) :when (pred element)] index)))
在清单6中的最终的 Clojure 版本中,我将语法转换为合适的 Clojure 并添加一次升级:此函数的调用方现在可传递任何判定函数(一个返回布尔值结果的函数),而不只是检查一个空字符串。Clojure 的一个目标是实现创建可读的代码的能力(在您理解圆括号之后),而且这个函数证实了这种能力:对于带索引的集合,在您的判定与元素匹配时,将会返回索引。西安达内科技二维码,更多集团优惠活动及试听机会尽在此。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/29562929/viewspace-1168264/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/29562929/viewspace-1168264/