简体   繁体   English

如何在Haskell中创建不同类型的对象的列表?

[英]How can I create a list of objects of different type in Haskell?

I have these two Haskell data types: 我有以下两种Haskell数据类型:

data Code 
    = Code_A | Code_B | Code C
    deriving (Eq,Show)

data ListObject = Code | Int

I need to make a list that contains ListObjects . 我需要制作一个包含ListObjects的列表。 That is both integer values and codes ([1,2, Code_A, 3]) . 那既是整数值又是代码([1,2, Code_A, 3]) I know it should be possible but I just can't figure the syntax for it. 我知道应该有可能,但是我无法弄清楚它的语法。 Haskell can do some neat stuff but its syntax sucks. Haskell可以做一些巧妙的事情,但是其语法很糟糕。 Some help would be greatly appreciated. 一些帮助将不胜感激。

How about: 怎么样:

data Code 
     = Code_A | Code_B | Code_C
     deriving (Eq,Show)

data ListObject = ListCode Code | Value Int

objects :: [ListObject]
objects = [ListCode Code_A, Value 0]

That should do it. 那应该做。

The way you defined ListObject there are exactly two values of type ListObject : Code and Value . 定义ListObject的方式中,存在ListObject类型的两个值: CodeValue This is somewhat similar to enum ListObject {Code, Int} in C-like languages. 这有点类似于C语言中的enum ListObject {Code, Int} The costructor Code of type ListObject doesn't actually have anything to do with the type Code (same goes for Int ). 该costructor Code类型ListObject实际上不会有什么做的类型Code (同样适用于Int )。

So the way you defined it, a list of ListObject s would look like this: [Int, Code, Code, Int] . 因此,按照定义方式, ListObject的列表如下所示: [Int, Code, Code, Int] Of course that's not very useful. 当然那不是很有用。

What you probably want to do is something like this: data ListObject = CodeObject Code | IntObject Int 您可能想要做的是这样的: data ListObject = CodeObject Code | IntObject Int data ListObject = CodeObject Code | IntObject Int , which says "A ListObject is either an IntObject containing an Int, or a CodeObject containing a Code". data ListObject = CodeObject Code | IntObject Int ,它表示“ ListObject是包含Int的IntObject或包含代码的CodeObject”。

With that definition your list may look like [IntObject 42, Code Code_A, IntObject 23] . 使用该定义,您的列表可能看起来像[IntObject 42, Code Code_A, IntObject 23]

Some terminology is certainly needed. 当然需要一些术语。 In your code the tokens Code and ListObject are both "data types" and are in one name space. 在您的代码中,标记CodeListObject都是“数据类型”,并且在一个名称空间中。

A second concept is "Constructors", commands used to construct a particular instance of a data type. 第二个概念是“构造函数”,用于构造数据类型的特定实例的命令。 Constructors live in a separate name space - in your code these include the tokens Code_A , Code_B , Code_C and Code , Int . 构造函数位于单独的名称空间中-在您的代码中,这些Code_A包括标记Code_ACode_BCode_CCodeInt

Here's your confusion. 这是您的困惑。 You think you've said you want a data type called ListObject which contains data types of either Code or Int . 您认为您已经说过想要一个名为ListObject数据类型,其中包含CodeInt数据类型。 When instead you've defined ListObject with two Null (zero content) constructors called Code and Int . 相反,您使用两个名为CodeInt Null(零内容)构造函数定义了ListObject

Now we get to the solution, which Robert jumped to in his answer: 现在我们找到解决方案,Robert跳到他的回答中:

data ListObject = CodeObject Code | IntObject Int

Notice the syntax: 注意语法:

data [DataTypeName] = [ConstructorName] [Component Data Type 1] | ...

Working out the above code in English, we have a data type ListObject with two constructors ( CodeObject and IntObject ), each constructor takes a single data type ( Code and Int respectively) to create an instance of the ListObject type. 用英语编写以上代码,我们有一个数据类型ListObject带有两个构造函数( CodeObjectIntObject ),每个构造函数都采用一个数据类型(分别为CodeInt )来创建ListObject类型的实例。

Stop here if you understand! 如果您理解,请在这里停止! Some people have an easier time (and some a harder time) with using function type signatures for constructors. 对于构造函数使用函数类型签名,有些人度过了更轻松的时光(有些日子更艰难)。 The above ListObject using such notation looks like: 上面使用这种表示法的ListObject看起来像:

data ListObject where
        CodeObject :: Code -> ListObject
        IntObject  :: Int -> ListObject

This makes it clear that CodeObject is some sort of function accepting Code and returning a ListObject . 这清楚表明CodeObject是某种接受Code并返回ListObject的函数。 Similar story with IntObject IntObject类似的故事

Example: heterogeneous lists 示例:异构列表

The premise behind Haskell's typeclass system is grouping types that all share a common property. Haskell的typeclass系统背后的前提是对所有具有共同属性的类型进行分组。 So if you know a type instantiates some class C , you know certain things about that type. 因此,如果您知道类型实例化了某些类C ,那么您就知道有关该类型的某些知识。 For example, Int instantiates Eq , so we know that elements of Int can be compared for equality. 例如,Int实例化Eq ,因此我们知道可以比较Int的元素是否相等。

