阅读 458

表驱动法(更优雅的写if-else、switch-case)

表驱动法

表驱动法就是一种编程模式(scheme)——从表里面查找信息而不使用逻辑语句(if 和case)。事实上,凡是能通过逻辑语句来选择的事物,都可以通过查表来选择。对简单的情况而言,使用逻辑语句更为容易和直白。但随着逻辑链的越来越复杂,查表法也就愈发显得更具吸引力。

直接访问

一个很简单的例子: 今天星期几

const day = new Date().getDay()
let day_zh;
if(day === 0){
    day_zh = '星期日'
}else if(day === 1) {
    day_zh = '星期一'
}
...
else{
    day_zh = '星期六'
}

// 或者 用 switch case
switch(day) {
    case 0:
        day_zh = '星期日'
        break;
    case 1:
        day_zh = '星期一'
        break;
        ...
}

复制代码

实现同样功能的一种更简单、更容易修改的方法是把这些数据存到一张表里面。

const week = ['星期日', '星期一',..., '星期六']
const day = new Date().getDay(
const day_zh = week[day]
复制代码

构造查询键值

在上述例子中,可以将数据作为键值直接访问表。

而事情并不随人愿,如保险费率根据不同年龄费率不同——不满18岁设置一个费率0.2,18-65岁设置一个费率0.4,65岁及以上设置一个费率0.6。 这就需要构造查询键值

  1. 复制信息从而能够直接使用键值(即每一个年龄对应一个费率,假设人一出生0岁, 最多100岁)
// 我们需要一个这样的数组: 这里写了18个0.2, 47个0.4, 36个0.6
const rateTabel = [ 0.2, ... , 0.2, 0.4, ... , 0.4, 0.6, ... , 0.6 ]
const rate = rateTable[age]
复制代码
优点:表自身结构简单,可以直接查询

缺点:信息冗余,复制增加错误可能性
复制代码
  1. 转换键值以使其能够直接使用

用一个函数将age 转换为另一个数值,从而使其能像键值那样使用。

例如把0-17之间的年龄转换成一个键值比如17,65及以上的都转换成 66, 18-65之间都转换成30。 可以用这样简单的函数来处理一下

    const ageKey = max( min (66, age), 17)
    const rateTable = {17: 0.2, 30: 0.4, 66: 0.4}
    const rate = rateTable[ageKey]
复制代码

索引访问

有时候呢,只用简单的数学运算还无法把age这样的数据转换成表键值,我们就需要用索引访问的方法来解决了。

当你使用索引的时候, 先用一个基本数据类型的数据从一张索引表中查出一个键值,然后再用这一键值查出你需要的主数据。

  1. 节省空间,比直接索引的全表法节约空间

  2. 灵活, 根据不同的字段生成不同的索引表

  3. 可维护性强

阶梯访问

这种方法不像索引访问那么直接,但比索引访问要节省空间。

典型的例子就是等级评定

考试按分数评优良中差四个等级

简单做法

    let level = '优'
    if(score <60 ){
        level = '差'
    }else if(score < 80) {
        level = '中'
    }else if(score < 90) {
        level = '良'
    }
复制代码

如果需要添加等级 60-70 之间 是一般,就需要改逻辑,逻辑简单的还好

    let level = '优'
    if(score <60 ){
        level = '差'
    }else if(score < 70) {
        level = '一般'
    }else if(score < 80) {
        level = '中'
    }else if(score < 90) {
        level = '良'
    }
复制代码

这时用阶梯查询就比较方便,而且扩展性也好

    const levelTable = ['差', '中', '良', '优']
    const scoreCeilTable = [60, 80, 90, 100]
    
    function getLevel(score) {
        const len = scoreCeilTable.length
        for(let i = 0; i < len; i++) {
            const scoreCeil = scoreCeilTable[i]
            if(score <= scoreCeil) {
                return levelTable[i]
            }
            
        }
        return levelTable[len - 1]
    }
    
复制代码

就算后面需要添加等级60-70 ---> '一般' 也只需要简单添加数据表就可以了,而不需要修改逻辑

    const levelTable = ['差', '一般', '中', '良', '优']
    const scoreCeilTable = [60, 70, 80, 90, 100]
复制代码

阶梯访问表需要注意几个问题

  1. 留心端点
  2. 二分查找替代顺序查找:上面例子用了顺序查找,当数据比较大时,查找成本还是比较大的,应该考虑用二分查找替代顺序查找
  3. 考虑用索引访问来取代阶梯技术,阶梯查找比较耗时,如果速度非常重要,可以用索引访问来取代阶梯,用空间换时间,但也要分情况,因为离散空间是不能够完全替代连续空间的