Python中的功能与命令式风格

时间:2013-08-20 21:24:25

标签: python python-2.7 coding-style functional-programming

我最近花了一些业余时间试图绕过Haskell和函数式编程。我的一个主要疑虑是(并且仍然是)我认为是简洁的,功能性的表达式(无论是在Haskell还是其他任何语言中)的不可读性。

这是一个例子。我刚刚在一些代码中进行了以下转换:

def scrabble_score(word, scoretable):    
    score = 0
    for letter in word:
        if letter in scoretable:
            score += scoretable[letter]
    return score

def scrabble_score(word, scoretable):
    return sum([scoretable.get(x, 0) for x in word])

后者写作更令人满意(并且,请记住,我自己也是写作的人,也是读者)。如果我在输入代码时犯了任何错误,它会更短更甜,并且不会声明任何令人头疼的变量。 (顺便说一下,我意识到我可以在命令式风格的函数中使用dict的get()方法,但是当我执行这个转换时我意识到这一点,所以我在示例中就这样做了。)

我的问题是:尽管如此,代码的后一版本是否真的更具可读性?与前一版本相比,它似乎是一个更加单一的表达方式,与之前的版本相比,你有机会从更小,更原子的部分构建块的含义。这个问题来自于我试图解码所谓的易于理解的Haskell函数,以及我作为一年级CS学生的TA的坚持,我们编写代码的主要原因之一(经常被忽视)是<强大>与其他程序员/计算机科学家交流想法。我很紧张,这种简洁的风格是只写代码,因此违背了沟通和理解代码的目的。

3 个答案:

答案 0 :(得分:6)

  

我的问题是:尽管如此,代码的后一版本是否真的比前者更具可读性?这似乎是一个更加单一的表达,必须一下子完成

我会说它更具可读性,但不一定是大多数可读选项。是的,它在一行中更具逻辑性,但这被三个事实所抵消。

首先,您可以在精神上将其分解为更小的部分,并在组合它们之前单独理解它们。我立即看到你正在总结某些东西(在这个阶段我不需要知道你正在总结的)。我还看到你从scoretable得到每个letter的条目,如果不存在则默认为0(在这个阶段我不必知道这个列表会发生什么)。这些东西可以独立识别和理解。 其次,很少有一些独立的部分,可能是两个或三个。立即理解这并不是不合理的,当几十个问题彼此相邻时,“单片”方面更是一个问题。

最后,第一个版本的假设优势,

  

与之前的版本相比,您可以从较小的,更原子的部分构建块的含义。

也是一个问题:当我开始阅读时,我看到一个for循环。这很好,但它实际上并没有告诉我任何东西(除了有一个循环,这不能帮助我理解逻辑,只有它的实现)。所以我必须记住这个事实并继续阅读以后解决这个循环意味着什么。在某种程度上,任何代码都是如此,但是代码越多,代价越大。同样地,score = 0行本身并不意味着任何东西,它只是一个很小的实现细节,没有人需要知道这个函数通过累加来计算单词的拼字游戏分数每个单词的分数得分。

在阅读命令性循环时,必须跟踪多个可变变量,控制流量等,以找出发生的情况,并且必须将这些部分拼凑在一起。此外,它只是阅读更多的字符,因此即使理解在任何情况下都是即时的,理解也需要更长的时间。您必须考虑这些成本。无论你通过让每一行变得更简单而获得什么,都会失去这种效果。

那就是说,以下版本可以说是更干净,正是因为它将算法的两个部分分成不同的部分:

def scrabble_score(word, scoretable):
    letter_scores = [scoretable.get(letter, 0) for letter in word]
    return sum(letter_scores)

但差异相当小,这是相当主观的。两者都很好,比你的第一个版本要好得多。 (哦,另一件事:我使用生成器表达而不是列表理解,我只保持列表理解以最小化噪声。)

答案 1 :(得分:1)

这些事情主要是品味问题。应该记住,品味本身就是一种教育和经验问题 - 你会发现更长,更具可见性的代码更具可读性,因为这是你学会阅读的唯一内容(到目前为止)。

我发现你的第二种形式更具可读性,因为我习惯了功能风格的形式,并且我理解其中的所有元素。我经常发现简洁的haskell代码难以辨认,因为我还没有学会所有的运算符。

实际的解决方案在我看来:(a)使用注释(b)避免任何过于聪明的事情(c)解释你选择做的任何聪明的事情(d)简洁和不缩进通常比大量和深刻的缩进更容易阅读; (e)记住你的观众。

将这些原则应用于这种情况:在python中,您可以期望通过简单的功能操作表达的事物以这种方式表达,因此您的受众应该对此感到满意。在任何情况下,您的代码都需要注释来解释您是否打算(或不要)默默地忽略无效(未标记)的字符。

答案 2 :(得分:1)

我认为简洁的风格很好,因为你可以在这种情况下自由扩展它,而不会在控制结构上浪费大量的垂直空间,就像第一个中的if一样。例如

def scrabble_score(word, scoretable):
    # local function to look up a score in the table
    def get_score(x): return scoretable.get(x,0)

    scores = map(get_score, word)  # convert word into a list of scores

    return sum(scores)

我发现它最具可读性,即使它需要更多行。我认为这是陈述式的。