Ограничение длины строки в значениях типа данных в Haskell - PullRequest
5 голосов
/ 29 апреля 2020

Я пытаюсь представить номерной знак автомобиля с Haskell. Определение формата номерного знака выглядит следующим образом:

  1. Старый номерной знак был 5 цифрами alphanumeri c long

  2. Новый номерной знак состоит из:

    • состояние регистрации (N, S, E, W и C)
    • район, в котором был зарегистрирован автомобиль (1,2..20)
    • год и месяц регистрации
    • случайная последовательность из трех цифр alphanumeri c.

Моя попытка решения заключается в следующем следует

import Data.Char
data Locality = N | S | E | W | C

data Reg = OldReg {code :: String} | NewReg {loc :: Locality,
            district :: Int,
            month :: Int,
            year :: Int,
            random:: String} 

currentYear =2020

createNewReg::Localitly->Int->Int->Int->String-> Maybe Reg
createNewReg l d m y r 
    | (d < 1) ||(d > 20)  = Nothing --district must be between 1 and 20
    | (m < 1) || (m > 12) = Nothing -- month must be between 1 and 12
    | (y >= 2020) && y<= currentYear = Nothing -- Year must be after new plates introduce and below or equal to current year 
    | length(r) /= 3 = Nothing  -- random sequence must be 3 digits long
    | not $foldl (&&) True $ map Data.Char.isAlpha r = Nothing --random sequence must be alphanumeric
    | otherwise = Just $NewReg l d m y r 

Мне было интересно, есть ли более "1042 *" решение проблемы? Могу ли я создать тип данных, который ограничивает эти поля, не используя другую функцию для его создания?

Что-то вроде NewData {random :: String (Alpha :3)}, чтобы ограничить его строкой alphanumeri c длины 3?

( Я знаю, что не будет никакого правильного синтаксиса, но я надеюсь, что вы получить идею )

Любой совет или помощь будет принята с благодарностью.

Спасибо

1 Ответ

5 голосов
/ 29 апреля 2020

Вы можете расширить Haskell с помощью всевозможных необычных функций, которые могут помочь вам в пути, но есть и несколько висячих фруктов. Как типично в программировании, они часто приходят в форме компромиссов. Это также относится и к случаю.

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

Например, вы можете определить месяцы следующим образом:

data Month =
  Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec
  deriving (Eq, Show, Ord, Enum, Bounded)

Это более безопасным для типов, чем использование Int, потому что вы ограничены только этими двенадцатью значениями. Вы также можете обойти вопрос, указывает ли 0 или 1 январь и т. Д. c.

Вы можете сделать то же самое со значением district (здесь не показано).

Аналогично, если вы знаете, что у вас всегда есть три значения, список не самый лучший тип данных. Кортеж лучше.

Частичное улучшение типа данных Reg будет примерно таким:

data Reg =
    OldReg { code :: String }
  | NewReg { loc :: Locality,
             district :: Int,
             month :: Month,
             year :: Int,
             random :: (Char, Char, Char) }
  deriving (Eq, Show)

Это не решает все проблемы, потому что random все еще может быть заполнен, скажем, ('1', '2', '2'). Также сложнее придумать тип, который правильно ограничивает год, поэтому вам, вероятно, все еще понадобится умный конструктор.

Возможно, вы сможете сделать больше. Как правило, номерные знаки допускают только заглавные буквы. Интеллектуальный конструктор может проверять наличие строчных букв и отклонять их или преобразовывать, или вы можете смоделировать буквы как тип суммы с двадцатью шестью значениями: A | B | C | D | ... et c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...