Haskell枚举方法

问题描述 投票:3回答:3

我想代表以下形式的一种类型:

(Card, Suit)

代表在Card实例将在集合中的纸牌游戏中的纸牌:

{2, 3, 4, 5, 6, 7, 8, 9, J, Q, K, 1}

Suit将在集合中具有实例:

{S, D, H, C}

如果不是数字,我会用两个数据声明来处理:

data Suit = S | D | H | C deri...

但是很明显,将数字添加到这些空Arity类型将失败。

所以我的问题是,如何模拟在C中找到的枚举类型?

我想我会误解类型系统的基本要点,将不胜感激!

编辑:我要添加一些上下文:我要表示this Euler problem中包含的数据,正如您可以检查的那样,数据以1S表示黑桃A,2D表示2菱形,等等...

我真正想要的是能够直接在字符串上执行读取操作以获得相应的对象。

haskell types enumeration
3个回答
19
投票

实际上,从我开发扑克机器人开始,我就很方便地实现了实现。它不是特别复杂,但确实可以工作。

首先是相关类型。等级和花色是枚举,而牌是明显的复合类型(带有自定义Show实例)

import Text.ParserCombinators.Parsec

data Suit = Clubs | Diamonds | Hearts | Spades deriving (Eq,Ord,Enum,Show)

data Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten
          | Jack | Queen | King | Ace deriving (Eq,Ord,Enum,Show)  

data Card = Card { rank :: Rank
                 , suit :: Suit } deriving (Eq,Ord,Bounded)

instance Show Card where
    show (Card rank suit) = show rank ++ " of " ++ show suit

然后我们有了使用Parsec的解析代码。您可以将其开发得更加复杂,以返回更好的错误消息,等等。

请注意,正如Matvey在评论中所说,将字符串解析为程序中它们的表示形式的问题与(或更应该是[[应该]])正交于枚举的表示形式。在这里,我欺骗并破坏了正交性:如果您想重新排列等级(例如,使Ace等级低于Two),那么您将破坏解析代码,因为解析器取决于[ C0]为Two0Three等。一种更好的方法是显式地拼出1中的所有等级(这是我在原始代码中所做的事情)。我这样写是为了(a)节省一些空间,(b)说明原则上如何将数字解析为等级,并且(c)给出一个明确阐明错误做法的示例,因此您可以避免它将来。

parseRank

这里正在起作用:

parseSuit :: Parser Suit parseSuit = do s <- oneOf "SDCH" return $ case s of 'S' -> Spades 'D' -> Diamonds 'H' -> Hearts 'C' -> Clubs parseRank :: Parser Rank parseRank = do r <- oneOf "23456789TJQKA" return $ case r of 'T' -> Ten 'J' -> Jack 'Q' -> Queen 'K' -> King 'A' -> Ace n -> toEnum (read [n] - 2) parseCard :: Parser Card parseCard = do r <- parseRank s <- parseSuit return $ Card { rank = r, suit = s } readCard :: String -> Either ParseError Card readCard str = parse parseCard "" str


编辑:

@ yatima2975在评论中提到,使用*Cards> readCard "2C" Right Two of Clubs *Cards> readCard "JH" Right Jack of Hearts *Cards> readCard "AS" Right Ace of Spades 可能会有些有趣。我无法让它做很多有用的事情,但似乎很有希望。首先,您需要通过将OverloadedStrings放在文件顶部来启用语言选项,并包括{-# LANGUAGE OverloadedStrings #-}行以导入相关的类型类。然后可以将import GHC.Exts ( IsString(..) )转换为字符串文字:

Card

这使您可以在卡的字符串表示形式上进行模式匹配,而不必显式地写出类型:

instance IsString Card where fromString str = case readCard str of Right c -> c

您也可以使用字符串文字作为函数的输入:

isAce :: Card -> Bool isAce "AH" = True isAce "AC" = True isAce "AD" = True isAce "AS" = True isAce _ = False

这里正在起作用:

printAces = do let cards = ["2H", "JH", "AH"] mapM_ (\x -> putStrLn $ show x ++ ": " ++ show (isAce x)) cards


9
投票
*Cards> printAces Two of Hearts: False Jack of Hearts: False Ace of Hearts: True

2
投票
我只想给数字加上字母,或者更好的是一个词。我也不会使用太多的单字母缩写-HK等完全无法读取。
© www.soinside.com 2019 - 2024. All rights reserved.