3

私は楽しみのために Ada を学んでいる C++ プログラマーです。以下のいずれかが不適切な形式である場合は、お気軽にご指摘ください。Ada のやり方を学ぼうとしていますが、古い習慣はなかなか直りません (それに、Boost が恋しいです!)

整数、スペース、文字列を含むファイルを読み込もうとしています。これを行うためのより良い方法があるかもしれませんが、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_Entry で何が起こるかはそれほど重要ではありませんが、その仕様は次のようになります。

 procedure Add_Entry(
   Root_Link : in out Link;
   ID_String : in String;
   Entry_String : in String);

あちこちでサイズを指定しなければならないことを心配したくないので、制限付き文字列ではなく制限なし文字列を使用したかったのです。これはコンパイルして正常に動作しますが、Add_Entry 内で Entry_String の各文字をループしようとすると、インデックスが 1 から始まるのではなく、元の文字列のオフセットから始まります。たとえば、Line_Buffer が「14 シリコン」の場合、次のようにループすると、インデックスは 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」パラメータとしてプロシージャに渡すと、スタック上にコピーが作成されますか、それとも元の文字列への参照が使用されますか?

4

2 に答える 2

8

まず、お悔やみ申し上げます。Ada 文字列は、おそらく C++ と Ada の間で最も異なる唯一のものです。さらに悪いことに、違いは表面下にあるため、素朴な C/C++ コーダーは、おそらくそこにはいないと考えて Ada のキャリアを開始し、Ada の文字列を C の文字列と同じように扱うことができます。今、あなたの特定の質問のために:

Ada 配列 (文字列を含む) にはすべて暗黙の境界が渡されます。これは、通常、特別なセンチネル値 (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 の違い 2 について説明します。inパラメータはコピーされません。これは非常に重要なので、少し叫びます: Ada パラメーターは C パラメーターのように渡されません!正確なルールは少し複雑ですが、原則として、Ada は賢明なことを行います。パラメーターがレジスターに収まる場合は、コピー (またはレジスター) によって渡されます。パラメーターが大きすぎる場合は、参照によって渡されます。あなたはこれを決めることはできません。これは最適化の問題であり、コンパイラによって行われます。ただし、コンパイラが大きな配列のコピーを作成せず、変更が許可されていないルーチンにそれらを渡すことは期待できます。それはばかげているでしょう。そのようなことをするのは、完全な馬鹿 (または C++ コンパイラ) だけです。Ada コンパイラがそれを実行しているのを見つけた場合は、バグとして報告してください。それはそのようになります。

最後に、ほとんどの場合、Ada のスコープ ルールを創造的に使用することで、完全なサイズの定数 "固定" 文字列を使用できます。動的文字列や個別の長さ変数を使用する必要はほとんどありません。悲しいことにAda.Text_IO.Get_Line、例外の 1 つです。パフォーマンスをあまり気にしない場合 (ユーザーからこの文字列を読み取る場合はそうすべきではありません)、Carlisle のルーチンを使用して、Text_IO から完全なサイズの固定文字列を読み取ることができます。

于 2011-02-07T20:39:45.677 に答える
4

GNAT 実装で定義されたパッケージを使用しても問題ない場合は、パッケージAda.Strings.Unbounded.Text_IOを利用できます。

また、Ada.Strings 子パッケージ (Fixed、Bounded、または Unbounded 文字列に固有) は、他の文字列内の特定の文字列を見つけるための Index() など、文字列処理に役立つサブプログラムをいくつか提供します。これは、埋め込まれた空白を見つけるのに役立ちます:-)

別の GNAT パッケージGNAT.Array_Split (GNAT.String_Split として文字列が事前にインスタンス化されています) があり、配列 (および文字列) を分解することを目的としたサブプログラムをさらに提供します。

于 2011-02-07T21:09:27.000 に答える