首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell自定义数据类型和表示

Haskell自定义数据类型和表示
EN

Stack Overflow用户
提问于 2015-03-05 09:56:37
回答 1查看 1.8K关注 0票数 2

假设有一个体育场,排号是A1-10,然后是B1-10,等等,直到ZZ。

如何创建自定义数据类型并使用它来表示Haskell中的座位?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-03-05 12:38:22

您可以认为枚举是由三个部分组成的。

  • 第一封信,
  • (可选)第二个字母,以及
  • 介于1到10之间的数字

第一部分和第二部分都依赖于“字母”的概念,所以让我们来定义一下

代码语言:javascript
复制
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 )

这种类型是显式枚举的,而不是仅仅使用Char,这样我们就不必担心小写和大写之间的差异,也不必担心包含'-''^'等额外内容的Char的问题。由于我按字母顺序枚举了元素,因此像Ord这样的自派生实例运行正常。

我们可能确实想利用LetterChar的一个子集这一事实,所以我们也要编写预测。

-这个总是有效的,因为每个字母都是一个字符.

代码语言:javascript
复制
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

现在我们用“数字从1到10”来玩同样的游戏。

代码语言:javascript
复制
data Digit
  = D1 | D2
  | D3 | D4
  | ...
  deriving ( Eq, Ord, Show )

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

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

我们甚至可以编写将Int撤回到Digit的其他方法。例如,我们可以(1)取整数的绝对值,然后(2)取其divmod对10个座位。这将导致Digit赋值和行号。

代码语言:javascript
复制
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) 

最后一种很简单!

代码语言:javascript
复制
data Seat = Seat { letter1 :: Letter
                 , letter2 :: Maybe Letter
                 , digit   :: Digit
                 } deriving ( Eq, Ord, Show )

Ord类型也是完全自动更正的,因为对于任何xNothing小于Show x,而记录排序是字典排序。我们还可以编写一个更友好的演示实例

代码语言:javascript
复制
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

很可能,将LetterDigit类型分别注入其超集类型CharInt的能力在以后编写代码时几乎肯定会派上用场。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28874880

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档