Haskell中重叠模式的含义

时间:2014-12-28 16:02:15

标签: haskell pattern-matching overlapping-matches

我目前对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的定义。

4 个答案:

答案 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 [] = []

总之,我认为这就是为什么推理(与正式定义相反)来确定重叠模式的原因。