-4

I would like to convert this multi-line function to a single line.

def preveri_zaporedje(besede):
    z=1
    if ni_ponavljanj(besede):
        while z<len(besede):
            if lahko_sledi(besede[z-1],besede[z]):
                z+=1
            else:
                return False
    else:
        return False
    return True
John Kugelman
  • 349,597
  • 67
  • 533
  • 578

2 Answers2

3

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 nots:

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.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
0

Well, I'm sure you can rewrite your function not in one line, but it will be a lot of more readable. This is one line implementation:

def preveri_zaporedje(besede):
    return True if ni_ponavljanj(besede) and len([z for z in range(len(besede)) if lahko_sledi(besede[z-1],besede[z])]) + 1 == len(besede) else False
Jefferson Houp
  • 858
  • 1
  • 7
  • 17