Array of strings in Ada - arrays

So, I'm coding in Ada for the first time and have to create this pre-populated array of strings, so I can use a random method and call a different value each time.
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");
But when I try to compile I get this error: "unconstrained element type in array declaration".
I Googled all over internet and couldn't find a solution to that. I even tried to use unbounded_string, but didn't work as well. It works fine when I pre-populate an array of integers, floats, but when it come to strings it's not working.
Can someone help me and explain what's going on and how to solve it? I really don't want to declare the array and then populate each of the 50 indexes one by one.

Your problem stems from the fact that String in Ada is an unconstrained type. This means, roughly, that you need to specify "something" in order to have a usable type. In the case of String you need to specify the string length, since a String is just an array of Character. If you say something like
X : String;
the compiler complains since it does not know the length of X and it does not know how much memory to reserve for X. You need to say something like
X : String(1..6);
or something like
X : String := "foobar";
Note that in the second case you do not need to specify the index range since the compiler is able to deduce it from the fact that you use a 6-Character string to initialize X.
This explains why it is illegal to declare an array of just String. When you declare an array the compiler needs to know the size of every entry (all the entries must share the same size, this makes access to a random entry of the array very efficient). If you declare an array of just String, the compiler would not know how much memory to allocate to every entry since the length is unspecified.
Note that this instead would work (disclaimer: I have not an Ada compiler at hand now, so I cannot check the code)
subtype ZIP_Code is String (1..5);
type ZIP_Code_Array is array(Positive range <>) of ZIP_Code;
since now the compiler knows the size of every entry of a ZIP_Code_Array (5 Character).
In your case, however, the strings have all different sizes. I can see two possible solutions
If the maximum length is known in advance you can use the subtype approach shown above and pad the strings with spaces. It depends on your context if this is a suitable solution or not
The approach that I typically use is to create an array of Unconstrained_String that I initialize as follows
function "+"(X: String) return Unbounded_String
renames To_Unbounded_String;
poolOfWords: constant array(1..50) of Unbounded_String :=
(+"gum",
+"sin",
+"for",
-- and so on...
);
Note the renames that defines the unary + as an alias for To_Unbounded_String. This is just syntactic sugar that allows you to write +"gum" instead of To_Unbounded_String("gum"). Someone cringes at this type of syntactic sugar (and I am not so keen either), but in this case I think it is worth since it makes the code "lighter." It would be nice to have the definition of unary + near its use, just for clarity.
An alternative to the use of Unbounded_String is to use the generic package Bounded_Strings and define poolOfWords as an array of Bounded_String. A Bounded_String can hold strings with variable length, as long as the length is smaller than the bound specified when instantiating the Bounded_Strings package. In order to convert back and forth String you can use the same "rename trick."
My personal preference goes to the Unbounded_String, unless I have some special reason that forces me to use the Bounded_String.

Another option is to use Ada's standard containers. For example:
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;
Allows you to store in the holder varying sized values of type String, so you could forego any dependence on Ada.Strings.Unbounded_String and use something like:
poolOfWords: constant array(Positive range <>) of String_Holder.Holder :=
+"gum", +"sin", +"for", +"cry", -- ...
+"Stuff"
);
Function Get_Word(Index : Positive) return String is
(+poolOfWords(Index)) with Pre => Index in poolOfWords'Range;
Alternatively, you could use Ada.Containers.Indefinite_Vectors like so:
Package String_Vector is new Ada.Containers.Indefinite_Vectors(
"=" => Ada.Strings.Equal_Case_Insensitive,
Index_Type => Positive,
Element_Type => String
);
though there is one problem with this in Ada 2012: You can't initialize such a vector directly within the declaration, but can initialize it via function-call:
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;
Being able to say X : String_Vectors.Vector := ("this", "that", "the other") is in Ada 2020 .

Related

VHDL const string array with different length

