1
\$\begingroup\$

Edit: Ok, this was a disaster of a question. I'll make sure to use timeit in the future

I solved a Codewars problem that had the user check if a number was a palindrome, was incrementing, and etc. After I solved it, I noted how the top solutions were much more simple, intuitive, and elegant than mine, although they were less efficient. I figured this was because code golf is more important in Codewars than efficiency, but after Googling around it seemed to me that perhaps a logically intuitive and elegant solution was more important in Python than an efficient but less elegant solution. Is there any truth to this?

To make this post more appropriate for the scope of Code Review, I will include two solutions for two problems, is_palindrome and is_incrementing. The first solution is the more elegant and logically intuitive one, and the second solution is my attempt at a more efficient one. I would like to know which solution is more appropriate for Python, and also what improvements could be made to my solution.

In Python, it is regularly suggested to use the following to determine if a string is a case-sensitive palindrome:

def is_palindrome(word):
    return word == word[::-1]

Here is my attempt at a more efficient way to accomplish this:

def is_palindrome(word):
    low = 0
    high = len(word) - 1
    while low < high:
        if word[low] != word[high]:
            return False
        low += 1
        high -= 1
    return True

As another example, to determine if a number is incrementing, such as 234 but not 231 it is suggested to use:

def is_incrementing(number):
    return str(number) in '0123456789'

Instead of something like:

def is_incrementing(number)
    number = str(number)
    prev = number[0]
    for char in number[1:]:
        if int(char) != int(prev) + 1:
            return False
        prev = str(int(prev) + 1)
    return True
\$\endgroup\$
  • 1
    \$\begingroup\$ Have you tested if yours are more efficient? The first is_palindrome took 1/4 the amount of time as your 'efficient' one, when I timed them. \$\endgroup\$ – Peilonrayz Nov 13 '16 at 23:05
  • \$\begingroup\$ To echo @Peilonrayz comment, even by changing first = str(int(first) + 1), which makes no sense, into prev = char, your is_incrementing is (again) 4 times slower than the first one. Even trying to be smart by not converting back and forth between strings and integer but rather using division and modulus, you end up with something 3 times slower than str(number) in '0123456789'. \$\endgroup\$ – Mathias Ettinger Nov 14 '16 at 0:23
  • \$\begingroup\$ Also note that Python is not C and that what you might consider efficient takes time for the interpreter to execute. On the other hand, short and clean solutions often leverage fast operations implemented in C. \$\endgroup\$ – Mathias Ettinger Nov 14 '16 at 0:39
  • \$\begingroup\$ @Peilonrayz facepalm the pythonic way is dramatically faster, which I found out after using timeit. \$\endgroup\$ – Matthew Moisen Nov 14 '16 at 1:06
  • \$\begingroup\$ @MathiasEttinger sorry that was a spelling error. Yes I timed it after your and Peilonrayz feedback and concur. \$\endgroup\$ – Matthew Moisen Nov 14 '16 at 1:08
5
\$\begingroup\$

If we look at your four different functions, you should be able to see that two are simple and easy to read, this goes in line with Pythons ideas, "simple is better than complex" - zen of Python. This is good as it means that others can read and understand your code with ease. This is a benift to both you and others. It also means if you need to change the functionality slightly, it's simple and easy. With your optimised function they take time to read and understand. This is a trade off, either you have fast code that is hard to understand, or slow but easy to understand code. And so from a human point of view, we prefer slower code.

I guess you, like I, prefer fast code. This leads to problems. Say we are tasked to make a program, from day 1 we'll be using hard to read code to make something simple. This at first is ok, but then it effects the overall structure of the project. And slowly we've made a monster, a hard to read monster. This is why premature optimisations are bad.

Instead if you don't worry about speed from the beginning, then you don't fall into this problem. And when you need to increase the performance of your code you can then swap out the old slow code for new fast code, only when it's needed. Sure you have to write a function twice, but at least we don't need to write the entire program again.

There is also another benefit to using simple code. When you're implementing the optimisation you have a base function to time against. This is good as then you can easily tell if your code is an optimisation, or just added complexity. Your function for example show this, if you time them both against their 'simple' version, you'll find, you haven't optimised the code, you've only obfuscated the code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.