如何确定一个Enum值是否是另一个Enum值的继承者?
我正在尝试编写一个函数,该函数告诉我一个Enum
是否为另一个的后继.这是我的第一次尝试:
I'm trying to write a function that tells me whether one Enum
is the successor of another. Here was my first attempt:
isSuccessorOf x y = x == succ y
看起来很合理.试试吧:
Looks reasonable. Let's try it:
λ> isSuccessorOf 3 2
True
λ> isSuccessorOf 1 5
False
λ> isSuccessorOf 3 (maxBound :: Int)
*** Exception: Prelude.Enum.succ{Int}: tried to take `succ' of maxBound
哇.那应该是False
.确保我们不要尝试执行succ maxBound
:
Whoops. That should have been False
. Let's make sure we don't try to do succ maxBound
:
isSuccessorOf x y = y /= maxBound && x == succ y
让我们再试一次:
λ> 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)
嗯,现在它仅适用于有界类型.我想避免对无界和有界的Enum
分别使用一个函数,尤其是在编译时没有任何事情可以阻止您在有界类型上使用无界函数的情况下.让我们改用Ord
约束:
Hmm, now it only works on bounded types. I'd like to avoid needing a separate function for unbounded and bounded Enum
s, especially if there's nothing at compile-time to keep you from using the unbounded function on a bounded type. Let's use an Ord
constraint instead:
isSuccessorOf x y = x > y && x == succ y
让我们尝试一下:
λ> isSuccessorOf 3 (maxBound :: Int)
False
λ> isSuccessorOf 3 (2 :: Integer)
True
但是现在我做了一个毫无根据的假设.让我们再尝试一件事(注意:这取决于Down
具有一个Enum
实例,这对GHC 8.10.1是新的):
But now I'm making an unwarranted assumption. Let's try one more thing (note: this depends on Down
having an Enum
instance, which is new to GHC 8.10.1):
λ> import Data.Ord (Down(..))
λ> let delisleFreezing = Down 150
λ> isSuccessorOf (succ delisleFreezing) delisleFreezing
False
那不理想.
那么,没有这三个缺陷之一,有什么办法可以完成这项看似简单的任务?
So is there any way to do this seemingly-simple task, without one of these three flaws?
- 无法为非
Bounded
的类型进行编译
-
Bounded
类型的底部
- 为
succ x > x
不成立的类型给出错误的答案
- Fails to compile for types that aren't
Bounded
- Bottoms for types that are
Bounded
- Gives the wrong answer for types where
succ x > x
doesn't hold
也许更安全的方法是使用 enumFromTo
,然后检查列表的第二项是否是我们要查找的后继项.我们可以像
Perhaps a more safe way to check this is making use of enumFromTo
, and check if the second item of the list is the successor we are looking for. We can, like you say, simply pattern match on a list with two elements, we do not need to check if that second element is indeed y
:
isSuccessorOf :: Enum a => a -> a -> Bool
isSuccessorOf y x
| [_,_] <- [x .. y] = True
| otherwise = False
or we can, like @chi says use this to look if there is a successor:
succMaybe :: Enum a => a -> Maybe a
succMaybe x = case [x ..] of
(_:z:_) -> Just z
_ -> Nothing