Haskell:堆栈中的非详尽模式

Haskell: Non-exhaustive patterns in stack

我有以下堆栈 class:

newtype Stack my_stack = Stack [my_stack] deriving Show

getSize :: Stack my_stack -> Int 
getSize (Stack []) = 0
getSize (Stack (x:xs)) = 1 + getSize (Stack xs)

push :: my_stack -> Stack my_stack -> Stack my_stack
push x (Stack xs) = Stack (x:xs)

pop :: Stack my_stack -> Stack my_stack
pop (Stack (x:xs)) = Stack xs

getTop :: Stack my_stack -> my_stack
getTop (Stack (x:xs)) = x

然而,当我尝试使用 getTop 函数时,出现 Non-exhaustive patterns in function getTop 错误。我如何为 getTop 函数声明基本情况(我假设是空情况?),这样我就不会得到非详尽的模式错误?谢谢!

此声明:

getTop (Stack (x:xs)) = x

意思是:“当getTop的参数是一个Stack,它包含一个由头x和尾xs组成的列表,结果是x"

但是如果参数是一个包含空列表的 Stack 怎么办?您尚未定义在这种情况下应该发生什么,因此会发生错误。

因此您需要针对该情况的模式匹配:

getTop (Stack (x:xs)) = x
getTop (Stack []) = ???

但问题是:在那种情况下 getTop 的结果应该是什么?只有您可以根据您的具体情况来决定。对于它的价值,一个常见的模式是 return a Maybe my_stack 代替:

getTop :: Stack my_stack -> Maybe my_stack
getTop (Stack (x:xs)) = Just x
getTop (Stack []) = Nothing

您的函数目前不安全。该警告是(正确地)通知您您不处理空堆栈情况。现在,问题是:如果用户用空堆栈调用 getTop,你想做什么?你有几个选择。我会按照我的喜好大致降序排列它们。

1。 ReturnMaybe

如果我们有一个可能存在也可能不存在的值,惯用的方法是 return MaybeMaybe 是一种特殊类型,可能包含也可能不包含值,由调用者决定如何处理它。他们自己可能会选择 return a Maybe,或者他们可能会提供一个默认值,或者他们可能只是向用户显示一个错误。但是类型系统会强制他们处理这种情况。

getTop :: Stack my_stack -> Maybe my_stack
getTop (Stack []) = Nothing
getTop (Stack (x:xs)) = Just x

这是绝对安全的,并且可以让调用者完全控制。

2。强制用户提供默认值

相反,如果堆栈为空,您可以要求用户指定他们想要的值。

getTop :: my_stack -> Stack my_stack -> my_stack
getTop orelse (Stack []) = orelse
getTop _ (Stack (x:xs)) = x

这仍然是非常安全的,就像以前的解决方案一样,但在我看来它比 Option 更尴尬。类型签名 Stack my_stack -> Maybe my_stack 非常清楚地表明了函数的作用:它需要一个堆栈,并且可能 return 从中得到一个值。类型签名 my_stack -> Stack my_stack -> my_stack 更加模糊:我们是在向堆栈添加一个值,我们是在 return 默认值,我们在做什么?此外,如果用户实际上 想要 Maybe,则可能没有合适的默认值来区别于“堆栈上什么都没有”。

3。抛出错误

这是不是惯用的Haskell,我建议在生产中这样做。但是如果你只是为了个人练习而写一些简短的代码,它可能对黑客有用。

getTop :: Stack my_stack -> my_stack
getTop (Stack []) = error "Empty stack in getTop"
getTop (Stack (x:xs)) = x

error 是一个具有签名 String -> a 的特殊函数(对于任何 a)。您向它提供一条错误消息,然后它……好吧,它使您的程序崩溃。技术上有捕捉 error 的方法,但它们很笨拙并且有很多困难的怪癖,所以通常你应该将 error 视为硬崩溃,并且只有在 [=47= 时才使用它]没有 恢复的可能性。因此,在这种情况下我不推荐它,因为“堆栈为空”是一个非常可恢复的条件。