在列表列表中,施加相同的长度

时间:2012-09-19 09:44:12

标签: haskell

我有这种数据类型,应该代表一个表:

data R = R [Bool]  deriving Eq -- Row
data T = T [R]     deriving Eq -- Table

问题是它允许拥有不同长度的行表,例如:

tab =T [R [True, False, True, True],
        R [False, False, True, False],
        R [False, False, False, True],
        R [False, False]]

是否可以修改T的数据定义以强制所有R元素具有相同的长度?

4 个答案:

答案 0 :(得分:10)

是的,有一种非常标准的方法来实现这一目标。但是,您支付的价格是您不能使用标准列表功能(因为您不会使用标准列表)。这个想法是这样的:我们首先会有一个脊椎告诉所有“列表”有多长,然后我们将在脊柱的底部有实际的列表。您可以通过多种方式对列表的长度进行编码;下面,我将展示如何使用简单的一元编号系统来完成它,但您当然可以使用其他编号系统设计更高效的版本。

data BalancedLists_ a as
    = Nil [as]
    | Cons (BalancedLists_ a (a, as))

type BalancedLists a = BalancedLists_ a ()

例如,包含两个长度为3的列表的平衡列表如下所示:

Cons (Cons (Cons (Nil [(1, (2, (3, ()))), (4, (5, (6, ())))])))

有一篇精彩的论文将此技术扩展到Ralf Hinze称为Manufacturing Datatypes的一百个不同方向。

答案 1 :(得分:8)

可以使用DataKinds执行此操作。但这可能过于复杂:

{-# LANGUAGE DataKinds, KindSignatures, GADTs #-}
-- requires 7.4.1, I think

data Nat = S Nat | Z

infixr 0 :.
data R (n :: Nat) where
  Nil :: R Z                     -- like []
  (:.) :: Bool -> R n -> R (S n) -- and (:)

data T (n :: Nat) = T [R n]

-- OK
test1 = T [(True :. True :. Nil), (True :. False :. Nil)]

-- will fail
test2 = T [(True :. True :. Nil), (False :. Nil)]

我更推荐使用智能构造函数的@MathematicalOrchids替代方法。


编辑:DataKinds做了什么。

DataKinds扩展允许编译器为每个写入的数据类型自动创建除*以外的新类型,并从构造函数中生成此类型的新类型。

所以Nat除了是一个简单的ADT之外,还会产生一种Nat类型构造函数Z :: NatS :: Nat -> Nat。此SMaybe :: * -> *相当 - 它不会使用所有类型的类型,而是您的新类型Nat,只能通过自然数字的表示来居住。

关键是,现在您还可以定义混合类型的类型构造函数。典型的例子是Vec

data Vec (n :: Nat) (a :: *) where {-...-}

Vec :: Nat -> * -> *种。同样,T也有T :: Nat -> *种。这样就可以将它与类型编码的常量长度一起使用,如果将两行不同的长度放在一起,则会导致类型错误。

虽然这看起来非常强大,但事实上它受到了限制。要从这些表示中获取所有内容,应使用Agda依赖类型的语言。

答案 2 :(得分:4)

list 类型表示任意大小的容器。您可以使用元组来强制执行特定大小 - 但它只适用于“小”大小。例如:

data R = R (Bool, Bool, Bool, Bool) deriving Eq

现在每行总是包含4个单元格。

如果您真正想要的是强制行可以是任何大小,只要它对于表中的所有行相同 ...要困难得多。有几种方法可以在类型系统中对此进行编码,但它们都不是特别“简单”。

另一种方法是在运行时强制执行该条件,而不是在编译时尝试保证它。您可以编写一个定义行和表类型的模块,但隐藏它们的定义,并且只公开用于处理这些类型的函数,这些函数保留您想要的不变量(即,所有行等长)。

答案 3 :(得分:1)

另一种方法是使用Data.Array。关于它的一个好处是它允许真正的多维数组而不是数组数组。只需使用元组索引Array