【问题标题】:Pattern-matching on Rationals in HaskellHaskell 中 Rationals 的模式匹配
【发布时间】:2016-08-19 12:27:23
【问题描述】:

下面的函数非常简单:

test :: Int -> Int
test x = case x of
    0 -> 0
    1 -> 1
    _ -> 2

事实上,test 0 == 0test 1 == 1test 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

什么给了?为什么我不能对有理数进行模式匹配?我可以用警卫解决这个例子的实际问题,但我很好奇为什么模式匹配不起作用。

【问题讨论】:

    标签: haskell pattern-matching rational-number rational-numbers


    【解决方案1】:

    您通常不能对函数进行模式匹配。这将需要计算逆,通常甚至不存在。您只能匹配 构造函数,例如 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
    

    然后可以像在您的原始示例中一样使用它。

    ...但坦率地说,最好还是直接使用numeratordenominator

    【讨论】:

    • 这真是太棒了,我可能会接受它。但它并不能完全回答为什么我不能在 (%) 上进行模式匹配的问题。我对模式匹配的内部不是特别熟悉,所以可能有一个我没有看到的简单答案。
    • 对,我把它加到顶部了。
    • 哦,我明白了。我想我没有意识到 (%) 实际上不是构造函数,但你可以看看它的 :info,它显然不是构造函数。有限小数分数方法巧妙地使用了默认的Fractional a 类型的小数,我已经忘记了。我不确定我是否完全理解智能模式同义词的答案——我使用恐龙 Haskell——但看起来我有一些阅读要做。非常感谢您的精彩回答!
    【解决方案2】:

    你也可以使用守卫来做类似的事情。您可以使用任意 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-13
      • 2021-05-22
      • 1970-01-01
      • 2017-07-09
      • 2016-04-26
      • 2016-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多