实际上,我正在尝试实现一个自定义数据类型,它是string类的子集(例如,这样我就可以对错误输入进行一些类型检查)。但我一辈子都找不到任何地方来解释每个类型关键字的含义。根据以下内容:Haskell Docs这是7个基本的:Eq,Ord,Enum,Ix,Bounded,Read,和Show。除了打印语句需要的Show和布尔风格比较检查需要的Eq,我不完全确定其他5个,谷歌也没有提供太多帮助。所以我希望你们能给我一些启发,也许能给我指出正确的方向。
于是疑问道:
1.这7个基本派生词是什么/添加它们有什么作用?
1.有没有办法在ghci中运行类似derives String
或derives "abc"
的东西?
下面是我正在编写的代码。实际上我刚刚创建了Card数据类型,正如你所看到的,它只是一个字符串,用于执行一些更严格的参数检查。我正在尝试将它传递给match,以匹配之前接受的3个字符串作为参数但是为了使用字符串操作语法(比如将其分解为一个字符列表),我认为我需要确定适当的derive关键字,以便它以这种方式工作。
match :: Card -> Card -> Card -> Bool
match [] [] [] = True
match [a] [b] [c] = (a == b && b == c)
|| (a /= b && b /= c && a /= c)
match (a:as) (b:bs) (c:cs) = match [a] [b] [c]
&& match as bs cs
data Card = Card String
deriving (Show,Eq)
card :: String -> Card
card x | length x /= 4 = error "Card input must be 4 characters."
| (x!!0 /= 'o') && (x!!0 /= 's') && (x!!0 /= 'd') = error "char 0 (shape) must be o s or d."
| (x!!1 /= '1') && (x!!1 /= '2') && (x!!1 /= '3') = error "char 1 (number) must be 1 2 or 3."
| (x!!2 /= 'r') && (x!!2 /= 'p') && (x!!2 /= 'g') = error "char 2 (color) must be r p or g."
| (x!!3 /= 'f') && (x!!3 /= 's') && (x!!3 /= 'o') = error "char 3 (shade) must be f s or o."
| otherwise = Card x
(更新示例1)
data Card = Card Shape Number Color Shade deriving (Show, Eq)
data Shape = Oval | Squiggle | Diamond deriving Eq
instance Show Shape where
show Oval = "Oval"
show Squiggle = "Squiggle"
show Diamond = "Diamond"
data Number = One | Two | Three deriving Eq
instance Show Number where
show One = "One"
show Two = "Two"
show Three = "Three"
data Color = Red | Pink | Green deriving Eq
instance Show Color where
show Red = "Red"
show Pink = "Pink"
show Green = "Green"
data Shade = Full | Half | Empty deriving Eq
instance Show Shade where
show Full = "Full"
show Half = "Half"
show Empty = "Empty"
shape :: Char -> Either ShapeError Shape
shape 'o' = Right Oval
shape 's' = Right Squiggle
shape 'd' = Right Diamond
shape x = Left (NotOSD x)
number :: Char -> Either NumberError Number
number '1' = Right One
number '2' = Right Two
number '3' = Right Three
number x = Left (Not123 x)
color :: Char -> Either ColorError Color
color 'r' = Right Red
color 'p' = Right Pink
color 'g' = Right Green
color x = Left (NotRPG x)
shade :: Char -> Either ShadeError Shade
shade 'f' = Right Full
shade 's' = Right Half
shade 'o' = Right Empty
shade x = Left (NotFSO x)
card :: String -> Either SetError Card
card [shp, n, c, shd] = Card <$> shape shp <*> number n <*> color c <*> shade shd
card x = Left (NotCard x)
data CardError = NotCard String
instance Show CardError where
show (NotCard x) = "Card must be 4 characters long, not " ++ show x
data SetError = CardError | ShapeError | NumberError | ColorError | ShadeError deriving Eq
data ShapeError = NotOSD Char
instance Show ShapeError where
show (NotOSD x) = "Shape must be o, s, or d, not " ++ show x
data NumberError = Not123 Char
instance Show NumberError where
show (Not123 x) = "Number must be 1, 2, or 3, not " ++ show x
data ColorError = NotRPG Char
instance Show ColorError where
show (NotRPG x) = "Color must be r, p, or g, not " ++ show x
data ShadeError = NotFSO Char
instance Show ShadeError where
show (NotFSO x) = "Shade must be f, s, or o, not " ++ show x
当前错误
match.hs:84:34:
Couldn't match type ‘ShapeError’ with ‘SetError’
Expected type: Either SetError Shape
Actual type: Either ShapeError Shape
In the second argument of ‘(<$>)’, namely ‘shape shp’
In the first argument of ‘(<*>)’, namely ‘Card <$> shape shp’
match.hs:84:48:
Couldn't match type ‘NumberError’ with ‘SetError’
Expected type: Either SetError Number
Actual type: Either NumberError Number
In the second argument of ‘(<*>)’, namely ‘number n’
In the first argument of ‘(<*>)’, namely
‘Card <$> shape shp <*> number n’
match.hs:84:61:
Couldn't match type ‘ColorError’ with ‘SetError’
Expected type: Either SetError Color
Actual type: Either ColorError Color
In the second argument of ‘(<*>)’, namely ‘color c’
In the first argument of ‘(<*>)’, namely
‘Card <$> shape shp <*> number n <*> color c’
match.hs:84:73:
Couldn't match type ‘ShadeError’ with ‘SetError’
Expected type: Either SetError Shade
Actual type: Either ShadeError Shade
In the second argument of ‘(<*>)’, namely ‘shade shd’
In the expression:
Card <$> shape shp <*> number n <*> color c <*> shade shd
match.hs:85:16:
Couldn't match expected type ‘SetError’
with actual type ‘CardError’
In the first argument of ‘Left’, namely ‘(NotCard x)’
In the expression: Left (NotCard x)
Failed, modules loaded: none.
1条答案
按热度按时间dxxyhpgq1#
在编写Haskell时,不要害怕定义大量的类型和小函数。小函数更容易理解,并且可以用各种方式组合来定义更大的函数。
您是否关心 * 类型类 *...
为了让您的问题马上解决,
Show
示例仅用于从某个值生成String
。Eq
用于比较两个具有==
的相同类型的值。Read
Ord
Enum
Bounded
Read
类似于Show
的逆函数,但通常不使用。(下面显示的解析器比Read
示例更容易编写,并且您将无法使用派生的Read
示例来定义大多数类型。)其他三个解析器可能与程序的其他部分相关,但与您所问的任何代码无关。派生的
Show
示例基本上只是将数据构造函数的名称转换为字符串,但为了保持良好的风格,该字符串应该是有效的Haskell代码。派生的Eq
示例只是检查两个数据构造函数是否相同。带参数的数据构造函数的定义是递归派生的:将使用"Card"
和show x
来定义show (Card x)
,而将Card c1 == Card c2
定义为c1 == c2
。不过,您的大部分问题都是关于模式匹配的(如注解中所提到的),这与类型类无关。
......还是 * 类型 *?
让我们从你的四个基本概念开始:形状、数字、颜色和底纹。为每个类型定义一个单独的类型。下面是
Shape
的类型示例:尽可能为数据构造函数使用描述性名称(
O
真的是Oval
的缩写吗?如果是,请使用Oval
),并记住名称必须全局唯一(无论如何,在模块内),因此不能将O
同时用于Shape
和Shade
。有时从构造函数名派生的字符串对你的Show
示例来说就足够了,有时就不够了。如果不够的话,不要定义一个Show
示例,而是编写你自己的Thing -> String
函数。是派生
Show
示例还是手动编写它可能取决于您选择的数据构造函数名称以及您 * 希望 * 从值中得到什么字符串。派生的Eq
示例几乎肯定是足够的,因为它们不依赖于您用于构造函数的名称。(As顺便说一句,
Number
比较棘手,因为不能使用1
、2
或3
作为数据构造函数名(除非需要对数字进行数学运算,否则可以考虑使用data Number = One | Two | Three
之类的名称,而不是过于宽泛的data Number = Number Int
)。现在,您的
Card
类型可以是四个属性类型的简单组合,而不仅仅是原始字符串的 Package 器。(But对于
Show
,任何具有自己的字符串转换函数的属性都可能迫使您编写自己的Card -> String
函数,而不是依赖Show
示例。)正在解析:字符串到值
你会希望函数能将字符解析成一个合适的值,而不仅仅是检查它是否能代表一个合适的值。(
'o'
代表ShapeO
),有时候它不是('x'
不代表Shape
)。所以你的解析器应该考虑到这一点,但不是简单地用error
引发异常。相反,让返回类型告诉调用者发生了什么,并让 * 他们 * 决定程序是否需要被终止。2例如,shape
返回Right
或Left
值这一事实通常足以让调用方决定下一步要做什么;由Left
Package 的错误消息更多地用于向用户报告错误。一旦您拥有了各个属性的所有解析器,那么
Card
的解析器就变得非常简单了,这要归功于Either String
类型的Applicative
示例。如果有任何属性产生
Left
值,card
会传回第一个无法剖析之属性的Left
值。只有当输入字串刚好有4个正确的字符时,char
才会产生以Right
Package 的Card
值。您可能还决定定义专用的错误类型,而不是使用任意字符串。
然后
基本上,捕获错误类型中有关错误的 * 信息 *,并让其
Show
示例生成一个人类可读的字符串 *,其中 * 包含该信息(如果需要)。如果各部分相等,则各部分相等
match
同样是平凡的,它利用了为Card
派生的Eq
示例,而Card
是从所有属性类型的Eq
示例派生的。