Как я могу определить, является ли одно значение Enum преемником другого? - PullRequest
8 голосов
/ 09 мая 2020

Я пытаюсь написать функцию, которая сообщает мне, является ли один Enum преемником другого. Это была моя первая попытка:

isSuccessorOf x y = x == succ y

Выглядит разумно. Попробуем:

λ> 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:

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 s, особенно если во время компиляции нет ничего, что мешало бы вам использовать неограниченную функцию для ограниченного типа. Давайте вместо этого воспользуемся ограничением Ord:

isSuccessorOf x y = x > y && x == succ y

И попробуем:

λ> isSuccessorOf 3 (maxBound :: Int)
False
λ> isSuccessorOf 3 (2 :: Integer)
True

Но теперь я делаю необоснованное предположение. Давайте попробуем еще кое-что (примечание: это зависит от того, имеет ли Down экземпляр Enum, что является новым для GH C 8.10.1):

λ> import Data.Ord (Down(..))
λ> let delisleFreezing = Down 150
λ> isSuccessorOf (succ delisleFreezing) delisleFreezing
False

Что ж, это далеко не идеально.

Итак, есть ли способ выполнить эту, казалось бы, простую задачу без одного из этих трех fl aws?

  • Не удается скомпилировать для типов, отличных от Bounded
  • Низ для типов Bounded
  • Дает неправильный ответ для типов, где succ x > x не соответствует

1 Ответ

6 голосов
/ 09 мая 2020

Возможно, более безопасный способ проверить это - использовать enumFromTo и проверить, является ли второй элемент списка тем преемником, которого мы ищем. Мы можем, как вы скажете , просто сопоставить шаблон в списке с двумя элементами, нам не нужно проверять, действительно ли этот второй элемент y:

isSuccessorOf :: Enum a => a -> a -> Bool
isSuccessorOf y x
    | [_,_] <- [x .. y] = True
    | otherwise = <b>False</b>

или мы может, например, @ chi говорит , используйте это, чтобы посмотреть, есть ли преемник:

succMaybe :: Enum a => a -> Maybe a
succMaybe x = case <b>[x ..]</b> of
    (_:z:_) -> Just z
    _ -> Nothing
...