从表驱动到函数式编程

不知不觉,接触代码也有一段时间了。虽然进学校正式地学编程也就是最近四五年的事情,不过写的第一行代码倒是在这之前。

大概是因为认识的朋友许多都在写代码或者打竞赛所以也就对代码感兴趣了,也跟过一些C或者Python的题,不过都半途而废了。倒是想起来,那时候写的第一个程序,似乎在现在看来也是有趣的呢。

大概是这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# include <stdio.h>

int main ()
{
printf("Please specify the month:");
int month;
scanf("%d", &month);
char * tsuki[] = {
"January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December",
};
printf("%s", tsuki[month-1]);
return 0;
}

那时候只是为了省事不想重复写12次if或者switch,就想到了开个数组把月份放进去的歪招。朋友倒是说我「发现了宝藏」,然后也就知道了有一个叫表驱动的技巧。

不过在那之后,竞赛代码的晦涩难懂还是把我劝退了,也没有继续写多少C的代码。这件事情也逐渐忘却了。也是后来转专业读计算机,接触了Linq和lambda后,才又对抽象和设计的事情感兴趣了。

其实回过头看的话,前面的表里面存放的是字符串,所以可以不用重复写12次if语句,也可以直接跳转实现根据不同条件(这里的输入月份)打印不同的结果的效果。

如果把字符串变成函数指针,是不是就可以根据不同月份来进行不同的操作了呢,比如9月的时候调用开学的函数,7和8月的时候调用放假的函数。

退一步说,甚至输入的月份数字,也可以从整数变成一个条件函数(Predicate),虽然这种情况下,主函数也需要稍作修改了。

从函数式编程的角度讲,其实也就是把函数放进一个数组或者字典里面而已。退一步说,只是因为在函数式的视角里面,函数本身也是变量的值,可以作为函数的传入参数或者返回值。

大概这就是表驱动的本质吧。当然,C语言里面的类型,从函数式编程的角度讲是不安全的。不过,这也许提供了窥视编程语言设计的一个角度。

如果用Scala或者Kotlin等现代语言来进行设计的话,表驱动也可以是类型安全的。

其实说到这里,也会想,为什么要用函数式编程,而不是过程式,面向对象,面向切面……吧。

表驱动也许提供了一个很好的应用场景:把逻辑结构从代码中抽取出来,变成数据结构。前面的代码就是个很好的例子,因为12个if语句是嵌入在代码里面的,如果程序变复杂了维护起来会很头疼。而放在同一个表里面的一组12个字符串就好维护很多。

更进一步地说,这个表甚至不一定要是一个预先设置的变量,可以是从文件里读取的,也可以从stdin里面的用户输入中读取。这就提供了运行时动态改变程序行为的可能性。

看到这些的话,会想起什么呢?感觉很像依赖注入和元编程。

实际上,撇开这些概念的话,编程语言需要的,大概是在保持静态的代码安全的前提下,又能够有尽可能多的扩展性和动态性吧,例如对AST的修改的可能性,或者换个说法,「运行时动态改变程序行为」。而函数式编程最擅长的也就是这个了。

最后也稍微对比一下表驱动和策略模式。表驱动的优点大概就是,所谓的「选择策略的算法」可以完全自动化,不需要手工修改,而代码也不用分散在一堆文件里面。策略模式,可能更适合比较复杂的逻辑。当然这里的策略模式也其实完全可以体现为一个只有单独一个函数的文件。


从表驱动到函数式编程
http://inori.moe/2023/09/27/from-table-driven-to-fp/
作者
inori
发布于
2023年9月27日
许可协议