2

我对 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 to parse "K" :: Piece ChessPieceto (parse "k" :: ChessPiece, Black)to (King, Black)

4

3 回答 3

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您当然应该为其他输入字符串提供定义(包括调用无效字符串的显式包罗万象的分支)。

将其更改为“解析 x :: a”给了我“无法从上下文中使用“解析”推断出(解析 a1)(解析 a)

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

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

a定义中的 theparse是一个新的 forall 量化类型变量,并且您实际上说该对的第一个组件可以具有任何类型,就好像您说

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

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

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

于 2012-10-17T12:10:38.637 回答
1

您需要ScopedTypeVariablespragma 以允许使用诸如在函数类型中parse x :: a使用 typevariable的位置。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

于 2012-10-17T12:04:25.287 回答
0

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

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

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

最后一点:您的问题的主要困难在于您的数据不是原子存储的:一个标记包含有关图形颜色和类型的信息。当您设计自己的文件时,请尝试将不同的东西分开。

于 2012-10-17T12:14:01.440 回答