I am computing the Lp distance functions for non-negative p‘s. For all but p = 0 and p = ∞ a built-in pow()
function serves well.
Before I learned about a structural pattern matching, I had used a dictionary and exception handling:
from math import sqrt, inf distance_function = { 0.0: lambda x, y: int(x != 0.0) + int(y != 0.0), 1.0: lambda x, y: abs(x) + abs(y), # Likely a tad faster than 'pow()' inf: lambda x, y: max(abs(x), abs(y))} def lp_distance(x, y, p): try: return distance_function[p](x, y) except KeyError: return pow(pow(abs(x), p) + pow(abs(y), p), 1.0/p)
Some people didn’t want exceptions here. So I rewrote the snippet into the following one:
def lp_distance(x, y, p): match p: case 0.0: return int(x != 0.0) + int(y != 0.0) case 1.0: return abs(x) + abs(y) # The line below triggers "SyntaxError: name capture 'inf' makes remaining patterns unreachable" case inf: return max(abs(x), abs(y)) # But the following works: case p if p == inf: return max(abs(x), abs(y)) case _: return pow(pow(abs(x), p) + pow(abs(y), p), 1.0/p)
Why case inf:
is not correct (Python v3.10.2)?
Advertisement
Answer
In a case
statement, a simple name is a pattern that captures (assigns) to that name. In contrast, a dotted name is a patterns that refers to the value of that name.
In simple terms
NAME
will always succeed and it will setNAME = <subject>
.
In simple terms
NAME1.NAME2
will succeed only if<subject> == NAME1.NAME2
Using just case inf:
means that the value to match is unconditionally assigned to the name inf
– it does not matter if the name was previously bound.
What you want instead is case math.inf:
, which means to compare against this value.
import math def lp_distance(x, y, p): match p: case 0.0: return int(x != 0.0) + int(y != 0.0) case 1.0: return abs(x) + abs(y) # compare against a value by using its dotted name case math.inf: return max(abs(x), abs(y)) case _: return pow(pow(abs(x), p) + pow(abs(y), p), 1.0/p)