1

我正在研究编程中的不相交联合。我遇到了这样的说法PascalSML并且C有自己的工会版本variant recordconstructionunion。也有人说Pascal包含一个您不必使用它的“标签”,SML有一个您需要使用它的标签并且C没有标签。此外,SML如果我们用错了会抛出异常,Pascal允许在运行时检查并且C没有在运行时检查的功能,程序员必须手动为“标签”添加一个字段。

首先,我不明白什么是“标签”。我试图查看这些联合的一些示例,但不明白“标签”代表什么。如果“标签”很重要,怎么C会有一个?这些工会有什么区别。另外,我没有找到任何与工会“标签”相关的材料。此外,“在运行时检查”是什么意思,检查什么?很高兴看到展示这些功能的简单示例。

4

4 回答 4

2

人们可以将这种不相交的联合称为一种非常早期的多态性形式。您有一种可以有多种形式的类型。在某些语言中,使用这些形式中的哪一种(处于活动状态)是由类型的成员(称为标记)来区分的。这可以是布尔值、字节、枚举或其他一些序数。

在一些(旧的?)Pascal 版本中,标签实际上需要包含正确的值。Pascal“联合”(或者,在 Pascal 中称为变体记录)包含一个值,用于区分当前哪些分支是“活动的”。

一个例子:

type
  MyUnion = record  // Pascal's version of a struct -- or union
    case Tag: Byte of // This doesn't have to be called Tag, it can have any name
      0: (B0, B1, B2, B3: Byte);  // only one of these branches is present
      1: (W0, W1: Word);          // they overlap each other in memory 
      2: (L: Longint);
  end;

在此类 Pascal 版本中,如果 Tag 的值为 0,则您只能访问 B0、B1、B2 或 B3,而不能访问其他变体。如果 Tag 为 1,则只能访问 W0 和 W1 等...

在大多数 Pascal 版本中,没有这样的限制,标签值纯粹是提供信息。在其中许多中,您甚至不再需要显式标记值:

MyUnion = record
  case Byte of // no tag, just a type, to keep the syntax similar
    etc...

请注意,Pascal 变体记录不是纯并集,其中每个部分都是可选的:

type
  MyVariantRec = record
    First: Integer; // the non-variant part begins here
    Second: Double;
    case Byte of // only the following part is a "union", the variant part.
      0: ( B0, B1, B2, B3: Byte; );
      1: ( W0, W1: Word; );
      2: ( L: Longint);
 end;

在 C 中,您必须在结构中嵌套一个联合才能获得几乎相同的东西:

// The following is more or less the equivalent of the Pascal record above
struct MyVariantRec
{
    int first;
    double second;
    union
    {
        struct { unsigned char b0, b1, b2, b3; };
        struct { unsigned short w0, w1 };
        struct { long l };
    };
}
于 2019-01-24T23:00:48.900 回答
0

标签是告诉您当前正在使用哪个工会成员的任何内容。它通常是一个枚举,但也可以是一个整数、一个布尔值或基于其中之一的位域。

例子:

union my_union { char *string; void *void_ptr; long integer; };

struct my_tagged_union {
    union my_union the_union;
    enum { is_string, is_void_ptr, is_integer } the_tag;
};

C 不强迫您使用内置标签意味着您可以更好地控制数据的布局和大小。例如,您可以使用位域标记并将其放在存储在结构中的其他位域信息旁边,以便合并位域,从而节省空间;或者有时当前使用的联合成员在您的代码所在的上下文中是隐含的,在这种情况下根本不需要标签。

于 2019-01-24T21:51:00.053 回答
0

SML 有一个标签,您需要使用它[...]。此外,如果我们使用错误,SML 会抛出异常,

标准 ML 具有代数数据类型,其中包含总和类型乘积类型sum 类型建立在union 之上(而product 类型建立在 struct 之上),但是在编译器中自动处理你所谓的标记或不相交联合;您指定构造函数,编译后的代码会计算出如何通过模式匹配来区分不同的构造函数。例如,

