Haskell解析为自定义数据类型

时间:2012-10-17 11:30:29

标签: parsing haskell instance where

我对Haskell比较陌生。这是我想要做的:

我希望解析字符串到象棋。我的代码非常简单,所以我希望它会解释自己:

data ChessPiece = King | ... etc
data DraughtPiece = DKing | ... etc
data Player = Black | White deriving (Show, Eq)
data Piece a = Piece (a, Player) 

所以一场比赛可以是两场比赛,两场比赛。我想要一个字符串“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使我'无法从上下文中使用'解析'推断(解析a1)(解析a) “

如何告诉Haskell parse "K" :: Piece ChessPiece(parse "k" :: ChessPiece, Black)(King, Black)

3 个答案:

答案 0 :(得分:3)

  

我如何告诉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中,您只为单个输入字符串parse定义了"k"。您当然应该为其他输入字符串提供定义(包括为无效字符串调用error的显式catch-all分支)。

  

将其更改为'parse x :: a'会让我'无法从上下文中使用'parse'来推断(解析a1)(Parse a)

这是一个有点不明显的事情。类型变量是隐式的forall-quantified,所以当你写

instance Parse a => Parse (Piece a) where
    parse x | isLowercase x = Piece (parse x :: a, White)

a定义中的parse是一个新的forall量化类型变量,你有效地说该对的第一个组件可以有任何类型,就像你说的那样

instance Parse a => Parse (Piece a) where
    parse x | isLowercase x = Piece (parse x :: anyType, White)

当然,没有instance Parse anyType可以从上下文Parse a中推断出来。

你可以告诉GHC,元组中的a应该使用ScopedTypeVariables扩展名表示与实例头中的{{1}}相同的类型,但最好暂时保留它。

答案 1 :(得分:1)

您需要ScopedTypeVariables pragma才能使用函数类型中使用了typevariable parse x :: a的{​​{1}}之类的内容。

如果您想要针对每种情况更具体的内容,您还可以使用a来定义FlexibleInstancesPiece ChessPiece的实例。

Piece DraughtPiece

在任何情况下,ghc都需要足够的上下文来知道要解析的类型,因此您需要instance Parse (Piece ChessPiece) where parse x = -- return Piece ChessPiece here instance Parse (Piece DraughtPiece) where parse x = -- return Piece DrauPiece here

之类的内容

答案 2 :(得分:0)

由于您的DraughtPieceChessPiece几乎相同,我建议您将它们放入相同的数据类型,原因与您不创建新的Maybe类型相同每次Nothing似乎都是错误的。另一个好处是,当您想增加游戏数量时,代码会更好地扩展。

data ChessPiece = King | ... etc
data Game = Game Int
data Player = Black | White
data Piece = Piece ChessPiece Player Game

您现在必须调整数据表示。如果您可以调整要解析的文件的表示形式,则可以将“黑棋王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

最后一点:您问题的主要困难在于您的数据未以原子方式存储:一个令牌包含有关图形颜色和类型的信息。当您设计自己的文件时,请尝试将单独的内容分开。

相关问题