Functional and imperative style in Python

Recently, I spent some free time trying to wrap my head around Haskell and functional programming in general. One of my main concerns was (and continues to be) what I perceive as the unreadability of short expressions in a functional style (whether in Haskell or in any other language).

Here is an example. I just did the following conversion in some code to 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 much more satisfactory for writing (and given that I myself wrote it to read too). It is shorter and sweeter and does not declare unnecessary variables that can become a headache if I am mistaken in writing code. (By the way, I understand that I could use the dict get() method in an imperative style function, but I realized this when I did this conversion, so I left it this way in the example.)

My question is: despite all this, is the latest version of the code really more readable than the first? It looks like a more monolithic expression that needs to be parsed right away, compared to the previous version, where you have a chance to build the value of the block from smaller, more atomic fragments. This question comes from my disappointment, trying to decipher Haskell's supposedly easy-to-understand features, as well as my insistence as TA for first-year CS students, which is one of the main reasons we write code (which is often overlooked) is to communicate with ideas with other programmers / computer scientists. I’m nervous that the style of patience looks like a write-only code and therefore wins the goal of communication and understanding of the code.

+7
python coding-style functional-programming
source share
3 answers

My question is: despite all this, is the latest version of the code more readable than the first? This seems like a more monolithic expression that must be deceived immediately.

I would say that this is a more readable, although not necessarily the most readable option. Yes, this is more logical on one line, but it is outweighed by three facts.

Firstly, you can still mentally decompose it into smaller parts and understand them individually before combining them. I immediately see that you are summing something up (at this point I do not need to know what you are summing up). I also see that you get entries from the scoretable for each letter , the default is 0 if not (at this point I do not need to know what is happening with this list). These things can be identified and understood independently. Secondly, very few, several independent parts, maybe two or three. It is unreasonable to immediately realize that the "monolithic" aspect is more of a problem when dozens of problems sit next to each other.

Finally, the perceived advantage of the first version,

compared to the previous version, where you have a chance to create a block value from smaller, more atomic fragments.

also a problem: when I start reading, I see a for loop. This is good, but in reality it does not tell me anything yet (except that there is a cycle that does not help me understand the logic, but only its implementation). Therefore, I must remember this and read further, in order to later decide what this cycle means. To some extent, this applies to any code, but it gets worse as more code. Similarly, the score = 0 line does not mean anything in itself, it is just one tiny implementation detail that no one needs to know to understand that this function calculates the scrabble word score by adding a score for each of the letters of the word.

When you read your imperative cycle, you need to keep track of a lot of mutable variables, control flow, etc., to figure out what is going on, and you need to combine the parts. In addition, it is simply more characters to read, and therefore it will take more time to understand, even if the understanding was instantaneous anyway. You must consider these costs. Whatever you win, if every line was easier, it was lost.

However, the next version is possible , precisely because it divides 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 quite small, it is quite subjective. Both are beautiful and much better than your first version. (Oh, one more thing: I would use a generator expression, not a list comprehension, I just kept the list comprehension to minimize noise.)

+6
source share

This is primarily a matter of taste. It should be remembered that taste itself is a matter of education and experience - you will find a longer, imperative code more readable, because this is the only thing you have learned to read (for now).

I find your second form more readable, because I'm used to the functional style form, and I understand all the elements in it. I often find the haskell short code code illegible because I did not learn all the operators.

It seems to me that a practical solution: (a) to use comments (b) to avoid anything overly smart (c) to explain something smart that you prefer to do (d) short and unreasonable is usually easier to read than voluminous and deep indentation; and (e) remember your audience.

To apply these principles to this case: in python, you expect things that can be expressed through simple functional operations to be expressed in this way, and therefore your audience should be happy with that. In any case, your code needs comments to explain that you (or don't want to) intend to silently ignore invalid (insecure) characters.

+1
source share

I think the short style is good, because you can expand it in cases like this without spending a lot of vertical space on the control structure, for example, if in the first. For example,

 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, although it requires a few more lines. I believe that this is in a declarative style.

+1
source share

All Articles