if/else
使用if/else语句,可以让程序根据条件选择不同的执行路径。
import enum
class A(enum.Enum):
A1 = 1
A2 = 2
def fA1():
print("A1")
def fA2():
print("A2")
def do_something(cond: A):
"""根据条件执行不同的函数"""
if cond == A.A1:
fA1()
elif cond == A.A2:
fA2()
当需要判断的条件过多时,if/else语句的数量也随之增多
import enum
class A(enum.Enum):
A1 = 1
A2 = 2
def fA1():
print("A1")
def fA2():
print("A2")
class B(enum.Enum):
B1 = 1
B2 = 2
def fB1():
print("B1")
def fB2():
print("B2")
def do_something(cond1: A, cond2: B):
"""根据条件执行不同的函数"""
if cond1 == A.A1 and cond2 == B.B1:
fA1()
fB1()
elif cond1 == A.A1 and cond2 == B.B2:
fA1()
fB2()
elif cond1 == A.A2 and cond2 == B.B1:
fA2()
fB1()
elif cond1 == A.A2 and cond2 == B.B2:
fA2()
fB2()
从上面的代码可以发现,当需要判断的条件有两个,且每个条件有两种情况时,就需要写4个if/else语句,当条件更多时,if/else语句的数量也随之增多,而且还需要修改do_something
函数的内部逻辑(添加其他的if/else语句),这样会导致do_something
函数的代码越来越长,越来越难以维护。
表驱动法
表驱动法是一种编程技巧,它可以将if/else语句转换为表格,从而简化代码,提高代码的可读性和可维护性。这里的表可以是字典,也可以是数组。将上面的代码修改为字典表驱动法:
from typing import Callable
callback: dict[A | B, Callable] = {
A.A1: fA1,
A.A2: fA2,
B.B1: fB1,
B.B2: fB2,
}
def do_something(cond1: A, cond2: B):
"""根据条件执行不同的函数"""
callback[cond1]()
callback[cond2]()
可以发现,通过字典根据不同的条件获取不同的函数,可以将if/else语句转换为表格,从而简化代码,提高代码的可读性和可维护性。如果在后期修改A/B情况的个数的时候,只需要修改字典callback
即可,不需要修改do_something
函数的代码,这样可以减少代码的维护成本。
表驱动法的核心是,执行的逻辑是在运行时隐式的确定,而if/else则是在编译时确定的。因此,表驱动法的性能会比if/else差一些,但是这种性能的损失是可以接受的。
C语言实现表驱动法
在C语言种没有原生的字典类型,所以使用数组来实现表驱动法。
// 定义回调函数
typedef int (*callback)(int, int);
// 定义表驱动元素
typedef struct {
char* op;
callback cb;
} icallback;
// 定义多个回调函数
int
add(int a, int b)
{
return a + b;
}
int
sub(int a, int b)
{
return a - b;
}
// 定义表
static icallback callback_table[] = {
{ "add", add },
{ "sub", sub },
{ NULL, NULL },
};
定义了表之后,需要实现一个查找函数,根据条件查找对应的函数。
callback
find_callback(char* op)
{
for (int i = 0; i < sizeof(callback_table) / sizeof(icallback); i++) {
if (callback_table[i].op == NULL) {
printf("not found %s\n", op);
return NULL;
}
if (strcmp(op, callback_table[i].op) == 0) {
return callback_table[i].cb;
}
}
return NULL;
}
通过这个查找函数,就可以确定执行的函数了,然后执行即可。
// 根据条件执行不同的函数
void
do_something(char* op, int a, int b)
{
callback cb = find_callback(op);
if (cb == NULL) {
printf("not found %s\n", op);
return;
}
printf("%d %s %d = %d\n", a, op, b, cb(a, b));
}
// 使用
do_something("add", 1, 2); // 1 add 2 = 3
do_something("sub", 1, 2); // 1 sub 2 = -1
do_something("mul", 1, 2); // not found mul
与Python不同的是,C语言中使用函数指针,编译器无法进行内联优化,但当回调函数执行时间较长时,这种性能的损失是可以接受的。
总结
表驱动法的核心是通过在运行时动态的查找对应的函数。由于编程语言之间的特点不同,具体的实现方法也不同,但是核心思想是一样的。