在Haskell中使用元组定义递归数据类型

时间:2012-11-05 18:14:10

标签: haskell

我在Haskell中定义新数据类型时遇到了麻烦。

我正在尝试创建一个数据类型NumPair,它将是一个包含两个整数,或整数和另一个NumPair的元组。

例如,(2, 2), (0, 5), (1, (2, 3)) and (0, (4, (3, 121)))应该都有效NumPairs

这是我为了尝试这样做而编写的代码:

data NumPair = (Int, Int) | (Int, NumPair) deriving (Eq, Show)

有人可以解释为什么这不起作用,而我应该做什么呢?

2 个答案:

答案 0 :(得分:15)

您需要为每个替代方案添加构造函数名称:

data NumPair = Pair (Int, Int) | More (Int, NumPair) deriving (Eq, Show)

这些构造函数名称允许您在数据类型上进行模式匹配,如下所示:

f :: NumPair -> A
f (Pair (x, y )) = ...
f (More (x, np)) = ...

然后,您可以使用构造函数构建一个值(这就是它们被称为构造函数的原因):

myNumPair :: NumPair
myNumPair = More (1, More (2, Pair (3, 4)))

还有其他两种方法可以改善您的类型。 Haskell构造函数内置了对多个字段的支持,因此您可以直接在构造函数中列出值,而不是使用元组,如下所示:

data NumPair = Pair Int Int | More Int NumPair deriving (Eq, Show)

您可以改进的另一种方法是识别您刚刚为非空列表编写了类型。非空列表的最佳实现位于Data.List.NonEmpty包的semigroups,您可以find here

然后你的类型就变成了:

type NumPair = NonEmpty Int

...你可以在非空列表上获得一堆免费的模块功能。

修改:n.m。引起我的注意,你可能想要的是:

data NumPair = Pair (Int, Int) | More ((Int, Int), NumPair)

......相当于:

type NumPair = NonEmpty (Int, Int)

不同之处在于后一个允许你追加整数对,其中前一个跟随你的问题的类型只允许你追加整数。

答案 1 :(得分:4)

你想要的是一种“真正的联合”类型,它在Haskell中不存在。 Haskell仅提供标记的联合,其中程序员必须将附加信息附加到所有数据。使用真正的工会与被标记的工会有一些权衡;我不会试图以某种方式卖给你。但是,可以通过使用提供真正联合类型的语言(例如Typed Racket)来完全按照需要定义类型。

#lang typed/racket

(define-type Num-Pair
  (Rec R
    (U (Pair Integer Integer)
       (Pair Integer R))))

(: foo Num-Pair)
(define foo '(0 . (4 . (3 . 121))))