[英]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.