简体   繁体   English

Haskell解析为自定义数据类型

[英]Haskell parsing to custom datatype

I'm relatively new to Haskell. 我是Haskell的新手。 Here is what i want to do: 这是我想做的:

I want parse strings to chesspieces. 我想将字符串解析为棋子。 my code is pretty straightforward so i hope it will explain itself: 我的代码很简单,所以我希望它能解释一下自己:

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

So a Piece can be of either 2 games, of either 2 players. 因此,一个棋子可以是2个游戏中的任意一个,也可以是2个玩家中的任意一个。 I want a string "k" or "K" to respectively parse to a Black or White King, so I made this: 我希望将字符串“ k”或“ K”分别解析为黑人或白人国王,所以我这样做了:

class Parse a where
  parse :: String -> a

instance Parse ChessPiece where
  parse a = case a of 
    "k" -> King

Everything fine so far.. i can call > parse "k" :: ChessPiece . 到目前为止一切都很好..我可以调用> parse "k" :: ChessPiece this works! 这可行!

instance Parse a => Parse (Piece a) where
   parse x | isLowercase x = Piece (parse             x, White)
           | otherwise     = Piece (parse $ lowercase x, Black)

This has to work for either piece. 这对任何一块都必须起作用。 The rule for uppercase and lowercase goes for DraughtPiece and for ChessPiece. 大写和小写的规则适用于DraughtPiece和ChessPiece。 How do i tell Haskell to parse the x to the correct type (a). 我如何告诉Haskell将x解析为正确的类型(a)。 Leaving out the cast for parse x gives me non-exhaustive pattern error, changing it to parse x :: a gives me 'could not deduce (Parse a1) from a use of 'parse' from the context (Parse a)' 省略parse x会给我非穷尽的模式错误,将其更改为parse x :: a会给我“无法从上下文(分析a)的使用“分析”推断出(分析a1)”

How do i tell Haskell to parse "K" :: Piece ChessPiece to (parse "k" :: ChessPiece, Black) to (King, Black) ? 我如何告诉Haskell将parse "K" :: Piece ChessPiece (parse "k" :: ChessPiece, Black)(parse "k" :: ChessPiece, Black)(King, Black)

How do i tell Haskell to parse the x to the correct type (a). 我如何告诉Haskell将x解析为正确的类型(a)。

If you're referring to 如果您指的是

instance Parse a => Parse (Piece a) where
   parse x | isLowercase x = Piece (parse             x, White)
           | otherwise     = Piece (parse $ lowercase x, Black)

you don't need to. 您不需要。 The type at which the recursive call to parse is made is determined from the context, it's the parameter type a . 根据上下文确定进行递归parse的类型,它是参数类型a

But when you want to parse anything, there must be enough context to tell GHC what type the result shall have. 但是,当您要解析任何内容时,必须有足够的上下文来告诉GHC结果应具有哪种类型。 In typical programmes, that can usually be determined from the context. 在典型的程序中,通常可以根据上下文确定。 If you have let p = parse input and then later use p in a context that requires p to have a certain type, that tells the compiler which type shall be parsed. 如果let p = parse input ,然后在要求p具有某种类型的上下文中使用p ,则告诉编译器应解析哪种类型。 But at the ghci prompt, there is no such context, and you must explicitly tell ghci what type you want 但是在ghci提示符下,没有这样的上下文,您必须明确告诉ghci您想要哪种类型

ghci> parse "K" :: Piece ChessPiece

Leaving out the cast for 'parse x' gives me non-exhaustive pattern error, 省略“ parse x”的类型转换会给我非穷尽的模式错误,

If you try to call parse with an input string you have not explicitly handled, you will get a non-exhaustive pattern error. 如果您尝试使用尚未显式处理的输入字符串来调用parse ,则会出现非穷举的模式错误。 The compilation alone will give you a warning about it (if you ask for warnings), eg 仅编译会向您发出警告(如果您要求警告),例如

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') : (_ : _)

which means that in the instance Parse Piece , you only defined parse for the single input string "k" . 这意味着在instance Parse Piece ,您仅为单个输入字符串"k"定义了parse You should of course provide definitions for other input strings (including an explicit catch-all branch calling error for invalid strings). 当然,您应该提供其他输入字符串的定义(包括对无效字符串的明确的全包分支调用error )。

changing it to 'parse x :: a' gives me 'could not deduce (Parse a1) from a use of 'parse' from the context (Parse a) 将其更改为'parse x :: a'使我'无法从上下文(Parse a)中使用'parse'来推断(Parse a1)

That's a somewhat non-obvious thing. 这是一件不太明显的事情。 Type variables are implicitly forall-quantified, so when you write 类型变量是隐式的forall-quantified,因此在您编写时

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

the a in the definition of parse is a fresh forall-quantified type variable, and you effectively say that the first component of the pair can have any type, as if you said parse定义中的a是一个全新的forall量化类型变量,您实际上说这对的第一个组件可以具有任何类型,就像您说的那样

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

and of course, there is no instance Parse anyType that can be inferred from the context Parse a . 当然,没有可以从上下文Parse a推断出的instance Parse anyType

You can tell GHC that the a in the tuple should denote the same type as the one in the instance head by using the ScopedTypeVariables extension, but it's better to leave that for the time being. 您可以通过使用ScopedTypeVariables扩展告诉GHC,元组中的a应表示与实例头中的a相同的类型,但最好暂时保留该类型。

You need ScopedTypeVariables pragma to allow use of things like parse x :: a where you have used typevariable a in the function type. 您需要ScopedTypeVariables编译指示来允许使用诸如parse x :: a类的东西,而您在函数类型中使用typevariable a parse x :: a地方。

You can also use FlexibleInstances to define instance for Piece ChessPiece and Piece DraughtPiece if you want to something more specific for each case. 如果您想为每种情况指定更具体的内容,也可以使用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 

In any case ghc needs enough context to know what type to parse to so you will need something like parse "k" :: Piece ChessPiece 无论如何,ghc需要足够的上下文来了解要解析的类型,因此您将需要parse "k" :: Piece ChessPiece类的东西parse "k" :: Piece ChessPiece

Since your DraughtPiece is pretty much the same as ChessPiece , I would suggest you put them into the same data type for the same reason you don't invent a new Maybe type every time Nothing seems to be the wrong word . 由于您的DraughtPiece几乎是一样的ChessPiece ,我建议你把它们放到出于同样的原因,你没有发明一种新的相同的数据类型Maybe键入每次都Nothing似乎是错误的单词 Another bonus is that the code scales much better when you want to increase the amount of games. 另一个好处是,当您想增加游戏数量时,代码的伸缩性要好得多。

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

You now have to adjust your data representation. 现在,您必须调整数据表示。 If you can adjust the representation of your file to be parsed, you could encode "black king of board n" as nk . 如果可以调整要解析的文件的表示形式,则可以将“ 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

A final note: the main difficulty of your problem is because your data isn't stored atomically: one token contains information about both color and type of a figure. 最后一点:问题的主要困难是因为您的数据不是原子存储的:一个令牌包含有关图形的颜色和类型的信息。 When you're designing your own files, try to keep separate things separate. 在设计自己的文件时,请尝试使单独的内容分开。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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