下面的函数非常简单:
test :: Int -> Int
test x = case x of
0 -> 0
1 -> 1
_ -> 2
和test 0 == 0
, test 1 == 1
, test 77 == 2
。
下面的函数几乎和一样简单:
import Data.Ratio
test2 :: Rational -> Int
test2 = case x of
0 -> 0
1 % 2 -> 1
_ -> 2
在GHCi中加载此代码会出现错误Parse error in pattern: 1 % 2
。
给了什么?为什么我不能在有理数上进行模式匹配?我可以用守卫解决这个例子中出现的现实问题,但是我很好奇为什么模式匹配不起作用。
一般不可以对函数进行模式匹配。这需要计算逆函数,而逆函数通常不存在。您只能在构造函数上匹配,如Just
或:+
:通过以大写字符或冒号开头,可以从普通函数/中音操作符中识别出这些。
你可以模式匹配的理性。
import GHC.Real (:%)
test2 :: Rational -> Int
test2 = case x of
0 -> 0
1 :% 2 -> 1
_ -> 2
原因,我想,为什么不建议使用:%
(因此它只从内部模块导出,而不是从Data.Ratio
)是Ratio
的值总是应该是最小的,但:%
作为一个普通的构造函数并不能确保这一点:
Prelude Data.Ratio GHC.Real> 4%2
2 % 1
Prelude Data.Ratio GHC.Real> 4:%2
4 % 2
特别是,如果你在这样一个非规范化的分数上进行模式匹配,你不能确保成功。
在1%2
这样的情况下,您可以通过对十进制分数(有限的十进制分数是唯一的)进行模式匹配来避免这个问题:
test2 :: Rational -> Int
test2 = case x of
0 -> 0
0.5 -> 1
_ -> 2
当然,这可能不太好。在现代Haskell中,理论上可以将:%
重新定义为智能模式的同义词:
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
import Data.Ratio
numDenum :: Integral a => Ratio a -> (a,a)
numDenum x = (numerator x, denominator x)
pattern (:%) :: () => Integral a => a -> a -> Ratio a
pattern a:%b <- (numDenum -> (a,b))
where a:%b = a%b
,然后可以像在原始示例中一样使用。
…但坦率地说,使用numerator
和denominator
可能会更好。
您也可以使用守卫来做非常类似的事情。您可以使用任意Bool表达式,因此您可以使用(%)
和所有其他纯函数。
test3 :: Rational -> Int
test3 x | x == 0 = 0
| x == 1 % 2 = 1
| otherwise = 2
它们也适用于case语句。
test3a :: Rational -> Int
test3a y = case y of
x | x == 0 -> 0
| x == 1 % 2 -> 1
| otherwise -> 2