10
\$\begingroup\$

I have a problem that I solved in 2 different ways and I would like to know which one is better, cleaner and easier to read.

Problem: Write a while loop that starts at the last character in the string and works its way backwards to the first character in the string, printing each letter on a separate line.

Example 1:

char = 'Wednesday'
cont = -1
cont2 = len(char)
cont3 = len(char) + 1
while cont < cont2:
    print(char[cont2:cont3])
    cont2 = cont2 - 1
    cont3 = cont3 - 1

Example 2:

char = 'Wednesday'
cont = len(char) - 1
while cont >= 0:
    cont2 = char[cont]
    print(cont2)
    cont -= 1
\$\endgroup\$
3
  • 2
    \$\begingroup\$ You should use better variable names. They should be more descriptive. In example 2, I'd suggest string instead of char, i instead of cont, and char instead of cont2. \$\endgroup\$ Sep 7 '20 at 18:14
  • 2
    \$\begingroup\$ If you just want to print a word backwards with each character being on a new line you could use a for loop and a slice that steps backwards: for character in word[::-1]: print(character) \$\endgroup\$
    – whme
    Sep 8 '20 at 13:40
  • 2
    \$\begingroup\$ Similar to @whme, you could even go for print(*reversed(word), sep = '\n'). That is: reverse the word, unpack it, i.e, split into letters and print every letter separated by a linebreak. \$\endgroup\$ Sep 8 '20 at 16:25
16
\$\begingroup\$

Looking at the two implementations side by side, ignoring the goal of the exercise and alternative implementations, I'd say the second example is better:

  • It uses one fewer variables.
  • It does a simple index lookup rather than a slice.
  • The implementation is easier to follow.

For simple problems like this it's often useful to think of the complexity of the solution as the number of "tokens", or distinct language elements, necessary to write it down. For example, print(cont2) is four tokens: print, (, cont2 and ). By a quick read the first example has about twice as many tokens as the second.

\$\endgroup\$
13
\$\begingroup\$

The shorter code, the better (as long as it does not goes into extreme which makes the code obfuscated). So the second solution is better - it's using fewer lines and variables to achieve the same goal.

Here's a small nitpick, which might sound like an overreaction advice for this simple task, but every massive codebase once started as a simple thing: use better variable names.

  • char is usually a reference to a single character, however in your case it's the whole string, which might be a bit confusing.

  • cont is abbreviation for what?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I second the variable naming. It's exactly what I would have written if you didn't it before. \$\endgroup\$ Sep 7 '20 at 15:39
5
\$\begingroup\$

Sure, the second answer is better, but neither answer looks like code that a fluent Python programmer would write.

The telltale sign: both answers fail to take full advantage of the fact that strings are sequences, like lists and tuples. Among other things, this means that strings are directly iterable.

word = 'Wednesday'

# No: too much red tape.

for i in range(len(word)):
    print(word[i])

# Yes: nice and simple.

for char in word:
    print(char)

In addition to being directly iterable, sequences are sortable and reversible.

for char in reversed(word):
    print(char)

Or even:

print('\n'.join(reversed(word)))

And it you absolutely must use a while loop, perhaps this will do the trick. Yes, it's dumb, but it's still simpler and easier to understand than the bureaucracy of accessing characters in a string by index.

while True:
    print('\n'.join(reversed(word)))
    break
\$\endgroup\$
7
  • \$\begingroup\$ Good answer. That's definitely more Pythonic. It is not a while loop though (OP's context). \$\endgroup\$ Sep 7 '20 at 18:18
  • \$\begingroup\$ @theonlygusti Point taken ... but who gives that assignment? Ugh! Added an edit to satisfy the critics. ;-) \$\endgroup\$
    – FMc
    Sep 7 '20 at 18:23
  • \$\begingroup\$ Haha I know right, probably the purpose is to get students to think about looping backwards, but they should have assigned a language other than Python... do we ever have to loop backwards in Python... \$\endgroup\$ Sep 7 '20 at 18:31
  • \$\begingroup\$ Even shorter and dumber: while print('\n'.join(reversed(word))): pass. Anyway, I think the assignment is legitimate. Everybody should know iteration with a while-loop like that. If people iterate iterables only ever with for loop "magic", they'll become the kind of people who ask why removing elements from the list they're iterating doesn't work as intended. \$\endgroup\$ Sep 7 '20 at 19:24
  • \$\begingroup\$ it was an assignment from "Python for everybody" with Dr. Charles R. \$\endgroup\$ Sep 7 '20 at 20:47
3
\$\begingroup\$

As other have pointed out descriptive variable naming will improve readability a great deal. Things like char allude to a character, but that's vague and needlessly shortened.

In a programmatic mindset of looking for optimizations it can be easy to try to reduce the size of code, but the length of variable names are of no consequence. It's more important their name be unambiguous and understandable from a glance than it be short.

A for loop can represent, among other things, operating on a set of items interacting with each on in turn. A while loop can represent, among other things, consuming a resource one item at a time. Perhaps it is the latter that this exercise was mean to demonstrate, thus the requirement of using a while loop instead of any loop.

word = "Wednesday"
while word:
    print(word[-1])
    word = word[:-1]

while word:

An empty sequence (list with no items, string with no characters) is evaluated to False for things like While loop conditions. This is equivalent to "while the string 'word' is not an empty string."

print(word[-1])

This project seems to be a fine way to explore string slicing. On the python website's "An informal introduction to Python" string slicing is introduced and it is shown that getting index -1 of a string or other sequence returns the last item.

word = word[:-1]

Slicing with [:-1] will get everything except the last item.

You can combine these to continue printing word, trimming off what was printed, until there is nothing left to print. If the word was actually a list (and you could make it one if you really wanted with list(word)) you could use the list.pop function which you can find more on here. In that case this code may look more like this:

char = ["w","e","d","n","e","s","d","a","y"]
# or
char = list("Wednesday")

while char:
    print(char.pop())
\$\endgroup\$
-1
\$\begingroup\$

Another reason the first one is worse is that it prints an extra empty line at the beginning due to your off-by-one error. The only reason you don't get an error is that Python plays nice when you ask for out-of-bounds slices and gives you an empty string.

\$\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.