作为一个对Hashell语言[1]彻头彻尾的新手,当第一次看到一个用这种语言编写的快速排序算法的优雅例子时,我立即对这种语言发生了浓厚的兴趣。下面就是这个例子: 1 2 3 4 5 6 7 | quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (p:xs) =
(quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter (>= p) xs
|
我很困惑。如此的简单和漂亮,能是正确的吗?的确,这种写法并不是“完全正确”的最优快速排序实现。但是,我在这里并不想深入探讨性能上的问题 [2]。我想重点强调的是,纯函数式编程是一种思维上的改变,是一种完全不同的编程思维模式和方法,就相当于你要重新开始学习另外一种编程方式。

首先,让我先定义一个问题,然后用函数式的方式解决它。我们要做的基本上就是按升序排序一个数组。为了完成这个任务,我使用曾经改变了我们这个世界的快速排序算法[3],下面是它几个基本的排序规则:
那么,如何用函数式的方式思考、函数式的方式编程实现?在这里,我将模拟同一个程序员的两个内心的对话,这两个内心的想法很不一样,一个使用命令式 的编程思维模式,这是这个程序员从最初学习编码就形成的思维模式。而第二个内心做了一些思想上的改造,清洗掉了所有以前形成的偏见:用函数式的方式思考。事实上,这程序员就是我,现在正在写这篇文章的我。你将会看到两个完全不同的我。没有半点假话。
让我们在这个简单例子上跟Java进行比较: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | public class Quicksort {
private int [] numbers;
private int number;
public void sort( int [] values) {
if (values == null || values.length == 0 ){
return ;
}
this .numbers = values;
number = values.length;
quicksort( 0 , number - 1 );
}
private void quicksort( int low, int high) {
int i = low, j = high;
int pivot = numbers[low + (high-low)/ 2 ];
while (i <= j) {
while (numbers[i] < pivot) {
i++;
}
while (numbers[j] > pivot) {
j--;
}
if (i <= j) {
swap(i, j);
i++;
j--;
}
}
if (low < j)
quicksort(low, j);
if (i < high)
quicksort(i, high);
}
private void swap( int i, int j) {
int temp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = temp;
}
}
|
哇塞。到处都是i 和j ,这是干嘛呢?为什么Java代码跟Haskell代码比较起来如此的长?这就好像是30年前拿C语言和汇编语言进行比较!从某种角度看,这是同量级的差异。[4]
让我们俩继续两个”我”之间的对话。
JAVA: 好 ,我先开始定义Java程序需要的数据结构。一个类,里面含有一些属性来保存状态。我觉得应该使用一个整数数组作为主要数据对象,针对这个数组进行排序。还有一个方法叫做sort ,它有一个参数,是用来传入两个整数做成的数组,sort 方法就是用来对这两个数进行排序。
1 2 3 4 5 6 7 | public class Quicksort {
private int [] numbers;
public void sort( int [] values) {
}
}
|
HASKELL: 好,这里不需要状态,不需要属性。我需要定义一个函数,用它来把一个list转变成另一个list。这两个list有相同 之处,它们都包含一样的元素,并有各自的顺序。我如何用统一的形式描述这两个list?啊哈!typeclass….我需要一个typeclass来实现 这个…对,Ord .
1 | quicksort :: Ord a => [a] -> [a]
|
JAVA: 我要从简单的开始,如果是空数组,如果数组是空的,我应该返回这个数组。但是…该死的,当这个数组是null时,程序会崩溃。让我来在sort 方法开始的地方加一个if 语句,预防这种事情。
1 2 3 | if (values.length == 0 || values == null ) {
return ;
}
|
HASKELL: 先简单的,一个空list。对于这种情况,需要使用模式匹配。我看看如何使用,好的,非常棒!
|