0

I wrote a function:

# given a n x m grid return how many different ways there are to move from top left to 
# bottom right by only being able to move right or down

def grid(n, m, memo = {}):
    if f'{n},{m}' in memo:
        return memo[f'{n},{m}']

    if n == 1 and m == 1:
        return 1
    
    if n == 0 or m == 0:
        return 0

    memo[f'{n},{m}'] = grid(n,m-1,) + grid(n-1,m)

    return grid(n,m-1,) + grid(n-1,m)

Recently I read a bit about short-circuiting in Python and I am trying to understand it further.

As I understand it does not provide any boost in runtime, just sort of syntax sugar.

For example:

1 < 2 < 3 # is True
1 < 2 and 2 < 3 # is also True

# hence
(1 < 2 < 3) == 1 < 2 and 2 < 3 # is True

I was wondering can I write my function with this kind of short-circuiting in my if statements?

I came up with this:

 def grid(n, m, memo = {}):
    if f'{n},{m}' in memo:
        return memo[f'{n},{m}']

    if (n or m) == 1:
        return 1
    
    if (n and m) == 0:
        return 0

    memo[f'{n},{m}'] = grid(n,m-1,) + grid(n-1,m)

    return grid(n,m-1,) + grid(n-1,m)

Is there any smarter way of using the short-circuit here?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Jonas Palačionis
  • 4,591
  • 4
  • 22
  • 55
  • 2
    `if (n or m) == 1` is definitely *not* the same thing as `if n == 1 or m == 1`. – Booboo Jan 04 '21 at 17:53
  • So how would this be written in such fashion: is n or m equal to 1? Without using `n == 1 or m == 1`? – Jonas Palačionis Jan 04 '21 at 17:59
  • See my answer below. – Booboo Jan 04 '21 at 18:00
  • 1
    The and test: `if n == 1 and m == 1:` can be converted to `if n == m == 1:`. – thebjorn Jan 04 '21 at 18:32
  • `1 < 2 < 3` is not [short-circuiting](https://docs.python.org/3/library/stdtypes.html?highlight=short-circuit#boolean-operations-and-or-not), it's actually [chaining](https://docs.python.org/3/reference/expressions.html?highlight=chained#index-78), which is sort of the opposite in some respects. – wjandrea Jan 04 '21 at 18:56
  • Possible duplicate: [How to test multiple variables against a value?](https://stackoverflow.com/questions/15112125/how-to-test-multiple-variables-against-a-value) – wjandrea Jan 04 '21 at 19:08

3 Answers3

3

(1 < 2 < 3) is not short-circuiting - I think you misunderstood the meaning of the term. You are correct that it is merely syntax sugar - although it can produce some very weird results in obscure cases. (1 < 2 < 3) expands to (1 < 2) and (2 < 3) - the middle operand is copied to both and and is used for the joining operator.

Short circuiting occurs when python already knows the answer to a boolean expression, even before calculating both the inputs. For example

def false():
    print("false")
    return False

def true():
    print("true")
    return True

print(false() and true())

The output would be

false
False

Because when python sees False and, it already knows that the result is False (because and requires both operands to be True), so it doesn't bother running the second half of the line. This real short circuiting does result in a performance boost, but since you can't turn off short circuiting, it doesn't really matter ¯\_(ツ)_/¯

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Hack5
  • 3,244
  • 17
  • 37
  • 2
    You are contradicting yourself... `(1 < 2 < 3)` expands to `(1 < 2) and (2 < 3)` so ***there is short-circuiting***... If `1 < 2`, then `2 < 3` will not be evaluated at all... And also - from the [docs](https://docs.python.org/3/reference/expressions.html#comparisons): *"`x < y <= z` is equivalent to `x < y and y <= z`, except that __`y` is evaluated only once__ (but in both cases __`z` is not evaluated at all when `x < y` is found to be false__"* - so there is your improvement, not *merely syntax sugar*... – Tomerikoo Jan 04 '21 at 18:00
  • Ok that's fair. I did read it, a few weeks ago, but I forgot. – Hack5 Jan 04 '21 at 18:38
1
if (n or m) == 1

evaluates to

if (<bool>) == 1  # e.g. if {True,False} == 1

which is probably not what you want, since it is essentially evaluating the truthiness of n or m.

Your existing code already captures the nature of short-circuiting;

if n == 1 and m == 1

will only evaluate the second argument m == 1 iff n == 1, or will otherwise short-circuit.

To your comment

As I understand it does not provide any boost in runtime, just sort of syntax suggar.

Well, actually it does provide a runtime boost if Python is able to skip evaluating what would otherwise be "expensive" conditions to evaluate because it is able to short-circuit early.

Brad Solomon
  • 38,521
  • 31
  • 149
  • 235
1

if (n or m) == 1 is definitely not the same thing as if n == 1 or m == 1. The first if statement is equivalent to:

value = n
if not value:
    value = m
if value == 1:
    # do something:

Or expressed more succinctly:

if (n if n else m) == 1:
       # do something

In other words, n or m only evaluates m if n is False (or 0 if n is an integer), otherwise the result of the expression is n.

What you want to avoid redundancy is:

if 1 in (n, m): # equivalent to: if 1 is either n or m:

Update: Demo

n = 4
m = 1
if (n or m) == 1:
    print('if branch taken')
else:
    print('else branch taken')

Prints:

else branch taken
Booboo
  • 38,656
  • 3
  • 37
  • 60
  • I believed I used `(n or m) == 1` correct in my case, isnt this the same as `If either n or m is 1: do something`. Because both `(1 or 0) ` and `(0 and 1)` return `1` which I after compare to `1`? – Jonas Palačionis Jan 04 '21 at 18:05
  • 2
    I am talking about the general case for arbitrary values n and m. If you know the values of n and m, why bother even having an if statement? Then the whole question becomes rather pointless. – Booboo Jan 04 '21 at 18:07
  • Equivalently, `if n == 1 and m == 1:` can be `if (m, n) == (1, 1):` (or `m == n == 1`...) – Tomerikoo Jan 04 '21 at 18:07
  • As `m` and `n` can only be integers and I only care if any of those are 1, if so, return 1. So `if (n or m) == 1: return 1` returns me 1 if either `m` or `n` is 1 which is what I need. Or am I missing something? – Jonas Palačionis Jan 04 '21 at 18:10
  • @Tomerikoo I think I would prefer `if n == 1 == m:`, which avoids creating a tuple. – Booboo Jan 04 '21 at 18:13
  • 1
    @JonasPalačionis If n is 4 and m is 1 then `(n or m)` evaluates to `4` which will *not* be equal to `1` and the if expression will evaluate to `False`. You are definitely missing something. Read my answer again. – Booboo Jan 04 '21 at 18:16