3

免责声明:我是一名学习 C 的 Haskell 程序员。在 Haskell 中,我们有如下数据声明

data No = NO

其中 NO 没有任何数字解释。如果我们在 C 中有等价的东西,我们可以做

union MaybeInt { enum No no; int just;};

它可以用来做一些事情,比如有一个初始化为 No 的数组。

int A[k];
for (int i = 0; i < k; i++)
    A[i] = NO;

这在做记忆时很有用,因为通常有一些递归算法,它在数组中查找事物并基于查找的值,进行递归调用或不进行调用。例如:(对于斐波那契数)

fibMem (int k){
    if (FIB[k] == NO)
      compute fibMem(k-1) + fib(k-2) and store the result in FIB[k]
    return FIB[k]
}

现在,当然,我可以将 FIB[i] 初始化为一些荒谬的值,例如 -100,这适用于这个问题;但是,给定一个任意的记忆例程,我不知道值的范围,这种解决方案是行不通的。

使用枚举类型的问题:我看到的第一件事就是让我从座位上跳起来说“是”是枚举类型。我想为什么不做类似的事情呢enum No {no}; ,用 s 初始化用于记忆的数组有问题no。问题是,如果我愿意,它no被定义为0或我选择的某个数字常数。这是不令人满意的,因为如果存储在数组中的值应该为零(或我选择的那个常数),那么当我进行检查时A[i] == no,它可能应该是这样的!因此,我最终将执行不需要的递归。

这给我们带来了问题 1:如何在 C 中获得一个被视为标志的符号常量,它与任何不同类型的东西都无法比较?

现在,工会的问题。联合将其所有字段存储在一个地址中。因此,例如,对maybeInt.just 的更新会影响mayInt.no 的值。例如,

union MaybeInt maybeInt;
maybeInt.just=9;
printf("%d",maybeInt.just);
printf("%d",maybeInt.no);

打印 99。如果 C 中有某种不相交的联合类型,那么如果我使用联合的一个值,另一个将变得无法获得。

这给我们带来了第二个也是最后一个问题:如何在 C 中获得不相交的联合类型——这种类型有许多可能的变体,但在任何给定时间只有一个变体。我想要一些能够做类似的事情:

disjoint T {type1 name1 , .... };

并设置了 if T.name2,然后对 T.name1 的引用会引发错误。或者更好的是,任何对 T 的引用都必须经过某种大小写区分。

如果这不能很好地完成,请解释原因。

4

1 回答 1

11

有区别的联合是一个非常标准的 C 习惯用法。您只需将标签与数据分开:

struct Data
{
    enum DataType
    {
        NotSet,
        Integer,
        Infinity,
        Message
    } tag;
    union ValueType
    {
        int n;
        char const * msg;
    } data;
};

现在您只需要维护标签规则,即只读取适合给定标签的值,并在写入联合成员后更新标签。例如:

void foo(struct Data const * x)
{
    switch (x->tag)
    {
    case NotSet:      // ...
    case Integer:     // use x->data.n
    case Infinity:    // ...
    case Message:     // use x->data.msg
    };

    x->data.msg = "Thank you!";
    x->tag = Message;
}
于 2013-09-24T22:04:00.843 回答