简体   繁体   English

Python中的功能与命令式风格

[英]Functional vs. imperative style in Python

I've been spending some of my spare time lately trying to wrap my head around Haskell and functional programming in general. 我最近花了一些业余时间试图围绕Haskell和函数式编程。 One of my major misgivings has been (and continues to be) what I perceive to be the unreadability of terse, functional-style expressions (whether in Haskell or any other language). 我的一个主要疑虑是(并且仍然是)我认为是简洁的,功能性的表达式(无论是在Haskell还是任何其他语言中)的不可读性。

Here's an example. 这是一个例子。 I just made the following transformation in some code for work: 我刚刚在一些代码中进行了以下转换:

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

to

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

The latter was a lot more satisfying to write (and, keeping in mind that I myself am the one who wrote it, to read too). 后者写作更令人满意(并且,请记住,我自己也是写作的人,也是读者)。 It's shorter and sweeter and declares no pesky variables that might become a headache if I made any mistakes in typing out the code. 如果我在输入代码时犯了任何错误,它会更短更甜,并且不会声明任何令人头疼的变量。 (BTW I realize I could have used the dict's get() method in the imperative-style function, but I realized it as I was performing this transformation, so I left it that way in the example.) (顺便说一下,我意识到我可以在命令式风格的函数中使用dict的get()方法,但是当我执行这个转换时我意识到这一点,所以我在示例中就这样离开了。)

My question is: despite all that, is the latter version of the code really more readable than the former? 我的问题是:尽管如此,代码的后一版本是否比前者更具可读性 It seems like a more monolithic expression that has to be grokked all at once, compared to the former version, where you have a chance to build up the meaning of the block from the smaller, more atomic pieces. 与前一版本相比,它似乎是一个更加单一的表达方式,与之前的版本相比,你有机会从更小,更原子的部分构建块的含义。 This question comes from my frustration with trying to decode supposedly easy-to-understand Haskell functions, as well as my insistence as a TA for first-year CS students that one of the major reasons we write code (that's often overlooked) is to communicate ideas with other programmers/computer scientists. 这个问题来自于我试图解码所谓的易于理解的Haskell函数,以及我作为一年级CS学生的TA的坚持,我们编写代码的主要原因之一(经常被忽视)是沟通与其他程序员/计算机科学家的想法 I am nervous that the terser style smacks of write-only code and therefore defeats the purpose of communication and understanding of code. 我很紧张,terser风格是只写代码,因此违背了沟通和理解代码的目的。

My question is: despite all that, is the latter version of the code really more readable than the former? 我的问题是:尽管如此,代码的后一版本是否比前者更具可读性? It seems like a more monolithic expression that has to be grokked all at once 这似乎是一个更加整体的表达,必须同时被一个人弄清楚

I would say it's more readable, though not necessarily the most readable option. 我会说它更具可读性,但不一定是最具可读性的选择。 Yes, it's more logic in one line, but this is outweighed by three facts. 是的,它在一条线上更具逻辑性,但这被三个事实所抵消。

First, you can still mentally decompose it into smaller parts and understand those individually before combining them. 首先,您仍然可以精神上将其分解为更小的部分,并在组合它们之前单独理解它们。 I immediately see that you're summing something (at this stage I don't have to know what you're summing). 我立即看到你在总结一些东西 (在这个阶段我不需要知道你在总结什么 )。 I also see that you get the entries from scoretable for each letter , defaulting to 0 if not present (at this stage I don't have to know what then happens to this list). 我还看到你从每个letter scoretable表中得到条目,如果不存在则默认为0(在这个阶段我不必知道这个列表会发生什么)。 These things can be identified and understood independently. 这些东西可以独立识别和理解。 Second, there are very few somewhat-independent parts, maybe two or three. 其次,很少有一些独立的部分,可能是两个或三个。 This is not unreasonable to understand at once, the "monolithic" aspect is more of a problem when dozens of concerns sit next to each other. 立即理解这并不是不合理的,当几十个问题彼此相邻时,“单片”方面更成问题。

Lastly, the supposed advantage of the first version, 最后,第一个版本的假设优势,

compared to the former version, where you have a chance to build up the meaning of the block from the smaller, more atomic pieces. 与之前的版本相比,您有机会从更小,更原子的部分构建块的含义。

