如何确定一个 Enum 值是否是另一个值的后继者?

Jos*_*ica 8 enums haskell bounded-types

我正在尝试编写一个函数来告诉我一个是否Enum是另一个的继承者。这是我的第一次尝试:

isSuccessorOf x y = x == succ y
Run Code Online (Sandbox Code Playgroud)

看起来很合理。让我们试试看:

?> isSuccessorOf 3 2
True
?> isSuccessorOf 1 5
False
?> isSuccessorOf 3 (maxBound :: Int)
*** Exception: Prelude.Enum.succ{Int}: tried to take `succ' of maxBound
Run Code Online (Sandbox Code Playgroud)

哎呀。那应该是False。让我们确保我们不要尝试这样做succ maxBound

isSuccessorOf x y = y /= maxBound && x == succ y
Run Code Online (Sandbox Code Playgroud)

让我们再试一次:

?> isSuccessorOf 3 (maxBound :: Int)
False
?> isSuccessorOf 3 (2 :: Integer)
<interactive>:2:1: error:
    • No instance for (Bounded Integer)
        arising from a use of ‘isSuccessorOf’
    • In the expression: isSuccessorOf 3 (2 :: Integer)
      In an equation for ‘it’: it = isSuccessorOf 3 (2 :: Integer)
Run Code Online (Sandbox Code Playgroud)

嗯,现在它只适用于有界类型。我想避免对 unbounded 和 bounded Enums使用单独的函数,特别是如果在编译时没有任何内容阻止您在有界类型上使用 unbounded 函数。让我们改用Ord约束:

isSuccessorOf x y = x > y && x == succ y
Run Code Online (Sandbox Code Playgroud)

让我们试试看:

?> isSuccessorOf 3 (maxBound :: Int)
False
?> isSuccessorOf 3 (2 :: Integer)
True
Run Code Online (Sandbox Code Playgroud)

但现在我做出了一个毫无根据的假设。让我们再尝试一件事(注意:这取决于是否Down有一个Enum实例,它仅在 GHC 8.10 中才有):

?> import Data.Ord (Down(..))
?> let delisleFreezing = Down 150
?> isSuccessorOf (succ delisleFreezing) delisleFreezing
False
Run Code Online (Sandbox Code Playgroud)

嗯,这不太理想。

那么有没有办法完成这个看似简单的任务,而没有这三个缺陷之一呢?

  • 无法编译不是的类型 Bounded
  • 类型的底部 Bounded
  • 对于succ x > x不成立的类型给出错误的答案

Wil*_*sem 6

也许更安全的检查方法是使用enumFromTo, 并检查列表的第二项是否是我们正在寻找的后继项。我们可以像你说的那样,简单地对具有两个元素的列表进行模式匹配,我们不需要检查第二个元素是否确实是y

isSuccessorOf :: Enum a => a -> a -> Bool
isSuccessorOf y x
    | [_,_] <- [x .. y] = True
    | otherwise = False
Run Code Online (Sandbox Code Playgroud)

或者我们可以,就像@chi 所说的那样,使用它来查看是否有继任者:

succMaybe :: Enum a => a -> Maybe a
succMaybe x = case [x ..] of
    (_:z:_) -> Just z
    _ -> Nothing
Run Code Online (Sandbox Code Playgroud)

  • 等等,我们也可以失去“Eq”约束!`isSuccessorOf yx = case [x..y] of [_,_] -&gt; True; _ -&gt; 假` (4认同)