Haskell数据域中的多态性
我试图通过创建一个简单的国际象棋游戏来学习Haskell。但是,我在定义表示电路板正方形的函数时遇到了问题Haskell数据域中的多态性,haskell,polymorphism,field,chess,maybe,Haskell,Polymorphism,Field,Chess,Maybe,我试图通过创建一个简单的国际象棋游戏来学习Haskell。但是,我在定义表示电路板正方形的函数时遇到了问题 import Data.Char type Board = [[Square]] type Square = Maybe Piece data Piece = Piece PieceColor PieceType deriving (Show, Eq) data PieceColor = White | Black deriving (Show, Eq) data PieceType =
import Data.Char
type Board = [[Square]]
type Square = Maybe Piece
data Piece = Piece PieceColor PieceType deriving (Show, Eq)
data PieceColor = White | Black deriving (Show, Eq)
data PieceType = King | Queen | Rook | Bishop | Knight | Pawn deriving (Show, Eq)
...
displaySquare :: Square -> Char
displaySquare n
| n == Nothing = ' '
| n == Just (Piece White _) = displaySquare' n
| otherwise = toLower (displaySquare' n)
where
displaySquare' (Just (Piece _ King)) = 'K'
displaySquare' (Just (Piece _ Queen)) = 'Q'
displaySquare' (Just (Piece _ Rook)) = 'R'
displaySquare' (Just (Piece _ Bishop)) = 'B'
displaySquare' (Just (Piece _ Knight)) = 'N'
displaySquare' (Just (Piece _ Pawn)) = 'P'
尝试运行GHCI将返回以下错误:
Chess.hs:21:30:
Found hole ‘_’ with type: PieceType
Relevant bindings include
displaySquare' :: Maybe Piece -> Char (bound at Chess.hs:24:13)
n :: Square (bound at Chess.hs:19:15)
displaySquare :: Square -> Char (bound at Chess.hs:19:1)
In the second argument of ‘Piece’, namely ‘_’
In the first argument of ‘Just’, namely ‘(Piece White _)’
In the second argument of ‘(==)’, namely ‘Just (Piece White _)’
Failed, modules loaded: none.
我不太清楚错误是想告诉我什么。如果我不得不猜测的话,我会说问题是将数据字段定义为一个洞“z”,因为这意味着函数可以将任何类型作为数据字段,这显然与给定的类型签名冲突。我的假设正确吗?我应该如何解决它?这是使用模式匹配编写
displaySquare
的方法:
displaySquare Nothing = ' '
displaySquare n@(Just (Piece White _)) = displaySquare' n
displaySquare n = toLower (displaySquare' n)
where
...your definition of displaySquare'...
您在定义displaySquare'
时正确使用了模式匹配
注意语法n@(Just(Piece White))
是如何工作的——它提供了一个模式来匹配:Just White
,并且它还将变量n
设置为传递给函数的参数
更新
@behzad.nouri在评论中提到,您需要将helper函数置于顶层
另一种使助手函数保持本地的写入方法:
displaySquare n =
case n of
Nothing -> ' '
Just (Piece White _) -> displaySquare' n
_ -> toLower (displaySquare' n)
where
displaySquare' x = ...
在这种情况下,
n
绑定到displaySquare
的第一个参数,因此不需要使用n@...
语法。这是使用模式匹配编写displaySquare
的方法:
displaySquare Nothing = ' '
displaySquare n@(Just (Piece White _)) = displaySquare' n
displaySquare n = toLower (displaySquare' n)
where
...your definition of displaySquare'...
您在定义displaySquare'
时正确使用了模式匹配
注意语法n@(Just(Piece White))
是如何工作的——它提供了一个模式来匹配:Just White
,并且它还将变量n
设置为传递给函数的参数
更新
@behzad.nouri在评论中提到,您需要将helper函数置于顶层
另一种使助手函数保持本地的写入方法:
displaySquare n =
case n of
Nothing -> ' '
Just (Piece White _) -> displaySquare' n
_ -> toLower (displaySquare' n)
where
displaySquare' x = ...
在这种情况下,
n
绑定到displaySquare
的第一个参数,因此不需要使用n@...
语法。关键是:模式匹配的工作原理与相等比较根本不同(通常更可取)
通过模式匹配,您只需查看片段数据是否“适合您感兴趣的粗略形状”。您可以完全忽略您不感兴趣的部分数据,就像您尝试使用Just(Piece White)
一样。因此,模式匹配通常是一种“模糊操作”
OTOH,=
只是一个运算符(),它接受两个值并确定它们是否完全相等Piece White实际上不是一个值,您需要给出一个具体的PieceType
,以适应\uCode>间隙。这就是GHC试图用type:PieceType
告诉你的信息发现了一个洞“uu”:如果你真的需要建立一个平方
值,你需要提供一个PieceType
值。当你处理复杂问题时,这些键入的孔非常有用:只需从外部开始,例如,你可以从n==Piece\uuu
开始,GHC会告诉你它期望在第一个间隙中有一个PieceColor
,你会插入White
,GHC会告诉你它期望在第二个间隙中有一个PieceType
,等等
只是,在displaySquare
函数中,实际上构造一个具体的Square
值来进行比较是没有意义的:您真正想要做的是解构这些值。这就是模式匹配更方便的地方。事实上,我会把这个函数写成一个大的子句列表:
displaySquare (Just (Piece White King)) = '♔'
displaySquare (Just (Piece White Queen)) = '♕'
displaySquare (Just (Piece White Rook)) = '♖'
displaySquare (Just (Piece White Bishop)) = '♗'
displaySquare (Just (Piece White Knight)) = '♘'
displaySquare (Just (Piece White Pawn)) = '♙'
displaySquare (Just (Piece Black pt))
= toEnum . (+6) -- Unicode U+2659 WHITE CHESS PAWN
-- is followed by BLACK CHESS KING, etc.
. fromEnum . displaySquare . Just $ Piece White pt
displaySquare Nothing = ' '
关键是:模式匹配的工作原理与相等比较的工作原理根本不同(通常更可取)
通过模式匹配,您只需查看片段数据是否“适合您感兴趣的粗略形状”。您可以完全忽略您不感兴趣的部分数据,就像您尝试使用Just(Piece White)
一样。因此,模式匹配通常是一种“模糊操作”
OTOH,=
只是一个运算符(),它接受两个值并确定它们是否完全相等Piece White实际上不是一个值,您需要给出一个具体的PieceType
,以适应\uCode>间隙。这就是GHC试图用type:PieceType
告诉你的信息发现了一个洞“uu”:如果你真的需要建立一个平方
值,你需要提供一个PieceType
值。当你处理复杂问题时,这些键入的孔非常有用:只需从外部开始,例如,你可以从n==Piece\uuu
开始,GHC会告诉你它期望在第一个间隙中有一个PieceColor
,你会插入White
,GHC会告诉你它期望在第二个间隙中有一个PieceType
,等等
只是,在displaySquare
函数中,实际上构造一个具体的Square
值来进行比较是没有意义的:您真正想要做的是解构这些值。这就是模式匹配更方便的地方。事实上,我会把这个函数写成一个大的子句列表:
displaySquare (Just (Piece White King)) = '♔'
displaySquare (Just (Piece White Queen)) = '♕'
displaySquare (Just (Piece White Rook)) = '♖'
displaySquare (Just (Piece White Bishop)) = '♗'
displaySquare (Just (Piece White Knight)) = '♘'
displaySquare (Just (Piece White Pawn)) = '♙'
displaySquare (Just (Piece Black pt))
= toEnum . (+6) -- Unicode U+2659 WHITE CHESS PAWN
-- is followed by BLACK CHESS KING, etc.
. fromEnum . displaySquare . Just $ Piece White pt
displaySquare Nothing = ' '
这将无法编译,因为助手函数不在第2行的作用域中。它还会抛出一个错误,因为n@(仅白色)
应该是n@(仅白色)
。但是,修复这个问题并将helper函数定义为常规函数是可行的!出于好奇,有没有办法定义helper函数,使其保留在displaySquare中?用谷歌搜索多个函数的helper函数似乎并没有带来任何效果。答案很好!使用case
可以让它更有效可读。@NicolásSiplis您还可以保留其他防护装置(并避免移动辅助功能)