Перечисления Haskell, начиная с 1

#haskell

#haskell

Вопрос:

Рассмотрим следующий метод для создания перечисления с начала 1 в Haskell:

 data Level = Lower | Middle | Upper
    deriving (Show, Eq, Ord)

instance Enum Level where
    toEnum 1  = Lower
    toEnum 2  = Middle
    toEnum 3  = Upper

    fromEnum Lower  = 1
    fromEnum Middle = 2
    fromEnum Upper  = 3

instance Bounded Level where
    minBound = Lower
    maxBound = Upper
  

Я бы предпочел не делать следующее:

 data Level = DontUseThis | Lower | Middle | Upper
    deriving (Show, Eq, Ord)
  

Если нет, есть ли более простой способ сделать это?

Комментарии:

1. Почему вы хотите, чтобы это смещение было на единицу?

2. Что вы на самом деле пытаетесь здесь сделать, что имеет значение fromEnum minBound , возвращает ли 0 или 1?

3. Это все еще не перечисление с начала 1, это перечисление с начала ниже.

4. Написанный вами код выглядит для меня совершенно нормально. Это правильно, это быстро, и для чтения требуется чертовски мало нейронов.

5. @Ana: возможно, явная функция преобразования :: Level -> Int имеет больше смысла, чем использование Enum then.

Ответ №1:

Прежде всего, вам не нужно определять Bounded экземпляр самостоятельно. Если вы добавите Bounded в список производных классов типов, вы должны получить идентичное поведение.

Во-вторых, самый простой способ, который я могу придумать для достижения этой цели, — просто вывести Enum , а затем определить ваши собственные функции перевода. Итак, что-то вроде этого:

 data Level = Lower | Middle | Upper
    deriving (Show, Eq, Ord, Bounded, Enum)

toEnum' x = toEnum (x - 1)
fromEnum' x = (fromEnum x)   1
  

Комментарии:

1. Не кажется чистым, но хорошая идея. Можно ли скрыть исходные toEnum и fromEnum и заменить их новыми версиями?

2. Вроде того? Вы можете добавить import Prelude hiding (toEnum, fromEnum) вверху, а затем добавить свои собственные определения этих функций. Это выглядит очень похоже на ваш исходный код, но преимущество заключается в том, что остальные 4 функции в Enum классе типов определены для вас.

Ответ №2:

Вы можете написать это немного более лаконично (ну, если у вас более 3 конструкторов), используя:

 import Data.List (elemIndex)
import Data.Maybet (fromJust)

values = [Lower, Middle, Upper]  

instance Enum Level where
    toEnum n  = values !! (n-1)
    fromEnum k  = 1   fromJust $ elemIndex k values
  

Комментарии:

1. Однако это довольно дорого с точки зрения времени поиска.

2. Если это действительно проблема, вы всегда можете использовать a Map и a Vector вместо этого.