is also a problem: When I start reading, I see a for loop. 也是一个问题:当我开始阅读时,我看到一个for循环。 That's nice, but it doesn't actually tell me anything yet (except that there is a loop, which doesn't help me understand the logic, only its implementation). 这很好,但它实际上并没有告诉我任何东西(除了有一个循环,这不能帮助我理解逻辑,只有它的实现)。 So I have to keep this fact in mind and read on to later resolve what this loop means . 所以我必须记住这个事实并继续阅读以后解决这个循环的含义 To an extent, this is true of any code, but it gets worse the more code there is. 在某种程度上,任何代码都是如此,但是代码越多,代价越大。 Likewise, the score = 0 line doesn't mean anything on its own, it's just one tiny implementation detail that nobody needs to know to understand that this function computes a word's scrabble score by adding up the score for each of the word's letters. 同样地, score = 0线本身并不意味着什么,它只是一个很小的实现细节,没有人需要知道这个函数通过将每个单词的字母的分数相加来计算单词的拼字比分。

When reading your imperative loop, one has to keep track of multiple mutable variables, control flow, etc. to figure out what happens, and has to piece the parts together. 在阅读命令性循环时,必须跟踪多个可变变量,控制流程等,以确定发生了什么,并且必须将这些部分拼凑在一起。 In addition, it's simply more characters to read, and hence it would take longer to comprehend even if understanding was instant in any case. 此外,它只是阅读更多的字符,因此即使理解在任何情况下都是即时的,理解也需要更长的时间。 You have to take these costs into account. 您必须考虑这些成本。 Whatever you might gain by having each line be simpler is lost to this effect. 无论通过让每条线更简单而获得什么,都会失去这种效果。

That said, the following version is arguably cleaner, precisely because it splits the two parts of the algorithm into separate lines: 也就是说,以下版本可以说更清晰,正是因为它将算法的两个部分分成不同的部分:

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

But the difference is rather small, it's rather subjective. 但差异相当小,相当主观。 Both are fine and much better than your first version. 两者都很好,比你的第一个版本要好得多。 (Oh, another thing: I'd use a generator expression rather than a list comprehension, I only kept the list comprehension to minimize noise.) (哦,另一件事:我使用生成器表达而不是列表理解,我只保持列表理解以最小化噪声。)

These things are primarily a matter of taste. 这些东西主要是品味问题。 It should be remembered that taste is itself a matter of education and experience - you find longer, imperative code more readable because that is the only thing you have learned to read (so far). 应该记住,品味本身就是教育和经验的问题 - 你会发现更长,更具可见性的代码更具可读性,因为这是你学会阅读的唯一内容(到目前为止)。

I find your second form more readable because I'm used to the form of functional style, and I understand all of the elements in it. 我发现你的第二种形式更具可读性,因为我习惯了功能风格的形式,并且我理解其中的所有元素。 I frequently find concise haskell code indecipherable because I haven't learned all of the operators. 我经常发现简洁的haskell代码难以辨认,因为我还没有学习所有的运算符。

The practical solution seems to me: (a) use comments (b) avoid anything excessively clever (c) explain anything clever you choose to do (d) concise and unindented is usually easier to read than voluminous and deeply indented; 在我看来,实际的解决方案是:(a)使用注释(b)避免任何过于聪明的事情(c)解释你选择做的任何聪明的事情(d)简洁和不缩进通常比大量和深刻的缩进更容易阅读; and (e) remember your audience. (e)记住你的观众。

To apply those principles to this case: in python you would expect things that can be expressed through simple functional operations to be expressed in that way, and so your audience should be comfortable with it. 将这些原则应用于这种情况:在python中,您可以期望通过简单的功能操作表达的事物以这种方式表达,因此您的受众应该对此感到满意。 Your code in any case needs a comment to explain that you do (or don't) intend to silently ignore invalid (unscored) characters. 在任何情况下,您的代码都需要注释来解释您是否(或不)打算默默地忽略无效(未标记)字符。

I think the concise style is nice because you are free to expand it in cases just like this one, without wasting lots of vertical space on control structures like for an if in the first. 我认为简洁的风格是很好的,因为你可以在这种情况下自由扩展它,而不会在控制结构上浪费大量的垂直空间,就像第一个中的if一样。 For instance 例如

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)

I find this the most readable, even though it takes a few more lines. 我发现它最具可读性,即使它需要更多行。 I'd consider this to be in the declarative style. 我认为这是陈述式的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM