Haskell 数据字段中的多态性
Polymorphism in Haskell data fields
我正在尝试通过创建一个简单的国际象棋游戏来学习 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 = 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 returns 出现以下错误:
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.
我不太确定错误试图告诉我什么。如果非要我猜的话,我会说问题是将数据字段定义为空洞“_”,因为这意味着该函数可以将任何类型作为数据字段,这显然与给定的类型签名冲突。我的假设是否正确,我该如何解决?
这是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 在评论中提到的,您需要将辅助函数放在顶层。
将辅助函数保持在本地的另一种编写方式:
displaySquare n =
case n of
Nothing -> ' '
Just (Piece White _) -> displaySquare' n
_ -> toLower (displaySquare' n)
where
displaySquare' x = ...
在这种情况下,n
绑定到 displaySquare
的第一个参数,因此您不需要使用 n@...
语法。
关键在于:模式匹配的工作原理与相等比较根本不同(通常更可取)!
通过模式匹配,你只看一块数据是否“符合你感兴趣的一些粗略形状”。您可能会完全忽略您 不 感兴趣的部分数据,就像您尝试使用 Just (Piece White _)
一样。因此,模式匹配通常是一种“模糊操作”。
OTOH,==
只是一个运算符 (library-defined function),它接受两个值并确定它们是否 完全相等 。 Piece White _
实际上不是一个值,您需要给出具体的 PieceType
以适应 _
差距。这就是 GHC 试图通过消息 Found hole ‘_’ with type: PieceType
告诉您的内容:如果您确实需要建立一个 Square
值,则需要提供一个 PieceType
值。当你处理复杂的问题时,这些类型化的漏洞非常有用:从外部开始,例如你可以从 n == Piece _ _
开始,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 = ' '
我正在尝试通过创建一个简单的国际象棋游戏来学习 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 = 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 returns 出现以下错误:
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.
我不太确定错误试图告诉我什么。如果非要我猜的话,我会说问题是将数据字段定义为空洞“_”,因为这意味着该函数可以将任何类型作为数据字段,这显然与给定的类型签名冲突。我的假设是否正确,我该如何解决?
这是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 在评论中提到的,您需要将辅助函数放在顶层。
将辅助函数保持在本地的另一种编写方式:
displaySquare n =
case n of
Nothing -> ' '
Just (Piece White _) -> displaySquare' n
_ -> toLower (displaySquare' n)
where
displaySquare' x = ...
在这种情况下,n
绑定到 displaySquare
的第一个参数,因此您不需要使用 n@...
语法。
关键在于:模式匹配的工作原理与相等比较根本不同(通常更可取)!
通过模式匹配,你只看一块数据是否“符合你感兴趣的一些粗略形状”。您可能会完全忽略您 不 感兴趣的部分数据,就像您尝试使用 Just (Piece White _)
一样。因此,模式匹配通常是一种“模糊操作”。
OTOH,==
只是一个运算符 (library-defined function),它接受两个值并确定它们是否 完全相等 。 Piece White _
实际上不是一个值,您需要给出具体的 PieceType
以适应 _
差距。这就是 GHC 试图通过消息 Found hole ‘_’ with type: PieceType
告诉您的内容:如果您确实需要建立一个 Square
值,则需要提供一个 PieceType
值。当你处理复杂的问题时,这些类型化的漏洞非常有用:从外部开始,例如你可以从 n == Piece _ _
开始,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 = ' '