[英]Haskell parsing to custom datatype
我是Haskell的新手。 這是我想做的:
我想將字符串解析為棋子。 我的代碼很簡單,所以我希望它能解釋一下自己:
data ChessPiece = King | ... etc
data DraughtPiece = DKing | ... etc
data Player = Black | White deriving (Show, Eq)
data Piece a = Piece (a, Player)
因此,一個棋子可以是2個游戲中的任意一個,也可以是2個玩家中的任意一個。 我希望將字符串“ k”或“ K”分別解析為黑人或白人國王,所以我這樣做了:
class Parse a where
parse :: String -> a
instance Parse ChessPiece where
parse a = case a of
"k" -> King
到目前為止一切都很好..我可以調用> parse "k" :: ChessPiece
。 這可行!
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x, White)
| otherwise = Piece (parse $ lowercase x, Black)
這對任何一塊都必須起作用。 大寫和小寫的規則適用於DraughtPiece和ChessPiece。 我如何告訴Haskell將x解析為正確的類型(a)。 省略parse x
會給我非窮盡的模式錯誤,將其更改為parse x :: a
會給我“無法從上下文(分析a)的使用“分析”推斷出(分析a1)”
我如何告訴Haskell將parse "K" :: Piece ChessPiece
(parse "k" :: ChessPiece, Black)
為(parse "k" :: ChessPiece, Black)
為(King, Black)
?
我如何告訴Haskell將x解析為正確的類型(a)。
如果您指的是
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x, White)
| otherwise = Piece (parse $ lowercase x, Black)
您不需要。 根據上下文確定進行遞歸parse
的類型,它是參數類型a
。
但是,當您要解析任何內容時,必須有足夠的上下文來告訴GHC結果應具有哪種類型。 在典型的程序中,通常可以根據上下文確定。 如果let p = parse input
,然后在要求p
具有某種類型的上下文中使用p
,則告訴編譯器應解析哪種類型。 但是在ghci提示符下,沒有這樣的上下文,您必須明確告訴ghci您想要哪種類型
ghci> parse "K" :: Piece ChessPiece
省略“ parse x”的類型轉換會給我非窮盡的模式錯誤,
如果您嘗試使用尚未顯式處理的輸入字符串來調用parse
,則會出現非窮舉的模式錯誤。 僅編譯會向您發出警告(如果您要求警告),例如
Pieces.hs:14:13: Warning:
Pattern match(es) are non-exhaustive
In a case alternative:
Patterns not matched:
[]
(GHC.Types.C# #x) : _ with #x `notElem` ['k']
(GHC.Types.C# 'k') : (_ : _)
這意味着在instance Parse Piece
,您僅為單個輸入字符串"k"
定義了parse
。 當然,您應該提供其他輸入字符串的定義(包括對無效字符串的明確的全包分支調用error
)。
將其更改為'parse x :: a'使我'無法從上下文(Parse a)中使用'parse'來推斷(Parse a1)
這是一件不太明顯的事情。 類型變量是隱式的forall-quantified,因此在您編寫時
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x :: a, White)
parse
定義中的a
是一個全新的forall量化類型變量,您實際上說這對的第一個組件可以具有任何類型,就像您說的那樣
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x :: anyType, White)
當然,沒有可以從上下文Parse a
推斷出的instance Parse anyType
。
您可以通過使用ScopedTypeVariables
擴展告訴GHC,元組中的a
應表示與實例頭中的a
相同的類型,但最好暫時保留該類型。
您需要ScopedTypeVariables
編譯指示來允許使用諸如parse x :: a
類的東西,而您在函數類型中使用typevariable a
parse x :: a
地方。
如果您想為每種情況指定更具體的內容,也可以使用FlexibleInstances
來為Piece ChessPiece
和Piece DraughtPiece
定義實例。
instance Parse (Piece ChessPiece) where
parse x = -- return Piece ChessPiece here
instance Parse (Piece DraughtPiece) where
parse x = -- return Piece DrauPiece here
無論如何,ghc需要足夠的上下文來了解要解析的類型,因此您將需要parse "k" :: Piece ChessPiece
類的東西parse "k" :: Piece ChessPiece
由於您的DraughtPiece
幾乎是一樣的ChessPiece
,我建議你把它們放到出於同樣的原因,你沒有發明一種新的相同的數據類型Maybe
鍵入每次都Nothing
似乎是錯誤的單詞 。 另一個好處是,當您想增加游戲數量時,代碼的伸縮性要好得多。
data ChessPiece = King | ... etc
data Game = Game Int
data Player = Black | White
data Piece = Piece ChessPiece Player Game
現在,您必須調整數據表示。 如果可以調整要解析的文件的表示形式,則可以將“ black board of n”編碼為nk
。
import Data.Char
instance Parse Piece where
parse x = case x of
[n,p] | isLower p -> Piece (parse [p]) White (parse [n])
| otherwise -> Piece (parse [p]) Black (parse [n])
_ -> [Parse error]
instance Parse ChessPiece where
parse [p] = case toLower p of
'k' -> King
...
_ -> [Parse error]
instance Parse Game where
parse = Game . digitToInt
最后一點:問題的主要困難是因為您的數據不是原子存儲的:一個令牌包含有關圖形的顏色和類型的信息。 在設計自己的文件時,請嘗試使單獨的內容分開。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.