为什么 [...] 形式的文字具有似乎取决于上下文的含义?
Why do literals of the form [...] have meaning that appears to depend upon context?
考虑以下程序:
{$APPTYPE CONSOLE}
type
TMyEnum = (enum1, enum2, enum3);
var
Arr: TArray<TMyEnum>;
Enum: TMyEnum;
begin
Arr := [enum3, enum1]; // <-- this is an array
for Enum in Arr do
Writeln(ord(Enum));
Writeln('---');
for Enum in [enum3, enum1] do // <-- this looks very much like the array above
Writeln(ord(Enum));
Writeln('---');
Readln;
end.
输出为:
2
0
---
0
2
---
为什么两个循环产生不同的输出?
for Enum in Arr do
Writeln(ord(Enum));
这里,Arr
是一个数组,所以数组的项是按顺序输出的。 documentation 表示:
The array is traversed in increasing order.
因此2
在0
之前输出。
for Enum in [enum3, enum1] do
Writeln(ord(Enum));
这里,[enum3, enum1]
是一个集合,集合的枚举器恰好按照序数值递增的顺序枚举。所以输出首先有 0
。
我认为文档中的任何地方都没有说明集合是按该顺序枚举的,但根据经验看来确实如此。然而,由于集合是一种无序类型,无论如何都不应该依赖于它们的枚举顺序。
所以问题就变成了理解 [...]
如何在代码的不同点成为一个集合或一个数组。这一切都源于新的 XE7 动态数组语法,它引入了(另一个)语法歧义。当我们写
Arr := [enum3, enum1];
那么[enum3, enum1]
就是一个数组。编译器知道 Arr
是一个数组,并且该信息定义了文字的类型。
但是当我们写
for Enum in [enum3, enum1] do
那么[enum3, enum1]
就是一个集合。这里的文字原则上可以是数组或集合。在这种情况下,我相信编译器总是更喜欢集合。
同样,我找不到任何说明情况如此的文档,但根据经验,情况确实如此。据推测,因为集合枚举器早于新的动态数组语法,所以当有歧义时它们优先。
[...]
形式的文字的含义取决于其上下文。
因为数组包含订单信息而集合不包含。
使用文档的解释:
静态或动态数组的internal data format:
is stored as a contiguous sequence of elements of the component type of the array. The components with the lowest indexes are stored at the lowest memory addresses.
使用 for in
循环遍历这些索引 is done in incremental order:
The array is traversed in increasing order, starting at the lowest array bound and ending at the array size minus one.
另一方面,组的internal data format:
is a bit array where each bit indicates whether an element is in the set or not.
因此所有这些 "indiced bits" 都存储在同一个 "value" 中。这就是为什么一个集合可以是 typecasted to an Integer type,以及为什么添加位的顺序会丢失:[enum3, enum1] = [enum1, enum3]
.
虽然并不总是理想的,但编译器正在使用上下文来确定右侧的类型。你可以看看字符串作为一个很好的例子:
在字符串的情况下,编译器会根据左边来判断右边的类型。这与问题中的代码之间的区别在于,这种情况已明确记录,而问题中的情况则没有。
使用字符的示例:
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
var
A: Char;
B: AnsiChar;
begin
A := 'a';
B := 'a';
Writeln(A);
Writeln(B);
Readln;
end.
从两者生成的汇编程序表明在这两种情况下右侧的处理方式不同:
Project10.dpr.17: A := 'a';
004D6731 66C705C8034E006100 mov word ptr [[=11=]4e03c8],[=11=]61
Project10.dpr.18: B := 'a';
004D673A C605CA034E0061 mov byte ptr [[=11=]4e03ca],
编译器使用赋值的目标类型来确定字符串(在本例中为 'a')的类型。问题中发生了类似的事情。
感谢 David 在评论中提供的额外信息
考虑以下程序:
{$APPTYPE CONSOLE}
type
TMyEnum = (enum1, enum2, enum3);
var
Arr: TArray<TMyEnum>;
Enum: TMyEnum;
begin
Arr := [enum3, enum1]; // <-- this is an array
for Enum in Arr do
Writeln(ord(Enum));
Writeln('---');
for Enum in [enum3, enum1] do // <-- this looks very much like the array above
Writeln(ord(Enum));
Writeln('---');
Readln;
end.
输出为:
2 0 --- 0 2 ---
为什么两个循环产生不同的输出?
for Enum in Arr do
Writeln(ord(Enum));
这里,Arr
是一个数组,所以数组的项是按顺序输出的。 documentation 表示:
The array is traversed in increasing order.
因此2
在0
之前输出。
for Enum in [enum3, enum1] do
Writeln(ord(Enum));
这里,[enum3, enum1]
是一个集合,集合的枚举器恰好按照序数值递增的顺序枚举。所以输出首先有 0
。
我认为文档中的任何地方都没有说明集合是按该顺序枚举的,但根据经验看来确实如此。然而,由于集合是一种无序类型,无论如何都不应该依赖于它们的枚举顺序。
所以问题就变成了理解 [...]
如何在代码的不同点成为一个集合或一个数组。这一切都源于新的 XE7 动态数组语法,它引入了(另一个)语法歧义。当我们写
Arr := [enum3, enum1];
那么[enum3, enum1]
就是一个数组。编译器知道 Arr
是一个数组,并且该信息定义了文字的类型。
但是当我们写
for Enum in [enum3, enum1] do
那么[enum3, enum1]
就是一个集合。这里的文字原则上可以是数组或集合。在这种情况下,我相信编译器总是更喜欢集合。
同样,我找不到任何说明情况如此的文档,但根据经验,情况确实如此。据推测,因为集合枚举器早于新的动态数组语法,所以当有歧义时它们优先。
[...]
形式的文字的含义取决于其上下文。
因为数组包含订单信息而集合不包含。
使用文档的解释:
静态或动态数组的internal data format:
is stored as a contiguous sequence of elements of the component type of the array. The components with the lowest indexes are stored at the lowest memory addresses.
使用 for in
循环遍历这些索引 is done in incremental order:
The array is traversed in increasing order, starting at the lowest array bound and ending at the array size minus one.
另一方面,组的internal data format:
is a bit array where each bit indicates whether an element is in the set or not.
因此所有这些 "indiced bits" 都存储在同一个 "value" 中。这就是为什么一个集合可以是 typecasted to an Integer type,以及为什么添加位的顺序会丢失:[enum3, enum1] = [enum1, enum3]
.
虽然并不总是理想的,但编译器正在使用上下文来确定右侧的类型。你可以看看字符串作为一个很好的例子:
在字符串的情况下,编译器会根据左边来判断右边的类型。这与问题中的代码之间的区别在于,这种情况已明确记录,而问题中的情况则没有。
使用字符的示例:
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
var
A: Char;
B: AnsiChar;
begin
A := 'a';
B := 'a';
Writeln(A);
Writeln(B);
Readln;
end.
从两者生成的汇编程序表明在这两种情况下右侧的处理方式不同:
Project10.dpr.17: A := 'a';
004D6731 66C705C8034E006100 mov word ptr [[=11=]4e03c8],[=11=]61
Project10.dpr.18: B := 'a';
004D673A C605CA034E0061 mov byte ptr [[=11=]4e03ca],
编译器使用赋值的目标类型来确定字符串(在本例中为 'a')的类型。问题中发生了类似的事情。
感谢 David 在评论中提供的额外信息