Suppose we have a group of values, and we don't know if they are all the same type, but we do know they all instantiate some class, ie we know all the values have a certain property. 假设我们有一组值,并且我们不知道它们是否都是同一类型,但是我们知道它们都实例化了某个类, 我们知道所有值都具有特定的属性。 It might be useful to throw all these values into a list. 将所有这些值放入列表中可能会很有用。 We can't do this normally because lists are homogeneous with respect to types: they can only contain a single type. 我们通常不能这样做,因为列表在类型方面是同质的:它们只能包含一个类型。 However, existential types allow us to loosen this requirement by defining a 'type hider' or 'type box': 但是,存在类型允许我们通过定义“类型隐藏器”或“类型框”来放宽此要求:

Example: Constructing a heterogeneous list 示例:构造一个异构列表

 data ShowBox = forall s. Show s => SB s heteroList :: [ShowBox] heteroList = [SB (), SB 5, SB True] 

We won't explain precisely what we mean by that datatype definition, but its meaning should be clear to your intuition. 我们不会确切解释该数据类型定义的含义,但是您的直觉应该清楚其含义。 The important thing is that we're calling the constructor on three values of different types, and we place them all into a list, so we must end up with the same type for each one. 重要的是,我们要在三个不同类型的值上调用构造函数,并将它们全部放入一个列表中,因此对于每个值,我们都必须以相同的类型结束。 Essentially this is because our use of the forall keyword gives our constructor the type SB :: forall s. Show s => s -> ShowBox 本质上这是因为我们对forall关键字的使用使构造函数的类型为SB :: forall s. Show s => s -> ShowBox SB :: forall s. Show s => s -> ShowBox . SB :: forall s. Show s => s -> ShowBox If we were now writing a function that we were intending to pass heteroList , we couldn't apply any functions like not to the values inside the SB because they might not be Bool s. 如果现在正在编写要传递heteroList的函数,则无法将类似not的任何函数应用于SB内部的值,因为它们可能不是Bool But we do know something about each of the elements: they can be converted to a string via show. 但是我们确实了解每个元素:可以通过show将它们转换为字符串。 In fact, that's pretty much the only thing we know about them. 实际上,这几乎是我们唯一了解的内容。

Example: Using our heterogeneous list 示例:使用我们的异构列表

  instance Show ShowBox where show (SB s) = show s -- (*) see the comment in the text below f :: [ShowBox] -> IO () f xs = mapM_ print xs main = f heteroList 

Let's expand on this a bit more. 让我们进一步扩展一下。 In the definition of show for ShowBox – the line marked with (*) see the comment in the text below – we don't know the type of s . ShowBox的show定义中-用(*)标记的行请参见下面的文本中的注释-我们不知道s的类型。 But as we mentioned, we do know that the type is an instance of Show due to the constraint on the SB constructor. 但是正如我们提到的,由于SB构造函数的限制,我们确实知道该类型是Show的实例。 Therefore, it's legal to use the function show on s , as seen in the right-hand side of the function definition. 因此,使用在s显示的函数是合法s ,如函数定义右侧所示。

As for f , recall the type of print : 至于f ,请记住print类型:

Example: Types of the functions involved 示例:所涉及功能的类型

  print :: Show s => s -> IO () -- print x = putStrLn (show x) mapM_ :: (a -> mb) -> [a] -> m () mapM_ print :: Show s => [s] -> IO () 

As we just declared ShowBox an instance of Show , we can print the values in the list. 正如我们刚刚将ShowBox声明为Show的实例一样,我们可以在列表中打印值。

Applying it to your situation, you get 将其应用于您的情况,您会得到

{-# LANGUAGE ExistentialQuantification #-}

data ShowBox = forall a. Show a => SB a

instance Show ShowBox where
  show (SB s) = show s

data Code = Code_A | Code_B | Code_C
  deriving (Eq,Show)

l :: [ShowBox]
l = [SB Code_A, SB 3, SB Code_C, SB 0, SB "but..."]

f = mapM_ print l

Output: 输出:

ghci> f
Code_A
3
Code_C
0
"but..."

The other answers show the approach you likely want, but this is here for completeness and to offer food for thought. 其他答案显示了您可能想要的方法,但这是出于完整性和思考的目的。 As the SB "but..." bit suggests, ShowBox is more permissive. 正如SB "but..."位所暗示的那样, ShowBox更宽松。

Give us more information about the problem you're working on, and we can give you more helpful suggestions that are specific to your situation. 向我们提供有关您正在解决的问题的更多信息,并且我们可以为您提供针对您情况的更有用的建议。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在C#的通用列表中存储不同类型的对象 - How I can store different type of objects in a generic list in C# 如何使用实现相同接口的不同对象对列表进行排序? - How can I sort a list with different objects that implement the same interface? 如何在Delphi中获取具有泛型类型的对象列表 - How can I get List of objects with Generic type in Delphi 当func &lt;&gt;的列表处理不同的对象时,如何接受Func &lt;&gt;委托的列表并返回列表的列表 - How can I accept a list of Func<> delegates and return a list of list when the List of func<> processes different objects 如何创建转换List的Extension方法 <T> 到另一个清单 <T> 在运行时的不同类型? - How do I create an Extension method that converts a List<T> to another List<T> of a different type at runtime? Java - 如何创建一个 class 可以处理特定 [restricted] 类型的 Generics 对象? - Java - How can I create a class that can process objects of Generics of a specific [restricted] type? 我如何创建ac#方法,该方法将返回与作为参数的具有相同元素类型的相同集合类型? - How can i create a c# method that will return the same collection type as as argument with a different element type? 如何将具有不同类型的泛型类对象传递给一个List? - How to pass generic class objects with a different type to one List? 如何创建未知值类型的字典对象列表? - How to create a list of dictionary objects of unknown value type? 如何使用List对象的多态性? - How can I utilize Polymorphism with List objects?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM