我对函数式编程非常陌生,我几乎没有以下自定义数据类型来代表一副纸牌。
西服的数据类型
data Suit = Spade | Club | Diamond | Heart
deriving (Eq, Ord, Enum, Bounded)
instance Show Suit where
show Spade = "S"
show Club = "C"
show Diamond = "D"
show Heart = "H"
instance Read Suit where
readsPrec _ [] = []
readsPrec _ (s: str) = case s of
'S' -> [(Spade, str)]
'C' -> [(Club, str)]
'D' -> [(Diamond, str)]
'H' -> [(Heart, str)]
_ -> []
排名数据类型
data Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten
| Jack | Queen | King | Ace
deriving (Eq, Ord, Enum, Bounded)
instance Show Rank where
show Ace = "A"
show King = "K"
show Queen = "Q"
show Jack = "J"
show rank = show $ fromEnum rank + 2
instance Read Rank where
readsPrec _ [] = []
readsPrec _ (s: str) = case s of
'A' -> [(Ace, str)]
'K' -> [(King, str)]
'Q' -> [(Queen, str)]
'J' -> [(Jack, str)]
_ -> readNum
where
readNum
| s >= '2' && s <= '9' = [(toEnum (ord s - ord '2') :: Rank, str)]
| s == '1' = readZero str -- Read a following 0
| otherwise = []
readZero ('0': rest) = [(Ten, rest)]
readZero _ = []
卡的数据类型
data Card = Card Suit Rank
deriving (Eq)
instance Show Card where
show (Card suit rank) = show suit ++ show rank
instance Read Card where
readsPrec _ str = do
(s, rest) <- reads str
(r, end) <- reads rest
return (Card s r, end)
当我在堆栈ghci控制台中键入read "S3H5" :: Card
时,它显示了*** Exception: Prelude.read: no parse
的异常错误。
请问该如何解决?
我有一串卡信息,例如代表两个卡的“ S3HA”:
我要实现的目标:
当我读取“ S3HA”的字符串时,我希望输出为一个数组
[Card Spade Three, Card Heart Ace]
请告知!我对Haskell函数式编程比较陌生!
并且我不想修改上述Card
,Suit
和Rank
的自定义数据类型的源代码,而是利用这些数据类型来创建{{1 }}。有人可以提供将字符串解析为[Card]
的方法吗?
答案 0 :(得分:0)
正如@chi在对问题的评论中所说,实现目标的最直接方法是为readList
定义Card
方法。
对于列表,我认为您需要在
readList
实例中重新定义Read Card
方法。如果这样做,则预定义实例Read a => Read [a]
将自动调用您自己的readList
,一切正常。
一个可能的定义:
instance Read Card where
...
readList str = do
(card, rest) <- reads str
if null rest then return ([card], "")
else fmap (Data.Bifunctor.bimap (card:) id) (readList rest
或者也许:
import Text.ParserCombinators.ReadP
...
instance Read Card where
...
readList = readP_to_S (many (readS_to_P reads))
但是,还请注意,默认的Show
和Read
实例具有一些优点:
因此,将相同的代码放入诸如parseCard
,parseCards
,renderCard
和renderCards
之类的单态函数中可能更合理,两个世界。
实际上,我认为自定义Read
实例是Haskell的一个晦涩角落。前一段时间我在研究类似问题时,找不到关于此主题的任何指南。 (与诸如细胞自动机或免费monad上的大量指南进行比较。)这可能是因为人们大多使用默认派生实例,直到他们真正需要解析大量数据为止,这就是当高级解析库(例如parsec
)发挥作用时。