datatype pokemon = Pikachu of int
                 | Bulbasaur of string
                 | Charmander of bool * char
                 | Squirtle of pokemon list

所以一个sum 类型可以有不同的构造函数和不同的参数,并且参数本身可以是其他类型的乘积,包括 sum 类型,并且包括自己定义的类型,使得数据类型定义是递归的。这是通过标记联合实现的,但顶部的抽象提供了更多的语法便利。

澄清一下,如果使用错误,Standard ML 不会抛出异常,但会在编译期间抛出类型错误。这是因为标准 ML 的类型系统。因此,您不会意外地将(void *)-pointer 投射到它不是的东西上,这在 C 中是可能的。

于 2019-01-25T08:43:32.103 回答
0

首先,我不明白什么是“标签”。

Wikipedia对整体概念进行了相当不错的讨论,它从同义词列表开始,包括“tagged union”。实际上,“标记联合”是文章的主标题,不相交联合是同义词之一。它以一个非常简洁的解释开始:

一种数据结构,用于保存可以采用多种不同但固定类型的值。任何时候只能使用其中一种类型,并且标签字段明确指示正在使用哪种类型。

你继续问,

如果“标签”很重要,那么 C 怎么会有一个呢?

在这种情况下,标签的重要性是一个语言设计问题,C、Pascal 和 SML 在该问题上采取不同的立场。由于 C 倾向于对大多数事情采取相当低级的方法,并允许用户进行大量控制,因此它不强制使用标签也就不足为奇了。想要标签的用户可以比较容易地自己实现它们,就像我有时自己做的那样。

或者,可能更容易说 C根本没有将标记联合作为内置语言功能,只有普通的、未标记的联合。从这个角度来看,如果您想在 C 中使用标记联合,那么您必须自己实现它。这可能是最一致的观点,但我认为它与您所研究的材料中的观点不同。

这些工会有什么区别。

它们是由不同语言提供的类似概念的不同实现。全面分析将超出 SO 答案的合理范围。就像计算机科学和其他领域的许多事情一样,不相交联合的抽象概念可以通过许多不同的方式来实现。

另外,我没有找到任何与工会“标签”相关的材料。

见上文,以及链接的维基百科文章。我相信你也可以找到更多的材料,尤其是使用 WP 的同义词列表。

此外,“在运行时检查”是什么意思,检查什么?

我必须查看上下文和确切的陈述才能确定,但​​您的消息来源似乎正在谈论检查以下一项或多项内容:

  • 联合的特定实例的标记是为该联合类型定义的标记之一,或
  • 联合的内容是标签所指示的类型,或
  • 备选行动清单(见下文)涵盖所有可能的备选方案。

很高兴看到展示这些功能的简单示例。

我的 Pascal 太生锈了,没有任何用处,而且我不知道 SML。然而,即使只是一个 C 示例也可能具有指导意义:

enum my_tag { INT_TAG, STRING_TAG, DOUBLE_TAG };

union disjoint_union {
    struct {
        enum my_tag tag;
        int an_int;
    };
    struct {
        enum my_tag tag_s;
        char *a_string;
    };
    struct {
        enum my_tag tag_d;
        double a_double;
    };
};

union disjoint_union u =  { .tag = INT_TAG,    .an_int = 42 };
union disjoint_union u2 = { .tag = STRING_TAG, .a_string = "hello" };
union disjoint_union u3 = { .tag = DOUBLE_TAG, .a_double = 3.14159 };

这是C,标记是手动显式提供的,语言没有特别区分它。此外,由程序员来确保联合的内容带有正确的标签。

您可以将这样的东西与这样的函数一起使用,它依赖于标签来确定如何处理联合类型的实例:

void print_union(union disjoint_union du) {
    switch (du.tag) {
        case INT_TAG:
            printf("%d", du.an_int);
            break;
        case STRING_TAG:
            printf("%s", du.a_string);
            break;
        case DOUBLE_TAG:
            printf("%f", du.a_double);
            break;
    }
}
于 2019-01-24T22:22:33.310 回答