如何拥有 Linus Torvalds 所说的编码“好品味” - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Coding.NET 轻量级社交
开源项目广场
使用帮助
意见反馈
darluc
V2EX    Coding

如何拥有 Linus Torvalds 所说的编码“好品味”

  •  1
     
  •   darluc 2016 年 11 月 11 日 6242 次点击
    这是一个创建于 3380 天前的主题,其中的信息可能已经有所发展或是发生改变。

    翻译自:Applying the Linus Torvalds "Good Taste" Coding Requirement

    点击阅读全文

    最近一次对 Linus Torvalds ( Linux 之父 ) 的采访视频中,约 14 分 20 秒的位置,他提到了带着“好品味”编程的观点。好品味?采访者对其细节进行了追问,而 Linus 则是早有准备。

    他首先展示了一个代码段。不过这不是一个有着“好品味”的代码。这个代码段是个坏样例,用作对比的原始代码。

    remove_list_entry(entry) { prev = NULL; walk = head; // Walk the list while (walk != entry) { prev = walk; walk = walk->next; } // Remove the entry by updating the // head or the previous entry if (!prev) head = entry->next; else prev->head = entry->next; } 

    无品味的代码示例

    这是个 C 语言实现的函数,用于移除链表中的某个节点。共 10 行代码。

    他让我们注意底部的 if 语句。就是这条 if 语句招致了他的批评。

    我暂停了视频,研究了一下幻灯片。最近我有写过一些类似的代码。觉得 Linus 实际上就是在说我的品味差。我只好暂时放下我的自尊心,继续看视频。

    Linus 向观众解释,从链表中移除一个节点时,有两种情况需要考虑。如果节点处于链表头,会于节点处于链表中的处理方式有所不同。而这就是那个 if 语句”坏品味“的来源。

    不过既然他也承认这种特殊处理是必须的,那它到底坏在哪里?

    接着,他又向观众展示了第二张幻灯片。这是他实现的同一个函数,不过是有着“好品味”的。

    remove_list_entry(entry) { // The "indirect" pointer points to the // *address* of the thing we'll update indirect = &head; // Walk the list, looking for the thing that // points to the entry we want to remove while ((*indirect) != entry) indirect = &(*indirect)->next; // .. and just remove it *indirect = entry->next; } 

    好品味代码示例

    最初的 10 行代码现在缩减为了 4 行。

    但是重点并不在于代码行数的减少。重点是那个 if 语句消失了,不再被需要。代码重构后, 无论节点在链表中的什么位置,都可以采用相同的处理过程来进行移除。

    Linus 解释了这段新代码对于边界情况的消除才是重点。采访继续,进入了下个话题。

    我研究了一会这个代码。 Linus 是对的。第二个幻灯片所展示的代码更好。如果这是一道测试编码好坏的题目,那我可能已经挂了。消除条件判断的想法从未在我的脑子里出现过。而且我也像那坏例子一样写了不止一次了,因为我经常进行链表处理操作。

    这个关于品味的展示,好处不仅仅在于教会你如何从一个链表中去除节点,它还使你去思考你曾经写过的代码,你曾经在程序中写过的小小算法或许还有改进的空间,并且以一种你从未想过的方式出现。

    所以最近我审阅项目代码时,这成为了我的关注点。很巧的是,这个项目也是使用的 C 语言。

    就我的理解而言,“好品味”的关键是消除边界情况,而这些情况一般表现为条件判断语句。越少使用条件判断,你的代码“品味”就会更好。

    下面有个我对代码进行改进的例子分享给大家。

    初始化网格边缘

    以下是我写的一个算法,用于初始化网格边缘的点,该网格使用一个多维数组表示:grid[rows][cols]

    这段代码的作用是初始化网格的边缘座标点 即顶部一行,底部一行,左侧一列,和右侧一列。

    为了完成工作,一开始我遍历了网格中的每个点,并且利用条件来判断他们是否处于边缘。代码如下:

    for (r = 0; r < GRID_SIZE; ++ r) { for (c = 0; c < GRID_SIZE; ++ c) { // Top Edge if (r == 0) grid[r][c] = 0; // Left Edge if (c == 0) grid[r][c] = 0; // Right Edge if (c == GRID_SIZE - 1) grid[r][c] = 0; // Bottom Edge if (r == GRID_SIZE - 1) grid[r][c] = 0; } } 

    虽然这段代码可以正常工作,回头来看,确实有点问题:

    1. 复杂度 在二重循环中使用 4 个条件判断语句,看起来有些过于复杂了。
    2. 效率 如果 GRID_SIZE 等于 64 ,这个循环迭代了 4096 次,只是为了给 256 个边缘点赋值。

    Linus 应该会觉得,这可真没品味。

    所有我对它进行了一些修补。花了些时间后,我可以降低它的复杂度,使其只需要一个包含四个条件判断的 for 循环。这只是在复杂度上改进了一点点,却极大地提升了性能,因为它只循环了 256 次,每个边缘上的座标点对应一次循环。

    for (i = 0; i < GRID_SIZE * 4; ++ i) { // Top Edge if (i < GRID_SIZE) grid[0][i] = 0; // Right Edge else if (i < GRID_SIZE * 2) grid[i - GRID_SIZE][GRID_SIZE - 1] = 0; // Left Edge else if (i < GRID_SIZE * 3) grid[i - (GRID_SIZE * 2)][0] = 0; // Bottom Edge else grid[GRID_SIZE - 1][i - (GRID_SIZE * 3)] = 0; } 

    这的确是一次改进。但是代码真是太丑陋了。而且并不易于理解。因此,我对这段代码并不满意。

    点击阅读全文

    10 条回复    2016-11-12 19:58:03 +08:00
    ianva
        1
    ianva  
       2016 年 11 月 11 日
    是一个良好的数据抽象解决问题的例子,但还缺少一个良好的过程抽象
    iEverX
        2
    iEverX  
       2016 年 11 月 11 日
    我会写四个循环
    TerrenceSun
        3
    TerrenceSun  
       2016 年 11 月 11 日
    这样呢?
    for (i = 0; i < GRID_SIZE; ++i) {
    grid[0][i] = grid[GRID_SIZE-1][i] = grid[i][0] = grid[i][GRID_SIZE-1] = 0;
    }
    holyghost
        4
    holyghost  
       2016 年 11 月 11 日
    justfly
        5
    justfly  
       2016 年 11 月 11 日
    这种感觉的培养,不止局限在代码层面,而是一种找到问题的本质和化繁为简的一种思想。

    其实在生活中遇到的各种事情,分析业务需求,日常写业务时都可以锻炼的,很多时候会明显感到是相通的。
    zhy0216
        6
    zhy0216  
       2016 年 11 月 12 日 via iPhone
    第一个例子 不知道用 python 怎么做 ..
    cnnblike
        7
    cnnblike  
       2016 年 11 月 12 日
    确实有道理
    lsmgeb89
        8
    lsmgeb89  
       2016 年 11 月 12 日 via Android
    可以说是品味问题也不是,对有些人来说即使他意识到了他的处理方法不够 general ,他也很难想到方法二。
    lsmgeb89
        9
    lsmgeb89  
       2016 年 11 月 12 日 via Android
    具体看了下代码,感觉方法二要看一会才能理解,需要对二级指针有很深的理解。
    alexapollo
        10
    alexapollo  
       2016 年 11 月 12 日
    不认为 linus 的这个做法是对的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     962 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 18:36 PVG 02:36 LAX 10:36 JFK 13:36
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86