编写单链表反转的程序

首先,我们定义一个链表。

struct list { int value; struct list * next; };

C 语言也要面向对象编程,于是,编写构造函数。

struct list * makeList(int value, struct list * next){ struct list * this = (struct list*)malloc(sizeof(struct list)); this->value = value; this->next = next; }

编写反转程序。

struct list * reverse(struct list * L) { struct list * result = NULL; for(struct list * i = L; i != NULL; i = i->next){ result = makeList(i->value, result); } return result; }

OK,让我们测试一下。

void printList(struct list * L) { printf("("); int n = 0; for(struct list * i = L; i != NULL; i = i->next, ++n){ if(n != 0){ printf(","); }; printf("%d", i->value); } printf(")"); return; } int main(int argc, char *argv[]) { struct list * a = makeList(1, NULL); a = makeList(2, a); a = makeList(3, a); printf("before reverse:"); printList(a); printf("\nafter reverse:"); printList(reverse(a)); printf("\n"); printf("before reverse:"); printList(NULL); printf("\nafter reverse:"); printList(reverse(NULL)); printf("\n"); return 0; }

程序输出

before reverse:(3,2,1) after reverse:(1,2,3) before reverse:() after reverse:()

这个程序的风格是 imperative 的,如果使用函数式编程,我们得换一个语言 Java 。

public static class List<T> { private final T value; private final List<T> next; public List(T value, List next) { this.value = value; this.next = next; } @Override public String toString() { int n = 0; StringBuilder sb = new StringBuilder(); sb.append("("); for(List i = this; i!=null; i = i.next, n ++){ if(n!=0) { sb.append(","); } sb.append(i.value); } sb.append(")"); return sb.toString(); } }

构造函数

public static <T> List<T> cons(T value, List<T> next) { return new List(value, next); }

著名的 foldl 函数,一切一次遍历循环的抽象。

public static <T,R> R foldl(BiFunction<T,R,R> f, R r, List<T> l){ if(l == null){ return r; }else{ return foldl(f, f.apply(l.value, r), (List<T>)l.next); } }

可惜 java 不支持 TCO (Tail Call Optimization) ,这种写法会爆栈的。于是,用命令式的编程风格再写一次。

public static <T,R> R foldl(BiFunction<T,R,R> f, R init, List<T> l){ R result = init; for(List<T> i = l; i!=null; i = i.next){ result = f.apply(i.value, result); } return result; }

反转列表的函数

static <T> List<T> reverse(List<T> l) { return foldl(ListTest::cons, null, l); }

测试代码

@Test public void main() throws Exception { List a = new List(1, null); a = new List(2, a); a = new List(3, a); System.out.println("before reverse: " + a); List b = reverse(a); System.out.println("after reverse: " + b); }

输出结果

before reverse: (3,2,1) after reverse: (1,2,3)

foldl 可以抽象很多一次遍历的循环,例如,max 可以如下

static <T extends Comparable<? super T>> T max(List<T> l){ if(l == null) return null; return foldl((r1,r2) -> Comparator.<T>naturalOrder().compare(r1,r2) > 0?r1:r2,l.value, l); }

在函数式编程中,reverse-foldl 是一个俗语。例如 map 可以写成下面的形式。

static <T,R> List<R> map(Function<T,R> f, List<T> l) { return reverse(foldl((r,n) -> cons(f.apply(r),n), null,l)); }

测试代码

@Test public void main2() throws Exception { List<Integer> a = new List(1, null); a = new List(2, a); a = new List(3, a); System.out.println("map: " + map(v-> v + 100, a)); }

输出结果

map: (103,102,101)