循环参数的类型
The type of a Loop Parameter
恐怕是初学者的问题。我需要记录数组中特定元素的位置(索引)。考虑以下因素:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
Ten : constant Positive := 10;
type ArrayIndex is new Positive range 1 .. Ten;
type MyRecord is record
firstItem : Integer;
secondItem : Integer;
end record;
TheRecords : array (1 .. Ten) of MyRecord;
indexOfElement50 : ArrayIndex := 1;
begin
-- set the values in TheRecords
for i in TheRecords'Range loop
TheRecords(i).firstItem := i * 10;
TheRecords(i).secondItem := i * 20;
end loop;
-- find which element of TheRecords has a
-- firstItem with a value of 50
for i in TheRecords'Range loop
if TheRecords(i).firstItem = 50 then
-- this next line is horrible: I should
-- not be required to do type casting
-- in a strongly-typed language.
indexOfElement50 := ArrayIndex(i);
exit;
end if;
end loop;
Put(ArrayIndex'image(indexOfElement50));
end Main;
一直到评论“查找 TheRecords 的哪个元素具有值为 50 的 firstItem”的所有内容都只是设置我遇到的问题(当然是在一个更大的程序中)。
虽然我来自 C 和 Python 世界,但我一直试图对 Ada 中的强类型保持虔诚。因此,我仔细定义了“indexOfElement50”,我希望它是 TheRecords 中元素的索引,该元素的 firstItem 为 50。注释下方开始的循环是搜索该元素的代码。并找到了!
但我必须将 i 转换为 ArrayIndex。在强类型的世界中,铸造是错误的。我试过使用 indexOfElement50 作为循环参数,但编译器没有任何参数。
所以,我似乎被迫要么将 indexOfElement50 声明为整数(这违反了尽可能限制范围的准则),要么执行类型转换(这在 C 中很棒,但我应该这样做不会使用强类型语言)。
或者,更有可能的是,我遗漏了一些非常明显的东西,专家们会热情地指出这一点。
我真的不知道这是否是你想要实现的。但也许它有帮助。
您可以使用 ArrayIndex 作为数组的索引:
TheRecords : array (ArrayIndex) of MyRecord;
但是你必须将 i 转换为整数:
TheRecords(i).firstItem := Integer(i) * 10;
TheRecords(i).secondItem := Integer(i) * 20;
完整示例:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
Ten : constant Positive := 10;
type ArrayIndex is new Positive range 1 .. Ten;
type MyRecord is record
firstItem : Integer;
secondItem : Integer;
end record;
TheRecords : array (ArrayIndex) of MyRecord;
indexOfElement50 : ArrayIndex := 1;
begin
-- set the values in TheRecords
for i in TheRecords'Range loop
TheRecords(i).firstItem := Integer(i) * 10;
TheRecords(i).secondItem := Integer(i) * 20;
end loop;
-- find which element of TheRecords has a
-- firstItem with a value of 50
for i in TheRecords'Range loop
if TheRecords(i).firstItem = 50 then
indexOfElement50 := i;
exit;
end if;
end loop;
Put(ArrayIndex'image(indexOfElement50));
end Main;
我倾向于稍微改变一下方法。
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
您真的不需要一个名为 Ten
且值为 10 的常量!如果你想晚 12 点怎么办?
type MyRecord is record
firstItem : Integer;
secondItem : Integer;
end record;
我们想要一个记录数组,但让我们推迟决定需要多长时间...
type Record_Array is array (Positive range <>) of MyRecord;
... 并定义一个测试数组,其大小恰好为 10 但可以是任何值。
TheRecords : Record_Array (1 .. 10);
有效结果(对于此测试程序)只能在 TheRecords’Range
中,但让我们添加一个超出范围的值以指示“未找到”。
subtype Possible_Index is Natural range 0 .. TheRecords'Last;
indexOfElement50 : Possible_Index := 0; -- indicates 'not found'
好的!
begin
-- set the values in TheRecords
for i in TheRecords'Range loop
TheRecords(i).firstItem := i * 10;
TheRecords(i).secondItem := i * 20;
end loop;
-- find which element of TheRecords has a
-- firstItem with a value of 50
for i in TheRecords'Range loop
if TheRecords(i).firstItem = 50 then
indexOfElement50 := i;
exit;
end if;
end loop;
Put_Line (indexOfElement50'Image); -- legal in Ada2012
end Main;
您遇到的主要问题是您声明了一个新类型,然后却没有始终如一地使用它。
这并不奇怪,因为类型不再被很好地教授,但是他们通过实践来很好地利用它们来捕捉真正的错误,而不让它们妨碍。
你做得很好:如果你发现自己写了太多的类型转换(而不是强制转换),这表明设计是错误的(代码味道),你发现了。
现在我要把你的类型声明分成两部分,以说明我是如何处理这个问题的。
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type FunnyInteger is new Positive;
subtype ArrayIndex is FunnyInteger range 1 .. 10;
Ada 提供两种类型和子类型。
类型不兼容(没有显式转换)。他们的工作是阻止您犯类别错误,例如添加脚和马。
子类型相互兼容,但可以表达限制,例如限制值的范围。
所以我介绍了一种新的类型,不要意外地与 Integer
混淆。
并且我已经命名了它的一个子类型,来定义数组的大小。这很重要:因为数组是用这个子类型索引的,所以任何与该子类型不兼容的东西都不能用来索引它……再见,Heartbleed。一个新类型会做同样的事情 : 但也需要在某处进行类型转换,正如您所注意到的。
现在,
type MyRecord is record
firstItem : Integer;
secondItem : FunnyInteger;
end record;
一个记录字段与索引类型兼容;另一个不是,也不能不小心与它混淆。这个选择来自问题领域。如果将 Integers 与 ArrayIndex 混合使用没有坏处,请参见下面的第二个示例。
此外,在声明 ArrayIndex(子)类型后,始终如一地使用它...
TheRecords : array (ArrayIndex) of MyRecord;
indexOfElement50 : ArrayIndex := 1;
begin
for i in ArrayIndex loop
TheRecords(i).firstItem := Integer(i) * 10;
TheRecords(i).secondItem := i * 20;
end loop;
for i in ArrayIndex loop
if TheRecords(i).firstItem = 50 then
indexOfElement50 := i;
exit;
end if;
end loop;
Put(ArrayIndex'image(indexOfElement50));
end Main;
请注意 firstItem
,与我们的 ArrayIndex
不兼容,需要进行类型转换。这证明我们正在打破类型规则;审阅者会注意这一点,它会提醒下一个处理代码的人注意。
我认为这就像我的猫再次从沙发上掉下来时给我的“我打算那样做”的样子。
secondItem
是兼容的,不需要这样的转换(因为问题域允许我们这样做)。
如果没有理由将 ArrayIndex
和 Integer
分开,只需将 ArrayIndex
设为 Integer
的子类型即可。它仍然是范围保护的,但不再是类型保护的。因此,您掌握了权力:选择您需要的保护级别。
还注意到范围是没有名称的子类型,我们可以简化为
with Ada.Text_IO; use Ada.Text_IO;
procedure Main2 is
type MyRecord is record
firstItem : Integer;
secondItem : Integer;
end record;
TheRecords : array (1 .. 10) of MyRecord;
indexOfElement50 : Integer range TheRecords'range;
begin
for i in TheRecords'range loop
TheRecords(i).firstItem := i * 10;
TheRecords(i).secondItem := i * 20;
end loop;
for i in TheRecords'range loop
if TheRecords(i).firstItem = 50 then
indexOfElement50 := i;
exit;
end if;
end loop;
Put(Integer'image(indexOfElement50));
end Main2;
所有与索引相关的内容现在都直接从数组声明派生,并且受范围保护,(但这里假设从根本上与我们的 Integer 类型兼容)。
另请注意,我完整保留了 Simon 指出的错误:如果没有匹配项 return 1
,这不是正确答案。初始化为超出范围的值:
indexOfElement50 : Integer range TheRecords'range := 0;
编译时出现警告;因为 indexOfElement50
是范围保护的,并且 运行 它产生:
./main2
raised CONSTRAINT_ERROR : main2.adb:12 range check failed
初始化时,显示范围保护。西蒙很好地解释了如何解决这个问题!
恐怕是初学者的问题。我需要记录数组中特定元素的位置(索引)。考虑以下因素:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
Ten : constant Positive := 10;
type ArrayIndex is new Positive range 1 .. Ten;
type MyRecord is record
firstItem : Integer;
secondItem : Integer;
end record;
TheRecords : array (1 .. Ten) of MyRecord;
indexOfElement50 : ArrayIndex := 1;
begin
-- set the values in TheRecords
for i in TheRecords'Range loop
TheRecords(i).firstItem := i * 10;
TheRecords(i).secondItem := i * 20;
end loop;
-- find which element of TheRecords has a
-- firstItem with a value of 50
for i in TheRecords'Range loop
if TheRecords(i).firstItem = 50 then
-- this next line is horrible: I should
-- not be required to do type casting
-- in a strongly-typed language.
indexOfElement50 := ArrayIndex(i);
exit;
end if;
end loop;
Put(ArrayIndex'image(indexOfElement50));
end Main;
一直到评论“查找 TheRecords 的哪个元素具有值为 50 的 firstItem”的所有内容都只是设置我遇到的问题(当然是在一个更大的程序中)。
虽然我来自 C 和 Python 世界,但我一直试图对 Ada 中的强类型保持虔诚。因此,我仔细定义了“indexOfElement50”,我希望它是 TheRecords 中元素的索引,该元素的 firstItem 为 50。注释下方开始的循环是搜索该元素的代码。并找到了!
但我必须将 i 转换为 ArrayIndex。在强类型的世界中,铸造是错误的。我试过使用 indexOfElement50 作为循环参数,但编译器没有任何参数。
所以,我似乎被迫要么将 indexOfElement50 声明为整数(这违反了尽可能限制范围的准则),要么执行类型转换(这在 C 中很棒,但我应该这样做不会使用强类型语言)。
或者,更有可能的是,我遗漏了一些非常明显的东西,专家们会热情地指出这一点。
我真的不知道这是否是你想要实现的。但也许它有帮助。
您可以使用 ArrayIndex 作为数组的索引:
TheRecords : array (ArrayIndex) of MyRecord;
但是你必须将 i 转换为整数:
TheRecords(i).firstItem := Integer(i) * 10;
TheRecords(i).secondItem := Integer(i) * 20;
完整示例:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
Ten : constant Positive := 10;
type ArrayIndex is new Positive range 1 .. Ten;
type MyRecord is record
firstItem : Integer;
secondItem : Integer;
end record;
TheRecords : array (ArrayIndex) of MyRecord;
indexOfElement50 : ArrayIndex := 1;
begin
-- set the values in TheRecords
for i in TheRecords'Range loop
TheRecords(i).firstItem := Integer(i) * 10;
TheRecords(i).secondItem := Integer(i) * 20;
end loop;
-- find which element of TheRecords has a
-- firstItem with a value of 50
for i in TheRecords'Range loop
if TheRecords(i).firstItem = 50 then
indexOfElement50 := i;
exit;
end if;
end loop;
Put(ArrayIndex'image(indexOfElement50));
end Main;
我倾向于稍微改变一下方法。
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
您真的不需要一个名为 Ten
且值为 10 的常量!如果你想晚 12 点怎么办?
type MyRecord is record
firstItem : Integer;
secondItem : Integer;
end record;
我们想要一个记录数组,但让我们推迟决定需要多长时间...
type Record_Array is array (Positive range <>) of MyRecord;
... 并定义一个测试数组,其大小恰好为 10 但可以是任何值。
TheRecords : Record_Array (1 .. 10);
有效结果(对于此测试程序)只能在 TheRecords’Range
中,但让我们添加一个超出范围的值以指示“未找到”。
subtype Possible_Index is Natural range 0 .. TheRecords'Last;
indexOfElement50 : Possible_Index := 0; -- indicates 'not found'
好的!
begin
-- set the values in TheRecords
for i in TheRecords'Range loop
TheRecords(i).firstItem := i * 10;
TheRecords(i).secondItem := i * 20;
end loop;
-- find which element of TheRecords has a
-- firstItem with a value of 50
for i in TheRecords'Range loop
if TheRecords(i).firstItem = 50 then
indexOfElement50 := i;
exit;
end if;
end loop;
Put_Line (indexOfElement50'Image); -- legal in Ada2012
end Main;
您遇到的主要问题是您声明了一个新类型,然后却没有始终如一地使用它。
这并不奇怪,因为类型不再被很好地教授,但是他们通过实践来很好地利用它们来捕捉真正的错误,而不让它们妨碍。
你做得很好:如果你发现自己写了太多的类型转换(而不是强制转换),这表明设计是错误的(代码味道),你发现了。
现在我要把你的类型声明分成两部分,以说明我是如何处理这个问题的。
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type FunnyInteger is new Positive;
subtype ArrayIndex is FunnyInteger range 1 .. 10;
Ada 提供两种类型和子类型。
类型不兼容(没有显式转换)。他们的工作是阻止您犯类别错误,例如添加脚和马。
子类型相互兼容,但可以表达限制,例如限制值的范围。
所以我介绍了一种新的类型,不要意外地与 Integer
混淆。
并且我已经命名了它的一个子类型,来定义数组的大小。这很重要:因为数组是用这个子类型索引的,所以任何与该子类型不兼容的东西都不能用来索引它……再见,Heartbleed。一个新类型会做同样的事情 : 但也需要在某处进行类型转换,正如您所注意到的。
现在,
type MyRecord is record
firstItem : Integer;
secondItem : FunnyInteger;
end record;
一个记录字段与索引类型兼容;另一个不是,也不能不小心与它混淆。这个选择来自问题领域。如果将 Integers 与 ArrayIndex 混合使用没有坏处,请参见下面的第二个示例。
此外,在声明 ArrayIndex(子)类型后,始终如一地使用它...
TheRecords : array (ArrayIndex) of MyRecord;
indexOfElement50 : ArrayIndex := 1;
begin
for i in ArrayIndex loop
TheRecords(i).firstItem := Integer(i) * 10;
TheRecords(i).secondItem := i * 20;
end loop;
for i in ArrayIndex loop
if TheRecords(i).firstItem = 50 then
indexOfElement50 := i;
exit;
end if;
end loop;
Put(ArrayIndex'image(indexOfElement50));
end Main;
请注意 firstItem
,与我们的 ArrayIndex
不兼容,需要进行类型转换。这证明我们正在打破类型规则;审阅者会注意这一点,它会提醒下一个处理代码的人注意。
我认为这就像我的猫再次从沙发上掉下来时给我的“我打算那样做”的样子。
secondItem
是兼容的,不需要这样的转换(因为问题域允许我们这样做)。
如果没有理由将 ArrayIndex
和 Integer
分开,只需将 ArrayIndex
设为 Integer
的子类型即可。它仍然是范围保护的,但不再是类型保护的。因此,您掌握了权力:选择您需要的保护级别。
还注意到范围是没有名称的子类型,我们可以简化为
with Ada.Text_IO; use Ada.Text_IO;
procedure Main2 is
type MyRecord is record
firstItem : Integer;
secondItem : Integer;
end record;
TheRecords : array (1 .. 10) of MyRecord;
indexOfElement50 : Integer range TheRecords'range;
begin
for i in TheRecords'range loop
TheRecords(i).firstItem := i * 10;
TheRecords(i).secondItem := i * 20;
end loop;
for i in TheRecords'range loop
if TheRecords(i).firstItem = 50 then
indexOfElement50 := i;
exit;
end if;
end loop;
Put(Integer'image(indexOfElement50));
end Main2;
所有与索引相关的内容现在都直接从数组声明派生,并且受范围保护,(但这里假设从根本上与我们的 Integer 类型兼容)。
另请注意,我完整保留了 Simon 指出的错误:如果没有匹配项 return 1
,这不是正确答案。初始化为超出范围的值:
indexOfElement50 : Integer range TheRecords'range := 0;
编译时出现警告;因为 indexOfElement50
是范围保护的,并且 运行 它产生:
./main2
raised CONSTRAINT_ERROR : main2.adb:12 range check failed
初始化时,显示范围保护。西蒙很好地解释了如何解决这个问题!