015_表驱动编程思想(c实现)

【背景】

数据压倒一切。如果选择了正确的数据结构并把一切组织的井井有条,正确的算法就不言自明。编程的核心是数据结构,而不是算法。

——Rob Pike

上面是这个名人说过的话,那么c语言之父 丹尼斯·麦卡利斯泰尔·里奇 的《c程序设计》里曾经也有这样一句话: 程序 = 算法 + 数据(原文是:program = algorithm + data structure)

从以上两个人的思想当中我们可以知道:算法和数据对于程序来说是至关重要的,如果把程序比作一个人的话,算法就是他的灵魂,数据结构就是他的肉体(个人理解,不喜勿喷),我们在普遍的编程里,一直追求的是算法的高效性和安全性,往往忽略了数据结构的重要性和编程用途,其实,数据结构也可以作为编程的一种思想,这便是:表驱动编程!

所谓表驱动法(Table-Driven Approach)简而言之就是用查表的方法获取数据。此处的“表”通常为数组,但可视为数据库的一种体现。

根据字典中的部首检字表查找读音未知的汉字就是典型的表驱动法,即以每个字的字形为依据,计算出一个索引值,并映射到对应的页数。相比一页一页地顺序翻字典查字,部首检字法效率极高。

具体到编程方面,在数据不多时可用逻辑判断语句(if…else或switch…case)来获取值;但随着数据的增多,逻辑语句会越来越长,此时表驱动法的优势就开始显现

表驱动是一种在C语言里常见的编程模式,从表里面查找信息而不使用逻辑语句(if和case)。核心操作是将输入因素作为直接或者间接的索引,到数组里找到直接的结果或者对应的处理(通常是函数指针)。

在传统的23种面向对象设计模式里,并没有表驱动这种模式。这种模式是强烈依赖数组或者多维数组的一种设计模式,不涉及类,继承等关系,所以在C语言等非面向对象编程里得到了广泛的应用。

表驱动是一种在C语言里常见的编程模式,从表里面查找信息而不使用逻辑语句(if和case)。核心操作是将输入因素作为直接或者间接的索引,到数组里找到直接的结果或者对应的处理(通常是函数指针)。

表驱动实质上把逻辑和数据进行了分离。因素和结果之间的映射关系能够全部存放到数组里,而不是混杂在if,else的流程代码里。当映射关系发生改变的时候,只需要改变数组就可以,不需要修改代码。管理和维护起来非常方便。甚至可以把数据作为配置文件存放到硬盘上,需要的时候读取进来,避免了代码重新编译

看完上面这写内容,可能你对表驱动编程思想有些粗略的认识,脑袋也许会懵,很正常,我们举个现实生活中的例子看看,如下:

一家水果超市有苹果,香蕉,橘子这三种水果售卖,你要去买之前,想要在app上查下苹果还有没有?价格多少?人们的评价如何?这时你输入apple,就会出现对应的这些问题的答案,这个怎么实现呢?

【分析】

看了上面这个问题后,我相信大多数人都会想到if...else...结构或者switch...case...结构来实现此功能,完全可以,但是我们不妨再往深想一层,目前只是三类水果,当然可以这么做,而且也不费力,但是如果要实现100个水果类别呢?是不是脑袋瞬间嗡嗡的了?那么我们就可以用表驱动编程思想,去处理这100个不同的数据,注意:我们把重心放在了让数据去选择程序,就是把数据和程序对应起来,这次数据作为主导,去搞事情,而不是逻辑作为主导!是不是有点懵或者一时半会儿转不过这个弯儿?没事,你不妨用笔画画,试试看,再分析分析哦,这个想通了,你就知道在大型结构数据面前,表驱动编程是多么的666了,接下来,我们看看具体的testcode的实现

【实现_TestCode】

/****************************
*表驱动编程(数据结构思想)
****************************/

/*定义打印宏函数*/
#define PFS(a, b, c) printf("state: %s --- price: %.1lf --- \
evaluate: %s \n", a, b, c )
/*1.基本数据结构*/                                    
typedef struct Bs                   /*水果属性数据结构*/
{
    char* state;                    /*状态:是否还有该水果*/
    double price;                   /*价格:该水果多钱一斤*/
    char* evaluate;                 /*评价:受到的顾客评价good, general, bad*/
}base;                                    
typedef struct fruit                /*水果类别数据结构*/
{
    base apple;
    base banana;
    base orange;
}fs;
/*2.函数指针类型声明*/
typedef fs* (*funcp)(fs* tp);
/*3.函数指针具体实现*/
fs* apples(fs *tmp)                    /*苹果函数实现*/
{
    char* state = "have";
    double price = 2.1;
    char* evaluate = "good";
    
    LOGS("welcome to zll fruit shop...");
    LOGS("I am is apples");
    tmp->apple.state = state;
    //tmp.apple.state = state;
    tmp->apple.price = price;
    tmp->apple.evaluate = evaluate;
    return tmp;
};
fs* bananas(fs* tmp)                    /*香蕉函数实现*/
{
    char* state = "have";
    double price = 3.0;
    char* evaluate = "general";

    LOGS("welcome to zll fruit shop...");
    LOGS("I am is banana");
    tmp->banana.state = state;
    tmp->banana.price = price;
    tmp->banana.evaluate = evaluate;
    return tmp;
};
fs* oranges(fs* tmp)                    /*橘子函数实现*/
{
    char* state = "have";
    double price = 1.2;
    char* evaluate = "bad";

    LOGS("welcome to zll fruit shop...");
    LOGS("I am is orange");
    tmp->orange.state = state;
    tmp->orange.price = price;
    tmp->orange.evaluate = evaluate;
    return tmp;
};
/*4.数据结构组合*/
typedef struct map                      /*表驱动结构:名字+对应函数指针*/
{
    char* name;
    funcp handle;
}Map_node;
/*5.表编程实现*/
static const Map_node g_fruit_handle[] = {
                                        /*对应的数组结构:根据名字回调对应的函数*/
    {"apple", apples},
    {"banana", bananas},
    {"orange", oranges}
};
/*6.回调函数实现*/
fs mycallback(char* name, int len)
{
    int i;
    fs ttm = {0};

    for (i = 0; i < len; i++) {
        if (!strcmp(g_fruit_handle[i].name, name)) {
            if (g_fruit_handle[i].handle) {
                (*(g_fruit_handle[i].handle))(&ttm);
                return ttm;
            }
        }
    }
};
/*7.调用*/
void test_38_biaoprogram()
{
    char* name = "apple";
    int len = sizeof(Map_node);
    fs gtm = {0};

    gtm = mycallback(name, len);
    PFS(gtm.apple.state, gtm.apple.price, gtm.apple.evaluate);
};

我们测试的是apple,结果如下:

那么,以后只要有新的水果需要添加,我们只需要实现本水果函数并把name和handle添加到g_fruit_handle数组中便可,是不是有种焕然一新的感觉?完全和if...else..以及switch...case...的编程思想是两种感觉,一起加油,伙伴们^-^

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值