写完『泛型与闭包』这篇文章后,暗自感慨了一番,要是 C 语言能够从语言层面支持闭包,而不是借助函数指针去进行繁琐的模拟,那么用 C 写程序就一定会有趣很多。

但是这种感慨很快就石化了。因为即使 C 语言支持闭包,但是像下面这样琐碎的代码:

static int
int_compare (const void *a, const void *b)
{   
        if (*((int *)a) > *((int *)b))
                return 1;
        else if (*((int *)a) == *((int *)b))
                return 0;
        else
                return -1;
}

将这样的代码放在任何一个匿名函数里,都与优雅无关,以至于我只好庆幸 C 语言不支持闭包……但是我想起了几年前曾经把玩过一两次的 Vala,这个项目现在还在开发中。

Vala 很有趣,它是 C# 风格的语言,也是一个编译器。Vala 编译器将 Vala 代码编译成 C 代码,不是普通的 C 代码,而是充斥着 GLib 与 GObject 函数调用的 C 代码。也就是说,如果你直接在 C 程序中使用 GObject 来写面向对象程序,那么你就可以考虑使用 Vala……也就是说,Vala 是 C + GObject + GLib 代码生成器。

下面,我给出一份基于 GLib 动态数组 GArray 的快速排序示例——test.c:

#include <glib.h>

gint int_compare (gconstpointer a, gconstpointer b) {
        if (*((gint *)a) > *((gint *)b))
                return 1;
        else if (*((gint *)a) == *((gint *)b))
                return 0;
        else
                return -1;
}

int main (void) {
        int d[] = {9, 3, 4, 8, 5, 7, 6};
        GArray *array = g_array_new (FALSE, FALSE, sizeof(int));
        for (guint i = 0; i < 7; i++) {
                g_array_append_val (array, d[i]);
        }

        g_array_sort (array, int_compare);

        for (guint i = 0; i < 7; i++) {
                g_print ("%d\n", g_array_index (array, int, i));
        }
}

编译命令为:

$ gcc -std=c99 $(pkg-config --cflags --libs glib-2.0) test.c -o test

下面是 Vala 的实现——test.vala:

delegate int CompareDataFunc (int *a, int *b);

void main() {
    int[] d = {9, 3, 4, 8, 5, 7, 6};
    GLib.Array<int> array = new GLib.Array<int>();
    for (int i = 0; i < d.length; i++) { array.append_val (d[i]); }
    
    CompareDataFunc compare = (a, b) => {
        if (*(int *)a > *(int *)b) { return 1; }
        else if (*(int *)a < *(int *)b) { return -1;}
        else return 0;
    };
    array.sort((GLib.CompareFunc<int>)compare);
    
    for (int i = 0; i < array.length; i++) {
        stdout.printf ("%d\n", array.index (i));
    }
}

编译命令为:

$ valac test.vala -o test 

虽然 Vala 代码显得并不比 C 代码精简多少,但是惊心动魄的是它从语法层面上提供了闭包的支持,而且是基于匿名函数的闭包!此外,它似乎还支持了基于模板的泛型容器与算法……

虽然形如

(a, b) => { if (*(int *)a > *(int *)b) { return 1; }
                 else if (*(int *)a < *(int *)b) { return -1;}
                 else return 0;
               };

这样的匿名函数并不美观,但是考虑到上述代码是比较『生硬』的调用了 GLib 中的函数,如果 Vala 自身实现了类似 qsort 的函数,那么代码会优雅一些,像下面这样:

void main () {
    int[] d =  {9, 3, 4, 8, 5, 7, 6};
    qsort (d, (a, b) => {if (a < b) return -1; if (a > b) return 1; return 0; });
    foreach (int a in d) { stdout.printf("%d\n", a); }
}

虽然 Vala 的匿名函数依然是使用 C 函数指针模拟的,但是观察上述代码,Vala 已将 C 归藏于无形。

现在,Vala 羽翼颇丰,似乎已经将 GNOME 平台的底层程序库一网打尽了(详见 http://valadoc.org),值得玩味,期待它能够早日达到 1.0 版本……不过,9 年过去了,它刚到 0.30.


garfileo
6k 声望1.9k 粉丝

这里可能不会再更新了。


引用和评论

0 条评论