我尝试将列表中的数据' profile1'转换为名为' DataSubject'的自定义类型。
我将此传递给了一个函数' makeDS'试图进行这种转换 - 但是以下内容并不起作用:
type Name = String
type Age = Int
type Iq = Int
type Language = String
data DataSubject = DS {name :: Name, age :: Age, iq :: Iq, language :: Language} deriving (Show)
data Contain = Name String | Age Int | Iq Int | Language String deriving (Show) --Use so list can take multiple types
profile1 = [Name "Bob", Age 22, Iq 100, Language "French"]
makeDS :: [Contain] -> DataSubject
makeDS t = DS {name = t!!0, age = t!!1, iq = t!!2, language = t!!3}
main = do
let x = makeDS profile1
putStrLn $ show x
错误:
Couldn't match type ‘Contain’ with ‘[Char]’
我刚刚开始使用Haskell - 有人可以就我的错误提出建议吗?如果有更好的方法可以做到这一点?
答案 0 :(得分:7)
在makeDS
的定义中,变量t
的类型为[Contain]
(即Contain
的列表),所以当你说t!!0
时将提取该列表的第一个元素,其类型为Contain
。问题是name
的{{1}}字段包含DataSubject
(String
的别名)。因此,您试图将[Char]
存储在Contain
的位置,这是不可能的,因为类型不同。您需要在代码中使用不同的方法。
一个问题是,每个[Char]
值代表Contain
的单个字段。因此,如果我们获得了DataSubject
的列表,则无法保证将按特定顺序(例如Contain
,然后是Name
等)提供值,或者甚至是提供所有字段。即使您始终按照约定在代码中按特定顺序提供所有字段,haskell也不可能知道这一点。一个不依赖于订单的解决方案是尝试构建" Age
对象一步一步地从"空"开始DataSubject
然后检查DataSubject
列表并添加相应的Contain
字段:
DataSubject
所以在这里,我定义了makeDS :: [Contain] -> DataSubject
makeDS = foldr updateDS emptyDS
where
updateDS (Name s) ds = ds {name = s}
updateDS (Age n) ds = ds {age = n}
updateDS (Iq n) ds = ds {iq = n}
updateDS (Language s) ds = ds {language = s}
emptyDS = DS {name = "", age = 0, iq = 0, language = ""}
这是一个"空" emptyDS
对象和名为DataSubject
的函数,它采用(单个)updateDS
和Contain
并根据{{1}指定的字段更新DataSubject
然后它返回它。最后,我使用折叠运行使用DataSubject
重复更新Contain
(以DataSubject
开头)。
答案 1 :(得分:3)
您有类型不匹配。您有Contain
的列表。所以当你使用
t !! 0
您获得了Contain
,而不是String
,这是name
中DS
所必需的。您需要一个函数Contain -> Name
,例如
containToName :: Contain -> Name
containToName (Name xs) = xs
containToName _ = error "not a name"
但是,这是一个部分功能,因为containToName (Age 12)
会导致错误。
请注意,这与类型类无关。现在,如果你想使用profile1
,一种方法就是使用
profile1 :: DataSubject
而不是
profile1 :: [Contain]
e.g。
profile1 :: DataSubject
profile1 = DS "Bob" 22 100 "French"
毕竟, type [Contain]
中没有任何内容可以确保您拥有完整DataSubject
的所有要素。
如果有更好的方法可以做到这一点?
这取决于你想做什么。如果您只想处理DataSubject
,请不要使用Contain
的临时列表。如果你想处理用户输入(或类似的),它会变得有点棘手。
答案 2 :(得分:1)
DataSubject
声明表示我们需要Name
字段name
。 Name
与String
相同。因此,在DS {name = t!!0, ...}
表达式中,我们需要t !! 0
来返回String
。但是,t !! 0
会返回t
的元素,t
的类型为[Contain]
。因此t !! 0
的类型为Contain
,与String
不同。
要修复此类型错误,您需要将Contain
转换为String
,可能是这样:
DS { name = case t !! 0 of Name s => s, ... }