Haskell 中有理数的模式匹配
Pattern-matching on Rationals in Haskell
以下函数非常简单:
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
or :+
:这些可以通过以大写字符或冒号开头的普通函数/中缀运算符来识别。
您可以 有理数模式匹配。
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
以下函数非常简单:
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
or :+
:这些可以通过以大写字符或冒号开头的普通函数/中缀运算符来识别。
您可以 有理数模式匹配。
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