【问题标题】:How can I create a list of objects of different type in Haskell?如何在 Haskell 中创建不同类型的对象列表?
【发布时间】:2010-09-15 23:49:13
【问题描述】:

我有这两种 Haskell 数据类型:

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

data ListObject = Code | Int

我需要创建一个包含ListObjects 的列表。那是整数值和代码([1,2, Code_A, 3])。我知道这应该是可能的,但我只是想不出它的语法。 Haskell 可以做一些简洁的事情,但它的语法很烂。一些帮助将不胜感激。

【问题讨论】:

  • Haskell 的语法并不糟糕。它与类 C 语言不同,这是一件非常好的事情。函数式语言除了命令式语言还有其他需求。
  • 很难得到一个可以包含多个类型的列表与 Haskell 的语法有什么关系?
  • 如果你连语法是什么都不知道,你怎么知道它很烂?仅供参考,从语言定义中复制并粘贴:data [context =>] simpletype = constrs [deriving] 。现在你知道了。

标签: generics list haskell


【解决方案1】:

怎么样:

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]

应该可以的。

【讨论】:

    【解决方案2】:

    您定义ListObject 的方式恰好有两个ListObject 类型的值:CodeValue。这有点类似于 C 语言中的enum ListObject {Code, Int}ListObject 类型的构造函数 Code 实际上与 Code 类型没有任何关系(Int 也是如此)。

    因此,按照您定义的方式,ListObjects 的列表将如下所示:[Int, Code, Code, Int]。当然这不是很有用。

    您可能想要做的是这样的事情:data ListObject = CodeObject Code | IntObject Int,它表示“ListObject 要么是包含 Int 的 IntObject,要么是包含 Code 的 CodeObject”。

    根据该定义,您的列表可能类似于[IntObject 42, Code Code_A, IntObject 23]

    【讨论】:

      【解决方案3】:

      当然需要一些术语。在您的代码中,标记 CodeListObject 都是“数据类型”并且在一个命名空间中。

      第二个概念是“构造函数”,用于构造数据类型的特定实例的命令。构造函数位于单独的名称空间中 - 在您的代码中,这些标记包括 Code_ACode_BCode_CCodeInt

      这就是你的困惑。你认为你说过你想要一个名为ListObject 的数据类型,它包含CodeInt 的数据类型。相反,您使用两个 Null(零内容)构造函数定义了 ListObject,分别称为 CodeInt

      现在我们得到解决方案,罗伯特在他的回答中跳到了这个问题上:

      data ListObject = CodeObject Code | IntObject Int
      

      注意语法:

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

      用英文计算上面的代码,我们有一个数据类型ListObject 有两个构造函数(CodeObjectIntObject),每个构造函数都有一个数据类型(分别为CodeInt)来创建ListObject 类型的实例。

      如果你明白了就到此为止!有些人在使用构造函数的函数类型签名时会更容易(而有些人则更难)。上面的ListObject 使用这样的符号看起来像:

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

      这清楚地表明CodeObject 是某种接受Code 并返回ListObject 的函数。与IntObject类似的故事

      【讨论】:

        【解决方案4】:

        Example: heterogeneous lists

        Haskell 类型类系统背后的前提是对所有共享一个公共属性的类型进行分组。因此,如果您知道一个类型实例化了某个类C,那么您就知道该类型的某些事情。例如,Int 实例化了Eq,因此我们知道可以比较 Int 的元素是否相等。

        假设我们有一组值,我们不知道它们是否都是同一类型,但我们知道它们都实例化了某个类,我们知道所有值都有一个一定的财产。将所有这些值放入列表中可能很有用。我们通常不能这样做,因为列表在类型方面是同质的:它们只能包含一个类型。但是,存在类型允许我们通过定义“类型隐藏器”或“类型框”来放松这一要求:

        示例:构造异构列表

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

        我们不会准确解释该数据类型定义的含义,但您的直觉应该很清楚它的含义。重要的是,我们在三个不同类型的值上调用构造函数,并将它们全部放入一个列表中,因此我们必须为每个值都使用相同的类型。本质上这是因为我们使用forall 关键字为我们的构造函数提供了SB :: forall s. Show s => s -> ShowBox 类型。如果我们现在正在编写一个打算传递heteroList 的函数,我们不能将任何函数应用到SB 中的值,因为它们可能不是Bools。但是我们确实知道一些关于每个元素的信息:它们可以通过 show 转换为字符串。事实上,这几乎是我们对它们的唯一了解。

        示例:使用我们的异构列表

         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
        

        让我们进一步扩展一下。在ShowBox 的show 定义中——标有(*) 的行见下文中的注释——我们不知道s 的类型。但正如我们所提到的,由于SB 构造函数的约束,我们确实知道该类型是Show 的实例。因此,在s 上使用函数 show 是合法的,如函数定义的右侧所示。

        至于f,回忆一下print的类型:

        示例:涉及的函数类型

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

        由于我们刚刚将 ShowBox 声明为 Show 的实例,因此我们可以打印列表中的值。

        将其应用于您的情况,您会得到

        {-# 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
        

        输出:

        ghci> f
        代码_A
        3
        代码_C
        0
        “但是……”

        其他答案显示了您可能想要的方法,但这是为了完整性并提供思考的食物。正如SB "but..." 位所暗示的那样,ShowBox 更宽松。

        向我们提供有关您正在解决的问题的更多信息,我们可以针对您的具体情况为您提供更多有用的建议。

        【讨论】:

        • 复制和粘贴与发帖人需要的内容无关的信息是否比说“这里有一个相关链接”更好?
        猜你喜欢
        • 1970-01-01
        • 2021-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-22
        相关资源
        最近更新 更多