I want to make a list of strings in a Testbench to load different files for example.
I tried:
type tastring ARRAY(iADCCount_C-1 downto 0) of string;
constant Filenames : tastring := ("file.txt",
"anotherfile.txt",
"jetanotherfile.txt");
Its not possible to have variable length strings in an array.
Also:
type tpstring is access string;
type tpastring is ARRAY(iADCCount_C-1 downto 0) of tpstring;
constant Filenames : tpastring := (new string'("file.txt"),
new string'(anotherfile.txt"),
new string'(jetanotherfile.txt"));
Does not work! You cannot make an access type constant. Do I miss something? Is there a way to make a list of Strings without padding them to the same size?
You are almost correct :)
The second code snippet must use a variable, because access types can only be used for objects of kind variable.
type line_vector is array(iADCCount_C-1 downto 0) of line;
variable Filenames : line_vector := (
new string'("file.txt"),
new string'("anotherfile.txt"),
new string'("jetanotherfile.txt")
);
Note 1: added missing " characters.
Note 2: type line is already defined in VHDL.
Note 3: type line_vector will be defined by VHDL-2017.
As an alternative, you can fill all string with character NUL. You might want to implement two functions for sizing the string to the constant's size and to trim the string (remove trailing NUL characters.
See this answer. AFAIK it is impossible to have arrays of variable-length strings.
If you implement custom trim functions as suggested in the answer, try using a fixed spacing character that cannot exist in file names(like ? for windows), as it will also ensure no problems with NUL-characters or other non-prinatbles that can cause hiccoughs with different synthesis tools.

Slicing Multidimensional Array by a variable

