Массив строк в Ada

#arrays #string #ada

#массивы #строка #ada

Вопрос:

Итак, я впервые пишу в Ada и должен создать этот предварительно заполненный массив строк, чтобы я мог использовать случайный метод и каждый раз вызывать другое значение.

 poolOfWords: constant array(1..50) of string := ("gum","sin","for","cry","lug","bye","fly",
                "ugly", "each","from","work","talk","with","self", "pizza","thing","feign","friend",
                 "elbow","fault", "dirty","budget","spirit","quaint","maiden","escort","pickax",
                 "example","tension","quinine","kidney","replica","sleeper", "triangle","kangaroo",
                 "mahogany","sergeant","sequence","moustache","dangerous","scientist",
                 "different","quiescent", "magistrate","erroneously","loudspeaker",
                 "phytotoxic","matrimonial","parasympathomimetic","thigmotropism");
 

Но когда я пытаюсь скомпилировать, я получаю эту ошибку: «неограниченный тип элемента в объявлении массива».

Я погуглил по всему Интернету и не смог найти решение этой проблемы. Я даже пытался использовать unbounded_string, но это не сработало. Он отлично работает, когда я предварительно заполняю массив целых чисел с плавающей запятой, но когда дело доходит до строк, он не работает.

Может кто-нибудь помочь мне и объяснить, что происходит и как это решить? Я действительно не хочу объявлять массив, а затем заполнять каждый из 50 индексов один за другим.

Комментарии:

1. Основная проблема очевидна: строки имеют разные размеры, поэтому их нельзя хранить в одном массиве. Определите «не сработало так хорошо» для unbounded_string . Возможно, это лучшее решение здесь (хотя bounded_string, ограниченный длиной самого длинного элемента, также был бы хорошим вариантом). Используйте любой из них, проработайте их трудности и спросите (с более подробной информацией), если вы застряли.

2. @BrianDrummond но если я использую unbounded_string , выдается сообщение об ошибке, в котором говорится, что я пытаюсь присвоить строку массиву unbounded_string , и я думаю , что решением этого было бы использовать To_Unbounded_String() в каждом элементе предварительно заполненного массива… Верно?

3. Да. Вы можете переименовать To_Unbounded_String в » «, чтобы упростить себе жизнь.

4. Потрясающе! Большое вам спасибо, ребята!!!

5. function " " (Source : String) return Unbounded_String renames To_Unbounded_String; . Вы также можете сократить Unbounded_String , чтобы сделать вашу жизнь еще проще (пример здесь: github.com/zertovitch/hac/blob/master/src/hal.ads )

Ответ №1:

Ваша проблема связана с тем фактом, что String в Ada это неограниченный тип. Это означает, грубо говоря, что вам нужно указать «что-то», чтобы иметь полезный тип. В случае String вам нужно указать длину строки, поскольку a String — это просто массив Character . Если вы скажете что-то вроде

  X : String;
 

компилятор жалуется, поскольку он не знает длину X и не знает, сколько памяти нужно зарезервировать X . Вам нужно сказать что-то вроде

   X : String(1..6);
 

или что-то вроде

   X : String := "foobar";
 

Обратите внимание, что во втором случае вам не нужно указывать диапазон индексов, поскольку компилятор может вывести его из того факта, что вы используете 6- Character строку для инициализации X .

Это объясняет, почему запрещено объявлять массив just String . Когда вы объявляете массив, компилятору необходимо знать размер каждой записи (все записи должны иметь одинаковый размер, это делает доступ к случайной записи массива очень эффективным). Если вы объявите массив just String , компилятор не будет знать, сколько памяти выделить для каждой записи, поскольку длина не указана.

Обратите внимание, что вместо этого это сработало бы (отказ от ответственности: у меня сейчас нет компилятора Ada, поэтому я не могу проверить код)

 subtype ZIP_Code is String (1..5);
type ZIP_Code_Array is array(Positive range <>) of ZIP_Code;
 

поскольку теперь компилятор знает размер каждой записи a ZIP_Code_Array (5 Character ).

Однако в вашем случае все строки имеют разные размеры. Я вижу два возможных решения

  1. Если максимальная длина известна заранее, вы можете использовать subtype подход, показанный выше, и дополнить строки пробелами. Это зависит от вашего контекста, является ли это подходящим решением или нет
  2. Подход, который я обычно использую, заключается в создании массива Unconstrained_String , который я инициализирую следующим образом
     function " "(X: String) return Unbounded_String
        renames To_Unbounded_String; 
    
    poolOfWords: constant array(1..50) of Unbounded_String := 
        ( "gum",
          "sin",
          "for",
          -- and so on...
        );
     

Обратите renames внимание на то, что определяет унарный как псевдоним для To_Unbounded_String . Это просто синтаксический сахар, который позволяет вам писать "gum" вместо To_Unbounded_String("gum") . Кто-то съеживается от такого синтаксического сахара (и я тоже не в восторге), но в данном случае я думаю, что это того стоит, поскольку это делает код «легче». Было бы неплохо иметь определение унарного рядом с его использованием, просто для ясности.

Альтернативой использованию Unbounded_String является использование универсального пакета Bounded_Strings и определение poolOfWords в виде массива Bounded_String . A Bounded_String может содержать строки переменной длины, если длина меньше, чем привязка, указанная при создании экземпляра Bounded_Strings пакета. Для преобразования туда и обратно String вы можете использовать тот же «трюк с переименованием».

Мое личное предпочтение отдается Unbounded_String , если только у меня нет какой-то особой причины, которая заставляет меня использовать Bounded_String .

Ответ №2:

Другой вариант — использовать стандартные контейнеры Ada. Например:

 Package String_Holder is new Ada.Containers.Indefinite_Holders(
   "="          => Ada.Strings.Equal_Case_Insensitive,
   Element_Type => String
  );
Function " "( Right : String_Holder.Holder ) return String
  renames String_Holder.Element;
Function " "( Right : String ) return String_Holder.Holder
  renames String_Holder.To_Holder;
 

Позволяет хранить в держателе значения типа разного размера String , чтобы вы могли отказаться от какой-либо зависимости Ada.Strings.Unbounded_String и использовать что-то вроде:

 poolOfWords: constant array(Positive range <>) of String_Holder.Holder := 
   "gum",  "sin",  "for",  "cry", -- ...
   "Stuff"
  );
 

Функция Get_Word(индекс : положительный) возвращает строку
( poolOfWords(индекс)) с Pre => Index в диапазоне poolOfWords;

В качестве альтернативы, вы могли бы использовать Ada.Containers.Indefinite_Vectors примерно так:

 Package String_Vector is new Ada.Containers.Indefinite_Vectors(
   "="          => Ada.Strings.Equal_Case_Insensitive,
   Index_Type   => Positive,
   Element_Type => String
  );
 

хотя в Ada 2012 есть одна проблема с этим: вы не можете инициализировать такой вектор непосредственно в объявлении, но можете инициализировать его с помощью вызова функции:

 Function Populate return String_Vector.Vector is
Begin
   Return Result : String_Vector.Vector do
      Result.Append( "some" );
      Result.Append( "data" );
      Result.Append( "HERE" );
   End Return;
End Populate;
 

Возможность сказать X : String_Vectors.Vector := ("this", "that", "the other") находится в Ada 2020 .