我目前对Haskell中模式重叠的理解是,如果传递给函数的某些参数值可以由多个模式匹配,则认为2个模式是重叠的。
假设:
last :: [a] -> a
last [x] = x
last (_ : xs) = last xs
传递参数值[1]将匹配第一个模式[x]和第二个模式(_:xs) - 这意味着该函数具有重叠模式,即使两个模式都可以匹配。
令人困惑的是,虽然模式(通过上面的定义)重叠,但GHC没有显示任何关于它们重叠的警告。
在last
函数中恢复2个模式匹配确实会显示重叠警告:
last :: [a] -> a
last (_ : xs) = last xs
last [x] = x
警告:
src\OverlappingPatterns.hs:6:1: Warning:
Pattern match(es) are overlapped
In an equation for `last': last [x] = ...
如果以前的模式使得无法匹配稍后发生的模式,GHC就会考虑模式重叠。
确定函数是否具有重叠模式的正确方法是什么?
更新
我正在寻找fp101x课程中使用的overlapping pattern
定义。
根据fp101x中使用的定义,以下函数具有overlapping patterns
:
last :: [a] -> a
last [x] = x
last (_ : xs) = last xs
这与overlapping pattern
的GHC定义相矛盾,后者认为它没有任何重叠模式。
如果没有正确定义overlapping pattern
在fp101x课程环境中的含义,就无法解决这个问题。那里使用的定义不是GHC的定义。
答案 0 :(得分:1)
更新后的问题澄清了OP想要重叠模式的正式定义。这里"重叠" GHC在发出警告时使用的意思是:当它检测到案例分支无法访问时,因为它的模式与之前分支尚未处理的任何内容不匹配。
可能的正式定义确实可以遵循这种直觉。也就是说,对于任何模式p
,可以首先定义与[[p]]
匹配的值集(标记)p
。 (为此,重要的是要知道p
- [[p]]
中涉及的变量类型取决于类型环境Gamma
。)然后,可以说按顺序图案
q0 q1 ... qn p
模式p
重叠iff [[p]]
作为一个集合,包含在[[q0]] union ... union [[qn]]
中。
上述定义几乎不起作用 - 它不会立即导致检查重叠的算法。实际上,计算[[p]]
是不可行的,因为它通常是无限集。
为了定义一个算法,我尝试为这组术语定义一个表示,而不是匹配的#34;通过任何模式q0 .. qn
。举个例子,假设我们使用布尔值列表:
Remaining: _ (that is, any list)
q0 = []
Remaining: _:_ (any non empty list)
q1 = (True:xs)
Remaining: False:_
p = (True:False:ys)
Remaining: False:_
这里,"剩下的" set没有改变,所以最后一个模式是重叠的。
另一个例子:
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!)
正如您所看到的,我们在每个步骤中都匹配剩余的"剩余的"样品与手头的图案。这会生成一组新的剩余样本(可能没有)。所有这些样本的集合构成了新的剩余集合。
为此,请注意了解每种类型的构造函数列表非常重要。这是因为在与True
匹配时,您必须知道还有另外False
个案例。同样,如果您与[]
匹配,则还有另外_:_
个案例。粗略地说,当与构造函数K
匹配时,所有其他相同类型的构造函数仍然存在。
以上示例还不是一种算法,但它们可以帮助您入门。
所有这一切当然都忽略了防护罩(使重叠不可判定),防护模式,GADT(可以以非常微妙的方式进一步细化剩余的防守)。
答案 1 :(得分:1)
我正在寻找fp101x课程中使用的重叠模式定义。
“不依赖于匹配顺序的模式是 称为不相交或不重叠。“(来自”Haskell编程“ Graham Hutton)
所以这个例子是非重叠的
foldr :: (a → b → b) → b → [a] → b
foldr v [] = v
foldr f v (x : xs) = f x (foldr f v xs)
因为您可以像这样更改模式匹配的顺序:
foldr :: (a → b → b) → b → [a] → b
foldr f v (x : xs) = f x (foldr f v xs)
foldr v [] = v
在这里你不能:
last :: [a] -> a
last [x] = x
last (_ : xs) = last xs
所以最后一个))重叠。
答案 2 :(得分:0)
我认为事情是,在第一种情况下,并非[x]的所有匹配都匹配(_:xs)。在第二种情况下,反之亦然(没有一个匹配(_:xs)将通过[x])。因此,重叠确实意味着存在无法到达的模式。
这就是GHC文档中有关它的说法:
默认情况下,如果有一组模式,编译器会发出警告 不完整的(即,你只匹配代数的一个子集 数据类型的构造函数),或重叠,即
f :: String -> Int f [] = 0 f (_:xs) = 1 f "2" = 2
最后一个模式匹配`f'永远不会被联系到 第二种模式与它重叠。通常是多余的 模式是程序员错误/错误,因此启用此选项 默认值。
可能"无法到达的模式"将是一个更好的选择。
答案 3 :(得分:0)
我建议将推理逻辑与编译器消息结合使用,测试结果将是了解函数是否具有重叠模式的更好方法。作为两个示例,已经列出的第一个示例确实会导致编译器警告。
-- The first definition should work as expected.
last1 :: [a] -> a
last1 [x] = x
last1 (_:xs) = last xs
在第二种情况下,如果我们交换最后两行然后是编译器错误,表明。 程序错误:模式匹配失败:init1 [] 结果
last :: [a] -> a
last (_:xs) = last xs
last [x] = x
这匹配传递单例列表的逻辑,该列表可以在两种模式中匹配,在这种情况下是现在的第二行。
last (_:xs) = last xs
在这两种情况下,都会匹配。如果我们再转到第二个例子
-- The first definition should work as expected
drop :: Int -> [a] -> [a]
drop 0 xs = xs
drop n [] = []
drop n (_:xs) = drop1 (n - 1) xs
在第二种情况下,如果我们再次将最后一行与第一行交换,那么我们不会遇到编译错误,但我们也没有得到我们期望的结果。主> drop 1 [1,2,3] 返回一个空列表[]
drop :: Int -> [a] -> [a]
drop n (_:xs) = drop1 (n - 1) xs
drop 0 xs = xs
drop n [] = []
总之,我认为这就是为什么推理(与正式定义相反)来确定重叠模式的原因。