简体   繁体   English

Haskell自定义数据类型和表示

[英]Haskell custom data type and reprsentation

Say there is a stadium and the row number is something like A1-10, then B1-10 and so on until ZZ 假设有一个体育场,行号是A1-10,然后是B1-10,以此类推,直到ZZ

How do I make a custom data type and use it to represent the seat in Haskell? 如何创建自定义数据类型并使用它来表示Haskell的席位?

You can think of your enumeration as being composed of three parts 您可以认为您的枚举由三部分组成

  • a first letter, 第一个字母,
  • an (optional) second letter, and (可选)第二个字母,以及
  • a number between 1 and 10 1到10之间的数字

The first and second part both rely upon the notion of "letter", so let's define that 第一部分和第二部分都依靠“字母”的概念,因此让我们定义

data Letter
  = La | Lb
  | Lc | Ld
  | Le | Lf
  | Lg | Lh
  | Li | Lj
  | Lk | Ll
  | Lm | Ln
  | Lo | Lp
  | Lq | Lr
  | Ls | Lt
  | Lu | Lv
  | Lw | Lx
  | Ly | Lz
  deriving ( Eq, Ord, Show )

This type is explicitly enumerated instead of merely using Char so that we don't have to worry about the differences between lower and upper case or the problems of Char containing extra things like '-' and '^' . 明确枚举了这种类型,而不仅仅是使用Char这样我们就不必担心大小写之间的区别,也不必担心Char包含'-''^' Since I enumerated the elements in alphabetical order, the autoderived instances like Ord behave properly. 由于我按字母顺序枚举了元素,因此自动派生的实例(例如Ord行为正常。

We probably do want to take advantage of the fact that Letter is a subset of Char so let's write the projections, too. 我们可能确实想利用LetterChar的子集这一事实,所以让我们也编写投影。

-- This one always works since every letter is a character. -因为每个字母都是一个字符,所以这个总是有效的。

letterToChar :: Letter -> Char
letterToChar l = case l of
  La -> 'a'
  Lb -> 'b'
  Lc -> 'c'
  Ld -> 'd'
  Le -> 'e'
  Lf -> 'f'
  Lg -> 'g'
  Lh -> 'h'
  Li -> 'i'
  Lj -> 'j'
  Lk -> 'k'
  Ll -> 'l'
  Lm -> 'm'
  Ln -> 'n'
  Lo -> 'o'
  Lp -> 'p'
  Lq -> 'q'
  Lr -> 'r'
  Ls -> 's'
  Lt -> 't'
  Lu -> 'u'
  Lv -> 'v'
  Lw -> 'w'
  Lx -> 'x'
  Ly -> 'y'
  Lz -> 'z'


-- This one might fail since some characters aren't letters. We also do
-- automatic case compensation.
charToLetter :: Char -> Maybe Letter
charToLetter c = case Char.toLower of
  'a' -> Just La
  'b' -> Just Lb
  'c' -> Just Lc
  'd' -> Just Ld
  'e' -> Just Le
  'f' -> Just Lf
  'g' -> Just Lg
  'h' -> Just Lh
  'i' -> Just Li
  'j' -> Just Lj
  'k' -> Just Lk
  'l' -> Just Ll
  'm' -> Just Lm
  'n' -> Just Ln
  'o' -> Just Lo
  'p' -> Just Lp
  'q' -> Just Lq
  'r' -> Just Lr
  's' -> Just Ls
  't' -> Just Lt
  'u' -> Just Lu
  'v' -> Just Lv
  'w' -> Just Lw
  'x' -> Just Lx
  'y' -> Just Ly
  'z' -> Just Lz
  _   -> Nothing -- default case, no match

Now we play the same game with "digits from 1 to 10" 现在我们玩同一游戏,“数字从1到10”

data Digit
  = D1 | D2
  | D3 | D4
  | ...
  deriving ( Eq, Ord, Show )

digitToInt :: Digit -> Int
digitToInt = ...

intToDigit :: Int -> Maybe Digit
intToDigit = ...

We might even write other ways of retracting an Int to a Digit . 我们甚至可以写出其他将Int缩进Digit For instance, we could (1) take the absolute value of the integer and then (2) take its div and mod against 10 seats. 例如,我们可以(1)取整数的绝对值,然后(2)取10个席位的divmod This will result in a Digit assignment and a row number. 这将导致Digit分配和行号。

intToDigitWrap :: Int -> (Int, Digit)
intToDigitWrap n = (row, dig) where
  (row, dig0) = n `divMod` 10
  -- we use an incomplete pattern match because we have an invariant
  -- now that (dig0 + 1) is in [1, 10] so intToDigit always succeeds
  Just dig    = intToDigit (dig0 + 1) 

And the final type is easy! 最后的类型很简单!

data Seat = Seat { letter1 :: Letter
                 , letter2 :: Maybe Letter
                 , digit   :: Digit
                 } deriving ( Eq, Ord, Show )

The Ord type is again completely automatically correct as Nothing is less than Show x for any x and record ordering is lexicographic. Ord类型再次完全自动更正,因为任何x Nothing小于Show x ,并且记录顺序是字典式的。 We can also write a show instance that's a bit friendlier very simply 我们还可以编写一个显示实例,它非常简单

prettySeat :: Seat -> String
prettySeat s = 
  let l1 = [Char.toUpper $ letterToChar $ letter1 s]
      l2 = case letter2 s of
             Nothing -> ""
             Just c  -> [Char.toUpper $ letterToChar c]
      dig = show (digitToInt (digit s))
  in l1 ++ l2 ++ "-" ++ dig

In all likelihood the ability to inject the Letter and Digit types into their superset types Char and Int respectively will almost certainly come in handy when writing code later. 在以后编写代码时,几乎肯定可以将LetterDigit类型分别注入其超集类型CharInt会派上用场。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM