Răz*_*nda 5 haskell pattern-matching overlapping-matches
我目前对Haskell中模式重叠的理解是,如果传递给函数的某些参数值可以由多个模式匹配,则认为2个模式是重叠的.
鉴于:
last :: [a] -> a
last [x] = x
last (_ : xs) = last xs
Run Code Online (Sandbox Code Playgroud)
传递参数值[1]将匹配第一个模式[x]和第二个模式(_:xs) - 这意味着该函数具有重叠模式,即使两个模式都可以匹配.
令人困惑的是,虽然模式(通过上面的定义)重叠,但GHC没有显示任何关于它们重叠的警告.
恢复last
函数中的2个模式匹配确实显示重叠警告:
last :: [a] -> a
last (_ : xs) = last xs
last [x] = x
Run Code Online (Sandbox Code Playgroud)
警告:
src\OverlappingPatterns.hs:6:1: Warning:
Pattern match(es) are overlapped
In an equation for `last': last [x] = ...
Run Code Online (Sandbox Code Playgroud)
如果先前的模式使得无法匹配稍后发生的模式,那么GHC几乎就会考虑模式重叠.
确定函数是否具有重叠模式的正确方法是什么?
更新
我正在寻找overlapping pattern
fp101x课程中使用的定义.
根据fp101x中使用的定义,以下函数具有overlapping patterns
:
last :: [a] -> a
last [x] = x
last (_ : xs) = last xs
Run Code Online (Sandbox Code Playgroud)
这与GHC定义相矛盾,GHC定义overlapping pattern
不认为它具有任何重叠模式.
如果没有正确定义overlapping pattern
fp101x课程背景中的含义,就无法解决这个问题.那里使用的定义不是GHC的定义.
更新后的问题澄清了OP想要重叠模式的正式定义。这里的“重叠”是指 GHC 在发出警告时使用的含义:即,当它检测到某个 case 分支无法访问时,因为它的模式与先前分支尚未处理的任何内容都不匹配。
可能的正式定义确实可以遵循这种直觉。也就是说,对于任何模式,我们可以首先定义与 匹配的p
一组值(表示)。(为此,了解涉及的变量的类型很重要--取决于类型环境。)然后,我们可以说在模式序列中[[p]]
p
p
[[p]]
Gamma
q0 q1 ... qn p
Run Code Online (Sandbox Code Playgroud)
该模式p
是重叠的[[p]]
,当且仅当,作为一个集合,包含在 中[[q0]] union ... union [[qn]]
。
然而,上面的定义很难操作——它并没有立即产生检查重叠的算法。确实,计算[[p]]
是不可行的,因为它通常是一个无限集。
为了定义一个算法,我会尝试为任何模式“尚未匹配”的术语集定义一个表示q0 .. qn
。举个例子,假设我们使用布尔值列表:
Remaining: _ (that is, any list)
q0 = []
Remaining: _:_ (any non empty list)
q1 = (True:xs)
Remaining: False:_
p = (True:False:ys)
Remaining: False:_
Run Code Online (Sandbox Code Playgroud)
在这里,“剩余”集合没有改变,因此最后一个模式是重叠的。
另一个例子:
Remaining: _
q0 = True:[]
Remaining: [] , False:_ , True:_:_
q1 = False:xs
Remaining: [], True:_:_
q2 = True:False:xs
Remaining: [], True:True:_
q3 = []
Remaining: True:True:_
p = True:xs
Remaining: nothing -- not overlapping (and exhaustive as well!)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,在每个步骤中,我们都会将每个“剩余”样本与手头的模式进行匹配。这会生成一组新的剩余样本(可能没有)。所有这些样本的集合形成新的剩余集合。
为此,请注意,了解每种类型的构造函数列表非常重要。这是因为当与 匹配时True
,您必须知道还False
剩下另一种情况。同样,如果您匹配[]
,则还剩下另一种_:_
情况。粗略地说,当与 constructor 匹配时K
,相同类型的所有其他构造函数都会保留。
上面的示例还不是算法,但希望它们可以帮助您入门。
当然,所有这些都忽略了大小写保护(这使得重叠无法确定)、模式保护、GADT(它可以以非常微妙的方式进一步细化剩余的集合)。