通过上述这两个简单的例子,我们已经看到了 Clojure 的很多特性。Clojure 的另一个重要的方面是其与 Java 语言的紧密集成。让我们看另外一个例子来了解从 Clojure 使用 Java 是多么有帮助。
示例 3:使用 Java 技术Java 平台能提供的功能很多。JVM 的性能以及丰富的核心 API 和以 Java 语言编写的第三方库都是功能强大的工具,能够帮助避免大量重复的工作。Clojure 正是围绕这些理念构建的。在 Clojure 中,很容易调用 Java 方法、创建 Java 对象、实现 Java 界面以及扩展 Java 类。为了举例说明,让我们来看看另一个 Project Euler 问题。
清单 10. Project Euler 的问题 8Find the greatest product of five consecutive digits in the 1000-digit number. 73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 85861560789112949495459501737958331952853208805511 12540698747158523863050715693290963295227443043557 66896648950445244523161731856403098711121722383113 62229893423380308135336276614282806444486645238749 30358907296290491560440772390713810515859307960866 70172427121883998797908792274921901699720888093776 65727333001053367881220235421809751254540594752243 52584907711670556013604839586446706324415722155397 53697817977846174064955149290862569321978468622482 83972241375657056057490261407972968652414535100474 82166370484403199890008895243450658541227588666881 16427171479924442928230863465674813919123162824586 17866458359124566529476545682848912883142607690042 24219022671055626321111109370544217506941658960408 07198403850962455444362981230987879927244284909188 84580156166097919133875499200524063689912560717606 05886116467109405077541002256983155200055935729725 71636269561882670428252483600823257530420752963450
在这个问题中,有一个 1,000-位的数字。在 Java 技术里,该数可以通过 BigInteger 表示。但是,我们无需在整个数上进行计算 — 只需每次计算 5 位。因而,将它视为字符串会更为简单。不过,为了进行计算,我们需要将这些数位视为整数。所幸的是,在 Java 语言中,已经有了一些 API,可用来在字符串和整数之间来回转换。作为开始,我们首先需要处理上面这一大段不规则的文本。
清单 11. 解析文本(def big-num-str (str "73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 85861560789112949495459501737958331952853208805511 12540698747158523863050715693290963295227443043557 66896648950445244523161731856403098711121722383113 62229893423380308135336276614282806444486645238749 30358907296290491560440772390713810515859307960866 70172427121883998797908792274921901699720888093776 65727333001053367881220235421809751254540594752243 52584907711670556013604839586446706324415722155397 53697817977846174064955149290862569321978468622482 83972241375657056057490261407972968652414535100474 82166370484403199890008895243450658541227588666881 16427171479924442928230863465674813919123162824586 17866458359124566529476545682848912883142607690042 24219022671055626321111109370544217506941658960408 07198403850962455444362981230987879927244284909188 84580156166097919133875499200524063689912560717606 05886116467109405077541002256983155200055935729725 71636269561882670428252483600823257530420752963450"))
这里,我们利用了 Clojure 对多行字符串的支持。我们使用了 str 函数来解析这个多行字符串。之后,使用 def 宏来定义一个常量,称为 big-num-str。不过,将其转换为一个整数序列将十分有用。这在清单 12 中完成。
清单 12. 创建一个数值序列(def the-digits (map #(Integer. (str %)) (filter #(Character/isDigit %) (seq big-num-str))))
同样地,让我们从最里面的表达式开始。我们使用 seq 函数来将 big-num-str 转变为一个序列。不过,结果表明此序列并非我们所想要的。REPL 可以帮助我们看出这一点,如下所示。
清单 13. 查看这个 big-num-str 序列user=> (seq big-num-str) (\7 \3 \1 \6 \7 \1 \7 \6 \5 \3 \1 \3 \3 \0 \6 \2 \4 \9 \1 \9 \2 \2 \5 \1 \1 \9 \6 \7 \4 \4 \2 \6 \5 \7 \4 \7 \4 \2 \3 \5 \5 \3 \4 \9 \1 \9 \4 \9 \3 \4 \newline...
REPL 将字符(一个 Java char)显示为 \c。因此 \7 就是 char 7,而 \newline 则为 char \n(一个换行)。这也是直接解析文本所得到的结果。显然,我们需要消除换行并转换为整数,然后才能进行有用的计算。这也正是我们在清单 11 中所做的。在那里,我们使用了一个过滤器来消除换行。请再次注意,我们为传递给 filter 函数的断言函数使用了一个简短的闭包。闭包使用的是 Character/isDigit。这是来自 java.lang.Character 的静态方法 isDigit。因此,这个过滤器只允许数值 char,而会丢弃换行字符。