Если у меня есть тип данных, скажите:
data Color = Red | Yellow | Green
Есть ли способ превратить это в список типа [Цвет], получая все возможные значения? [Красный, Желтый, Зеленый]
Возможно, это полный анти-шаблон?
Если у меня есть тип данных, скажите:
data Color = Red | Yellow | Green
Есть ли способ превратить это в список типа [Цвет], получая все возможные значения? [Красный, Желтый, Зеленый]
Возможно, это полный анти-шаблон?
data Color = Red
| Yellow
| Green
deriving Enum
allColors = [Red ..]
Не уверен, что это анти-шаблон (и я не думаю о хорошем использовании прямо сейчас), но это возможно. Используйте Enum
(позволяет создавать классы типа [someCtor .. someOtherCtor]
) и Bounded
(для minBound
и maxBound
). К счастью, вы можете получить оба:
data Color = Red
| Yellow
| Green
deriving (Enum, Bounded)
allColors = [(minBound :: Color) ..]
Если вы когда-либо добавляли другой цвет, allColors обновляются автоматически. Одно ограничение: Enum
требует, чтобы все контрмеры были нулевыми, т.е. Добавление Foo Int
нарушает все. К счастью, потому что список всех возможных значений для этого был бы слишком большим.
Изменить: другой ответ также работает, может быть, лучше, поскольку для него не требуется вывод Bounded
и, следовательно, немного короче. Я все равно оставлю свой, потому что мне нравится сверхналоженный, но чрезвычайно общий код;)
Разумеется, delnan ответ лучше. Поскольку я не знаю, как включить кусок кода в комментарий, я дам обобщение в качестве отдельного ответа здесь.
allValues :: (Bounded a, Enum a) => [a]
allValues = [minBound..]
Теперь это работает для любого типа с экземпляром Bounded
и Enum
! И allColors
- это просто частный случай:
allColors :: [Color]
allColors = allValues
Во многих случаях вам даже не нужно определять allColors
отдельно.
Вот пример использования этого метода для синтаксического анализа перечислений с помощью Parsec
data FavoriteColor = Maroon | Black | Green | Red |
Blue | Pink | Yellow | Orange
deriving (Show, Read, Enum, Bounded)
И парсер парсера
parseColor :: Parser FavoriteColor
parseColor = fmap read . foldr1 (<|>) $ map (try . string . show)
[ minBound :: FavoriteColor ..]
конечно, попытка может быть применена лучше с помощью сопоставления с образцом, а некоторые другие могут сделать ее более приятной, но это всего лишь пример использования этой техники.