私は実際にポーカーボットを開発していたときから便利な実装をしています。特に洗練されているわけではありませんが、機能します。
まず、関連するタイプ。ランクとスーツはカードが(カスタム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が述べたように、文字列をプログラムで表現する際の問題は、)を列挙しています。ここで私は不正行為をして直交性を壊してしまった:のランクをTwo
のように並べ替えるとしたら、パーサはの内部表現に依存しているので、解析コードを破るだろう。0
、Three
1
などです。
より良いアプローチは、parseRank
のすべてのランクを明示的に指定することです(これは元のコードで行います)。私はこれを次のように書いた:(a)スペースを節約する、(b)数字をランクに解析することが原則可能であることを示している、(c)明示的に綴られていない悪い練習の例を与える。将来は。
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
そして、ここではそれがアクションである:
*Cards> readCard "2C"
Right Two of Clubs
*Cards> readCard "JH"
Right Jack of Hearts
*Cards> readCard "AS"
Right Ace of Spades
編集:あなたはOverloadedStrings
で遊んでいくつかの楽しみを持っていることができるかもしれないコメントで述べたyatima2975 @
。私は多くのことをすることができませんでしたそれは有用ですが、それは有望なようです。まず、{-# LANGUAGE OverloadedStrings #-}
をファイルの先頭に置き、import GHC.Exts (IsString(..))
という行を含めて言語オプションを有効にして、関連するtypeclassをインポートする必要があります。あなたは文字列リテラルに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
そして、ここではアクションである:あなたはまた、関数への入力として文字列リテラルを使用することができます
*Cards> printAces
Two of Hearts: False
Jack of Hearts: False
Ace of Hearts: True
Cでもできません。 –
"1S"や "2D"のような文字列をカードに解析する機能は、実際には列挙型表現と直交しています。 –
@MatveyAksenovまあ、私はそれを理解しています。私が探していたのは、型システム内でそれを行うための最も慣用的な方法でした。とにかく、私は間違った質問をしたと思います。 – m09