如何避免使用continue和break

大神王垠说避免使用continue和break。他说:

出现continue或者break的原因,往往是对循环的逻辑没有想清楚。如果你考虑周全了,应该是几乎不需要continue或者break的。如果你的循环里出现了continue或者break,你就应该考虑改写这个循环。改写循环的办法有多种:

1.如果出现了continue,你往往只需要把continue的条件反向,就可以消除continue。
2.如果出现了break,你往往可以把break的条件,合并到循环头部的终止条件里,从而去掉break。
3.有时候你可以把break替换成return,从而去掉break。
4.如果以上都失败了,你也许可以把循环里面复杂的部分提取出来,做成函数调用,之后continue或者break就可以去掉了。

现在我有这样一段代码:代码要处理的就是检查name中有没有非规范的字符。代码功能是没有问题的。

int is_valid_name(char *name, uint16_t len)
{
    char regularChrs[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
    char tmp[sizeof(uint16_t)]={0};
    uint16_t regLen,i,j; 
    if(len <= 0)
        return 1;   
    memcpy(tmp, name, len);
    
    regLen = strlen(regularChrs);
    for(i=0; i<len; i++)
    {
        for(j=0; j<regLen; j++)
        {
            if(tmp[i] != regularChrs[j])
                continue;
            else
                break;
        }
        if(j == regLen)
        {
            return 1;
        }
    }
    return 0;
}

如何改写呢?

这里要注意,我们的核心逻辑里面包含了continebreak,其控制流程如下图所示:

QQ图片20151207160802.jpg-1034kB
QQ图片20151207160802.jpg-1034kB

但仔细分析一下,我们可以发现continue语句是无效的语句,因为即使没有它,控制流程也能返回。那么实际的逻辑语句是:

        ...
        for(j=0; j<regLen; j++)
        {
            if(tmp[i] == regularChrs[j])
                break;
        }
        ...

改写步骤

现在情况简单明了,只需要将break语句消除掉即可,方法是:如果出现了break,你往往可以把break的条件,合并到循环头部的终止条件里,从而去掉break。并将中间循环提取出来形成一个单独函数,用于判断一个字符是否是规范字符,以使表达更简洁;另外对于返回值,要根据函数语义进行修改。

const char regularChrs[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
uint16_t regLen = strlen(regularChrs);
int is_valid_letter(char ch)
{
    uint16_t index = 0;
    while(index < regLen && ch != regluarChrs[index])
    {
        index += 1;
    }
    //退出循环后可以判断是否遍历完regularChrs。
    //如果遍历完,说明ch不等于规范字符串中的任何一个。invalid
    if(j == regLen)
    {
        return 0; //invalid
    }else
    {
        return 1; //valid
    }
}

然后将原来代码中相关的逻辑处理用这个函数代替。

int is_valid_name(char *name, uint16_t len)
{
    uint16_t i = 0;
    
    if(len <= 0)
        return 0;
    
    while(i < len && is_valid_letter(name[i]) )
    {
        i += 1;
    }
    //遍历完整个name发现都是valid letter,即name也是valid    
    if(i == len)
    {
        return 1; //valid
    }else
    {
        return 0; //invalid
    }
}

仔细观察上面两个函数,它们代码结构是相似的,不同在于两个函数的判断标准是相反的,因此返回值正好相反,这是为了保证函数语义的一致性
这样的两个不同的逻辑判断如果在一个函数里面实现的时候,就会因为逻辑嵌套且上层与下层的逻辑实现不同而造成背离感,这种背离感是这种复杂逻辑所固有的。在我们修改版本中体现的更好。修改版本中,如果你保证上层与下层函数语义的一致性,那么就会失去实现逻辑的一致性;如果保证实现的一致性,那么就会失去函数语义的一致性。
我们可以想象用is_invalid_letter函数来重新实现我们的功能。代码如下:

int is_invalid_letter(char ch)
{
    uint16_t index = 0;
    while(index < regLen && ch != regluarChrs[index])
    {
        index += 1;
    }
    //退出循环后可以判断是否遍历完regularChrs。
    //如果遍历完,说明ch不等于规范字符串中的任何一个。invalid
    if(j == regLen)
    {
        return 1; //invalid
    }else
    {
        return 0; //valid
   }
}

int is_valid_name(char *name, uint16_t len)
{
    uint16_t i = 0;
    
    if(len <= 0)
        return 0;
    
    while(i < len && !is_invalid_letter(name[i]) )
    {
        i += 1;
    }
    //遍历完整个name发现都是valid letter,说明name也是valid   
    if(i == len)
    {
        return 1; //valid
    }else
    {
        return 0; //invalid
    }
}

此时大家会发现,两个函数实现逻辑已经是相同的了,但是函数语义已经背反。

总结

原始代码里面,看起来代码量比较少,但是语义不清,理解起来要困难很多,而且尤其是continuebreak在一起使用时,使整个代码逻辑上跟纠缠的面条一样,增加阅读难度。经过整理之后的代码,代码行数虽然增加,但是语义清晰,容易理解,而且同构的代码在逻辑层次上也能捋清了。
大神诚不欺我矣。

再进一步,这样的一个功能,还可以优化一下,函数接口参数增加对规范字符的支持。函数原型是:

int is_valid_name(char *name, uint16_t len, char *regular, uint16_t reglen)

至于实现,相信大家!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,322评论 5 465
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,288评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,227评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,015评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,936评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,534评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,995评论 3 389
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,616评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,907评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,923评论 2 315
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,741评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,525评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,016评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,141评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,453评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,054评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,249评论 2 339

推荐阅读更多精彩内容

  • 刚看完《釜山行》,挺不错的电影,讲人性,讲爱,整部电影虽然绝望但还是透着一点希望,人与人的爱盖过了原有的人性,看的...
    4984916b6fc6阅读 237评论 0 0
  • 文|余七青 -1- 我从来没有想到过有一天,我们会变成这副模样。时间改变了面貌也改变了我们的内心,很多年以后再回头...
    余七青阅读 911评论 4 3
  • #师北宸一块听听写作课# 我觉得主要是时间+拖延症+没啥可写。 虽然我用并列的方式列出了这三个,但其实里面有交叉的...
    涉蓝阅读 338评论 2 1