Ada带弦切片 我是一个长时间的C++程序员,学习艾达是为了好玩。如果以下任何一项是不好的形式,请随时指出它。我正在努力学习Ada做事的方法,但旧习惯很难改掉(我想念Boost!)

Ada带弦切片 我是一个长时间的C++程序员,学习艾达是为了好玩。如果以下任何一项是不好的形式,请随时指出它。我正在努力学习Ada做事的方法,但旧习惯很难改掉(我想念Boost!),ada,gnat,Ada,Gnat,我试图加载一个包含整数、空格和字符串的文件。也许有更好的方法可以做到这一点,但我认为应该将该行加载到一个字符串缓冲区中,我知道该缓冲区不会超过80个字符。我在适当的位置声明一个缓冲区变量,如下所示: Line_Buffer : String(1..80); 打开文件后,我循环遍历每一行,并在空格处拆分缓冲区: while not Ada.Text_IO.End_Of_File(File_Handle) loop Ada.Text_IO.Get_Line(File_Handle, It

我试图加载一个包含整数、空格和字符串的文件。也许有更好的方法可以做到这一点,但我认为应该将该行加载到一个字符串缓冲区中,我知道该缓冲区不会超过80个字符。我在适当的位置声明一个缓冲区变量,如下所示:

 Line_Buffer : String(1..80);
打开文件后,我循环遍历每一行,并在空格处拆分缓冲区:

 while not Ada.Text_IO.End_Of_File(File_Handle) loop
   Ada.Text_IO.Get_Line(File_Handle, Item=>Line_Buffer, Last=>Last);
   -- Break line at space to get match id and entry
   for String_Index in Line_Buffer'Range loop
     if Line_Buffer(String_Index) = ' ' then
       Add_Entry(Root_Link=>Root_Node,
        ID_String=> Line_Buffer(1..String_Index-1),
        Entry_String=> Line_Buffer(String_Index+1..Last-1)
        );
     end if;
   end loop;
 end loop;
Add_条目中发生的事情并不重要,但其规范如下所示:

 procedure Add_Entry(
   Root_Link : in out Link;
   ID_String : in String;
   Entry_String : in String);
我想使用无界字符串而不是有界字符串,因为我不想担心必须在这里和那里指定大小。这可以很好地编译和工作,但在Add_条目中,当我尝试循环条目_字符串中的每个字符时,索引不是从1开始,而是从原始字符串中的偏移量开始。例如,如果Line_Buffer是“14 silicon”,如果I循环如下,则索引从4变为10

for Index in Entry_String'Range loop
  Ada.Text_IO.Put("Index: " & Integer'Image(Index));
  Ada.Text_IO.New_Line;  
end loop;

有没有更好的方法来进行这种解析,以便我传递给Add_Entry的字符串具有以1开头的边界?另外,当我将切片字符串作为“in”参数传递给过程时,是在堆栈上创建的副本,还是使用了对原始字符串的引用?

首先,我表示同情。艾达字符串可能是C++和艾达之间最不同的东西。更糟糕的是,差异隐藏在表面之下,因此天真的C/C++程序员开始他们的Ada职业生涯时认为他们可能不存在,他们可以像对待C字符串一样对待Ada字符串。现在请回答您的具体问题:

Ada数组(包括字符串)都有隐式边界。这意味着通常不需要特殊的sentinel值(如nul),也很少需要单独的长度变量。这也意味着
1
0
或任何其他索引没有什么特别之处

因此,在Ada中处理数组的正确方法是,不要在子例程中假设起始边界和结束边界是什么。你把他们弄明白。该语言专门为此提供了
“first
”last
,以及
“range
”。从您的示例中,如果您想要打印给定字符串开头的偏移量(出于某些奇怪的原因),它将是:

for Index in Entry_String'Range loop
  Ada.Text_IO.Put("Index offset: " & Integer'Image(Index-Entry_string'first));
  Ada.Text_IO.New_Line;  
end loop;
嗯。现在是Ada和C之间的第二个差异。您的
in
参数不会被复制。这一点非常重要,所以我要大声说:Ada参数不像C参数那样传递确切的规则有点复杂,但对于你来说,原则是Ada会做明智的事情。如果参数可以放入寄存器,则将通过副本(或者寄存器)传递。如果参数太大,则将通过引用传递。你不必决定这个。这是一个优化问题,将由编译器完成。但是,您可以指望编译器不会创建大型数组的副本,而只是将它们传递给一个无论如何都不允许修改它们的例程。那将是愚蠢的。只有一个白痴(或C++编译器)才会做这种事。如果你发现有一个Ada编译器在做这件事,就报告它是一个bug。是的


最后,在大多数情况下,创造性地使用Ada的作用域规则将允许您使用大小完美的常量“固定”字符串。您几乎不需要使用动态字符串或单独的长度变量。遗憾的是,
Ada.Text\u IO.Get\u Line
是例外之一。如果您不太关心性能(如果您从用户处读取此字符串,则不应如此),可以使用from Text_IO。

如果您同意使用GNAT实现定义的包,则该包可用

另外,Ada.Strings子包(特定于固定、有界或无界字符串)为字符串处理提供了一些有用的子程序,例如Index()用于在其他字符串中查找特定字符串——用于查找嵌入空格:-)


还有另一个GNAT包(它预先实例化了字符串,名为GNAT.String_Split),它提供了更多面向分解数组(和字符串)的子程序。

这些属性非常灵活。不用担心索引值是件好事。我正在学习的Ada的另一个特性(除了类型和到目前为止的纯可读性)正如TED所暗示的那样,您可以定义子类型,然后使用这些类型的范围来创建和索引数组,甚至是非标准的范围,比如(-5..5),这样就不需要将任何索引偏移到0或1:)谢谢。我会仔细看看这些包裹。GNAT是我唯一使用的Ada编译器,所以我也会检查GNAT编译器。我在Get_Line中看到了一个很好的递归读取技巧,它可以将行转换成一个大小完美的固定字符串。哦,还有额外的道具引导他去Ada.Strings.Fixed和friends。如果他还不需要这些,他很快就会用到。在ADA2005+中,有
函数Get_Line(File:In File_Type)返回字符串(以及使用标准输入的无参数版本):ARM 2012文本位于。这意味着您可以声明一个局部常量字符串,并使用下一行的内容初始化它,而不必担心长度。