I don't know if a single line is possible—or even a good idea—but let's do some refactoring and see what we can do. Whenever you're staring at a big block of code and wondering how to make it better, don't expect the solution to just pop out at you in some flash of insight. The best approach is to refactor it slowly, one small change at a time. Just make it better bit by bit until you run out of things to improve.
First, let's reorganize it a bit. z=1
is only needed inside the if
block so we'll move it there.
def preveri_zaporedje(besede):
if ni_ponavljanj(besede):
z=1
while z<len(besede):
if lahko_sledi(besede[z-1],besede[z]):
z+=1
else:
return False
else:
return False
return True
We can reverse the if
statement and exit early if it fails. That'll reduce the level of indentation.
def preveri_zaporedje(besede):
if not ni_ponavljanj(besede):
return False
z=1
while z<len(besede):
if lahko_sledi(besede[z-1],besede[z]):
z+=1
else:
return False
return True
Let's replace the manual looping with z
with a range-based loop. That'll be more Pythonic.
def preveri_zaporedje(besede):
if not ni_ponavljanj(besede):
return False
for z in range(1, len(besede)):
if lahko_sledi(besede[z-1],besede[z]):
pass
else:
return False
return True
Then we can apply the same invert-the-if trick as before.
def preveri_zaporedje(besede):
if not ni_ponavljanj(besede):
return False
for z in range(1, len(besede)):
if not lahko_sledi(besede[z-1],besede[z]):
return False
return True
Now comes a tricky bit. The code is looping over pairs of elements from the input list. There's a clever way to loop over pairs using zip
that will let us get rid of the z
variable. See the link for details.
def preveri_zaporedje(besede):
if not ni_ponavljanj(besede):
return False
for a, b in zip(besede[:-1], besede[1:]):
if not lahko_sledi(a, b):
return False
return True
This pattern looks familiar. It's checking if any of the pairs of items fail the lahko_sledi
test. Let's make that any test explicit, eh?
def preveri_zaporedje(besede):
if not ni_ponavljanj(besede):
return False
if any(not lahko_sledi(a, b) for a, b in zip(besede[:-1], besede[1:]))
return False
return True
And that can be condensed to:
def preveri_zaporedje(besede):
if not ni_ponavljanj(besede):
return False
return not any(not lahko_sledi(a, b) for a, b in zip(besede[:-1], besede[1:]))
Now we're getting somewhere!
Here comes another tricky bit. If you remember your first-order logic then you'll recall that any(not P)
is the same as not all(P)
:
def preveri_zaporedje(besede):
if not ni_ponavljanj(besede):
return False
return not not all(lahko_sledi(a, b) for a, b in zip(besede[:-1], besede[1:]))
Cancel out the double not
s:
def preveri_zaporedje(besede):
if not ni_ponavljanj(besede):
return False
return all(lahko_sledi(a, b) for a, b in zip(besede[:-1], besede[1:]))
Finally, we can add the first test to the result. There we go! A single line.
def preveri_zaporedje(besede):
return ni_ponavljanj(besede) and all(lahko_sledi(a, b) for a, b in zip(besede[:-1], besede[1:]))
Well how about that, it is possible.
Is it a good idea? I suppose that's for you to decide. Readability is a judgment call. Shorter isn't always better. That's another good reason to refactor step by step: you may find that one of the steps along the way was actually more readable than the end result.