繁体   English   中英

Haskell解析为自定义数据类型

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM