所以我一直忙于真实世界Haskell书,我做了lastButOne练习.我想出了两个解决方案,一个是模式匹配
lastButOne :: [a] -> a lastButOne ([]) = error "Empty List" lastButOne (x:[]) = error "Only one element" lastButOne (x:[x2]) = x lastButOne (x:xs) = lastButOne xs
一个使用案例表达
lastButOneCase :: [a] -> a lastButOneCase x = case x of [] -> error "Empty List" (x:[]) -> error "Only One Element" (x:[x2]) -> x (x:xs) -> lastButOneCase xs
我想知道的是模式匹配何时优先于case表达式,反之亦然.这个例子对我来说不够好,因为看起来虽然两个函数都按预期工作,但它并没有让我选择一个实现而不是另一个.所以选择"看起来"优先乍一看?
那么有没有通过源代码获得好的案例,无论是在haskell自己的源代码还是github或其他地方,哪里可以看到哪种方法是首选的?
首先是一个简短的术语转移:我会称之为"模式匹配".我不确定是否有一个很好的术语来区分模式匹配 - 通过案例和模式匹配 - 通过多重定义.
两者之间的技术区别确实很轻.您可以通过要求GHC使用-ddump-simpl
标志转储为两个函数生成的核心来自行验证.我在几个不同的优化级别尝试了这一点,并且在所有情况下,核心的唯一差异是命名.(顺便说一下,如果有人知道Core的一个好的"语义差异"程序 - 它至少知道alpha等价 - 我很有兴趣听到它!)
不过,有一些小问题需要注意.您可能想知道以下内容是否也是等效的:
{-# LANGUAGE LambdaCase #-} lastButOne = \case [] -> error "Empty List" (x:[]) -> error "Only One Element" (x:[x2]) -> x (x:xs) -> lastButOneCase xs
在这种情况下,答案是肯定的.但请考虑这个看起来相似的一个:
-- ambiguous type error sort = \case [] -> [] x:xs -> insert x (sort xs)
突然间,这是一个类型类多态CAF,所以在旧的GHC上,这将触发单态限制并导致错误,而具有显式参数的表面相同的版本不会:
-- this is fine! sort [] = [] sort (x:xs) = insert x (sort xs)
另一个微小的区别(我忘记了 - 感谢Thomas DuBuisson提醒我)是在处理where子句.由于where子句附加到绑定站点,因此它们不能跨多个方程共享,但可以跨多个案例共享.例如:
-- error; the where clause attaches to the second equation, so -- empty is not in scope in the first equation null [] = empty null (x:xs) = nonempty where empty = True nonempty = False -- ok; the where clause attaches to the equation, so both empty -- and nonempty are in scope for the entire case expression null x = case x of [] -> empty x:xs -> nonempty where empty = True nonempty = False
您可能认为这意味着您可以使用不能用case表达式执行的方程式执行某些操作,即在两个方程中对同一名称具有不同的含义,如下所示:
null [] = answer where answer = True null (x:xs) = answer where answer = False
但是,由于case
表达式的模式是绑定站点,因此也可以在case
表达式中进行模拟:
null x = case x of [] -> answer where answer = True x:xs -> answer where answer = False
当然,where
条款是附加到case
模式还是等式取决于缩进.