I am writing a method which accepts a two dimensional array of doubles and an int row number as parameters and returns the highest value of the elements in the given row.
it looks like this:
function getHighestInRow(A, i)
return(maximum(A[:i,:]))
end
the issue i am having is when i slice the array with
A[:i,:]
I get an argument error because the :i makes i get treated differently.
the code works in the other direction with
A[:,i,:]
Is there a way to escape the colon? so that i gets treated as a variable after a colon?
You're doing something strange with the colon. In this case you're using the symbol :i not the value of i. Just getHighestInRow(A,i) = maximum(A[i,:]) should work.
Edit: As Dan Getz said in the comment on the question, getHighestInRow(A,i) = maximum(#view A[i,:]) is more efficient, though, as the slicing will allocate a temporary unnecessary array.

How can I specify a type for a function argument without restricting its dimensions?

In Julia, I want to specify the type of a function argument as an array of arrays. So I have
function foo{T <: Any}(x::Array{Array{T}})
but if I set the argument x in the REPL, for example:
x = Array[[0,1],[1,2,3],[0,1,2,4]]
then it automatically gets the following type assignment (for example), which includes its dimensions:
x::Array{Array{T,N},1}
so that I get the error
ERROR: `foo` has no method matching foo(::Array{Array{T,N},1}).
I don't want to restrict the array dimensions at all, so was thinking that the solution maybe something along the lines of
function foo{T <: Any, N <: Number}(x::Array{Array{T,N},N})
but this doesn't work either.
How can I specify the argument type to be an array of arrays?
Given an array of arrays x = Array[isodd(i) ? [1i,2i] : [1.0i 2.0i] for i=1:10], Julia reports its type as Array{Array{T,N},1}. This is deceiving, as it seems to imply that there exists some T and some N for which the above type will match. But that's not the case: the odd elements will be of type Array{Int,1}, and the evens are Array{Float64,2}. So when you try to write a method for foo with the type parameters:
foo{T,N}(::Array{Array{T,N},1}) = T,N
What are T and N for x? Clearly, there is no such N — it's both 1 and 2! And the elements of these subarrays aren't of type Any — they're both Int and Float64. The same applies for Array[[0,1],[0,1,2]], even though in your example you know that T and N are consistent, Julia's type system doesn't… and you could potentially push elements that aren't Int vectors.
There are quite a few ways around this. The best approach is to try to make sure that your arrays always have concrete (or at least uniform) element types, but that's not always possible. Given your example x above, you could instead write: x = Array{Int,1}[[0,1],[1,2,3],[0,1,2,4]].
Another alternative is to change your function signature:
foo{N}(x::Array{Array,N}) = 1 # Will *only* work for arrays like x above
foo{T<:Array, N}(x::Array{T,N} = 2 # Will work for all arrays of arrays
The first will only apply if you have exactly that type due to invariance, whereas the second will work for all Arrays of Arrays, both poorly-typed and concrete.
(Edit: As a final note, N<:Number won't match literal numbers. It will match types that are a subtype of Number, like Real or Int. There's currently no way to express that a type parameter must be a value of type Int beyond the convention that N is an integer).

Ada 83 Address to Array Conversion

In the code I inherited, a buffer is passed to a procedure using its start address and length as parameters. This procedure code uses inline assembly language to process the buffer contents.
In a new procedure that uses the same parameters, I want to refer to the buffer as an array but I want to use the same parameters and their types that the existing procedure uses. This is so I don't have to make intrusive modifications to the original code except to use the same calling signature, which is like this:
procedure Push_Buffer(
Source_Address : Address;
Count : Natural);
In this case, the buffer is just an array of machine words. I want to refer to it as an array of machine words, and already have types (Word and Buffer_Type) that are used elsewhere without a problem. (The objects of unconstrained type Buffer_Type have their constraints defined where they are used.)
I would like to refer to the buffer that's being passed by address as an array within my procedure. How would I do this?
Like this (which works with -gnat83, but might not work with a real Ada83 compiler; I don’t have one to check with):
with Text_IO; use Text_IO;
with System;
procedure Push is
type Integers is array (Positive range <>) of Integer;
procedure Push_Buffer (Source_Address : System.Address;
Count : Natural) is
Source : Integers (1 .. Count);
for Source use at Source_Address;
begin
for J in Source'Range loop
Put_Line (Integer'Image (J) & " => " & Integer'Image (Source (J)));
end loop;
end Push_Buffer;
begin
declare
Data : Integers (1 .. 3) := (4, 5, 6);
begin
Push_Buffer (Data'Address, Data'Length);
end;
end Push;
As a side note, probably not relevant to your situation: if there was any question of default initialization of the object whose address you’ve specified (for example, if it’s a structure containing access variables, which are default-initialized to null) you would need to suppress the initialization by writing the declaration as
Source : Integers (1 .. Count);
pragma Import (Ada, Source);
for Source use at Source_Address;
(or something compiler-dependent like that; GNAT says warning: (Ada 83) pragma "Import" is non-standard). See the GNAT Reference Manual 8.15, Address Clauses, about half-way down.
I would use an unconstrained array as the parameter, rather than a System.Address. This ensures that the compiler does not pass the bound information (aka "fat pointers" in gcc and gnat), and should be compatible with a pragma import.
type Unconstrained_Integer_Array is array (Positive) of Integer;
procedure Push_Buffer (Buffer : Unconstrained_Integer_Array;
Length : Natural)
is
begin
for A in 1 .. Length loop
...
end loop;
end Push_Buffer;
We use similar tricks when interfacing with C in the GtkAda bindings

How can I assign a value to a Char array?

Say I have the variable:
Var question : array[1..50] of char;
When I do:
question := 't'; //What is the correct way to change the value?
It returns an error:
Incompatible types: 'array[1..50] of Char' and 'Char'
Note: I want to have a max string size of 50 chars, not 50 different chars.
The reason for this question is that I have a record in another unit(This is just a basic example, not what I actually have written above) In that unit I have a Record, which I can't use the string data type in(Or is there a way? please explain if there is). I just need to know how to give an array of chars a value.
While Delphi strings and arrays of char are related, they are not the same.
Delphi overloads assignment of strings and literals (char and string) to array of chars, but only when the lower array bound is zero.
The following code works for me in D2007 and Delphi XE:
var x : array[0..49] of char;
begin
x:='b'; // char literal
x:='bb'; // string literal
end.
If I change the [0 to [1 it fails. This limitation probably simplifies the language helper that takes care of this, and probably the feature is only meant for dealing with converted C structs where arrays always have lower bound 0.
Are you sure that you can't use string data type in a record?
Anyways...
type
TCharArray = array[Char] of Char;
function StringToArray(Str: string): TCharArray;
begin
FillChar(Result, High(Byte), #0);
Move(Str[1], Result, length(Str));
end;
procedure TestCharArray;
var
question: TCharArray;
begin
question := StringToArray('123');
ShowMessage(PChar(#question));
end;
Also take a look at StrPCopy function.
If you don't need unicode characters, you should just define your string like string[50].
After that you don't need any functions or conversions to work with that string, and it'll be just as easy to read and write it to a file.
Hscores = record
var
_topscore : integer;
_topname : string[50];
end;
I'm pretty sure you can use strings in record types.
This blog entry shows an example: http://delphi.about.com/od/beginners/a/record_type.htm
In order to assign a value to the Char array, you have to index it, like any other array:
question[1] := 't';

Resources