结构模式匹配和无限
Structural pattern matching and infinity
我正在计算非负 p 的 Lp 距离函数。对于除 p = 0 和 p = ∞ 之外的所有情况,内置的 pow()
函数效果很好。
在了解结构模式匹配之前,我使用过字典和异常处理:
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)
有些人不想在这里有例外。所以我将片段重写为以下片段:
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)
为什么case inf:
不正确(Python v3.10.2)?
在 case
语句中,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 set NAME = <subject>
.
In simple terms NAME1.NAME2
will succeed only if <subject> == NAME1.NAME2
Using just case inf:
意味着要匹配的值被无条件地分配给名称 inf
– 名称是否先前绑定并不重要。
您想要的是 case math.inf:
,这意味着要与该值进行比较。
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)
正如其他受访者所指出的,您不能直接使用 inf,因为那是一种捕获模式。显而易见的解决方案是使用带点查找的 value 模式;但是,仅适用于正无穷大。
要处理其他所有特殊值,例如负无穷大和 NaN,您需要守卫:
match x:
case 1.0: ... # Exact value (literal pattern)
case 0.0: ... # Exact value (literal pattern)
case _ if math.isfinite(x): ... # Normal cases
case _ if math.isnan(x): ... # NaNs defy equality tests
case _: ... # Negative infinity
我正在计算非负 p 的 Lp 距离函数。对于除 p = 0 和 p = ∞ 之外的所有情况,内置的 pow()
函数效果很好。
在了解结构模式匹配之前,我使用过字典和异常处理:
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)
有些人不想在这里有例外。所以我将片段重写为以下片段:
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)
为什么case inf:
不正确(Python v3.10.2)?
在 case
语句中,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:
意味着要匹配的值被无条件地分配给名称 inf
– 名称是否先前绑定并不重要。
您想要的是 case math.inf:
,这意味着要与该值进行比较。
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)
正如其他受访者所指出的,您不能直接使用 inf,因为那是一种捕获模式。显而易见的解决方案是使用带点查找的 value 模式;但是,仅适用于正无穷大。
要处理其他所有特殊值,例如负无穷大和 NaN,您需要守卫:
match x:
case 1.0: ... # Exact value (literal pattern)
case 0.0: ... # Exact value (literal pattern)
case _ if math.isfinite(x): ... # Normal cases
case _ if math.isnan(x): ... # NaNs defy equality tests
case _: ... # Negative infinity