How do you initialize an array with only one element, without hardcoding the first index? - arrays

I am trying to pass an array of Unbounded_String to a function, and I don't care about the range of the index, as the function is going to loop over each element.
The (element1, element2) syntax automatically starts at the first index value in the range, then increments for the second value given, which works fine for more than one value. However, for a single value, this cannot be used as the parentheses are considered superfluous.
This code shows the error messages for each of the attempts I have made. (1) works, but (2), the preferable syntax for passing a single-element array, does not. (3) works, and is given as an answer to this similar question. However, this hardcodes the first index of the range into the calling side; if the String_Array implementation changes, all the call-sites have to be changed, even though they don't care about the index values used.
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Main is
function "+"(S: String) return Ada.Strings.Unbounded.Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
type String_Array is array (Positive range <>) of Unbounded_String;
procedure Foo(input : in String_Array) is
begin
null;
end Foo;
begin
Foo((+"one", +"two")); --(1)
--Foo((+"only")); --(2) positional aggregate cannot have one component
Foo((1 => +"only")); --(3)
--Foo((String_Array'First => +"only")); --(4) prefix for "First" attribute must be constrained array
--Foo((String_Array'Range => +"only")); --(5) prefix for "Range" attribute must be constrained array
--Foo((String_Array'Range'First => +"only")); --(6) range attribute cannot be used in expression
--Foo((String_Array'Range'Type_Class'First => +"only")); --(7) range attribute cannot be used in expression
end Main;

What you want (2) is indeed impossible as it could be mistaken for a parenthesized expression (see http://www.adaic.org/resources/add_content/standards/12aarm/html/AA-4-3-3.html note 10).
If you really want to avoid expression (3) for the reasons you stated, as workaround, you could define a function to handle the one-element array case:
function Singleton_String_Array (S: String) return String_Array is ((1 => + S));
-- one element call
Foo(Singleton_String_Array ("only"));
It reuse your expression (3) but the first index hardcoding is no longer done on call site.
You can also overload your foo function to handle the special one-element case:
procedure Process_String (input : in Ada.Strings.Unbounded.Unbounded_String) is
begin
null;
end Process_String;
procedure Foo(input : in String_Array) is
begin
for string of input loop
Process_String (string);
end loop;
end Foo;
procedure Foo(input : in Ada.Strings.Unbounded.Unbounded_String) is
begin
Process_String (input);
end Foo;
-- One element call
Foo(+"only");

The short answer is that all array objects must be constrained, which means callers usually have to decide on the array bounds.
However, you know the index type, and could do
Foo((Positive'First => +"only"));
which doesn't really answer your question, since someone may still fiddle with the array range, and there's not really any guard against that.
Adding a new subtype as the range may be a viable solution, though:
subtype String_Array_Range is Positive;
type String_Array is array (String_Array_Range range <>) of Unbounded_String;
...
Foo((String_Array_Range'First => +"only"));
Any fiddling can now be done on the String_Array_Range subtype without affecting any callers. But there's still no guarantee against evil programmers changing the index type of the array itself...

type String_Array is array (Positive range <>) of Unbounded_String;
Declares a type of array but doesn't provide the size.
Remember that an array has a static size.
So String_Array'First and String_Array'Range don't match to anything.
If you declared
type my_String_Array is String_Array(1 .. 35);
my_arr : my_String_Array
Then my_arr'First denotes 1 and my_arr'Range denotes 1..35.
As long as you don't put a contraint on the type, you won't have access to these attributes.

Related

Golang sub slice

In Go, why does this code work:
package main
import (
"fmt"
)
func main() {
a := []int{1}
a = a[1:]
fmt.Println(len(a))
}
but this doesn't:
package main
import (
"fmt"
)
func main() {
a := []int{1}
a = a[2:]
fmt.Println(len(a))
}
I've heard about capacity in slices, can someone elaborate it?
The Go specification contains the answer to both of your questions:
Slice expressions
Slice expressions construct a substring or slice from a string, array, pointer to array, or slice. There are two variants: a simple form that specifies a low and high bound, and a full form that also specifies a bound on the capacity.
Simple slice expressions
[snippage]
For arrays or strings, the indices are in range if 0 <= low <= high <= len(a), otherwise they are out of range.
Since len(a) is 1, the index 1 is in range, but the index 2 is out of range.
Full slice expressions
For an array, pointer to array, or slice a (but not a string), the primary expression
a[low : high : max]
constructs a slice of the same type, and with the same length and elements as the simple slice expression a[low : high]. Additionally, it controls the resulting slice's capacity by setting it to max - low. Only the first index may be omitted; it defaults to 0. ...
Read the entire spec. It can be a bit slow going, but it's not all that long. There's no need to memorize it all, but having gone through the whole thing once, you should be able to remember that you read that somewhere, and then go back and find what you are looking for.
Slice's capacity is always at least the same with slice's length. When unspecified, the default is always the same as length.
The definition of length of a slice is the number of element set into the slice.
The definition of capacity of a slice is the whole blocks of the slice can provide which is hidden from the caller and has default values of empty(0 for int, "" for string).
When you call value of a certain index like this a[x] then it will get the value at index x within range of lenght.
But when you re-slice the slice like this a[x:y] then it will get the slice at index x within range of capacity till y(max value must be the same with capacity). So as a caller of the slice, you can get the hidden elements of slice within the whole slice structure which default values are empty.
If you don't specify the end of re-sclicing(y) like you did above -> a[2:], then the default end of the re-slicing will be set to lenght, which make your call to a[2:] imply to a[2:1] since lenght of slice is 1. Think about it, calling a sub-slice of a[2:1], what would happen?. Exactly as the error message told us:
panic: runtime error: slice bounds out of range [2:1]
because you're calling sub-slice from the index which is not even within range of capacity(the whole structure of the slice it self). Even if 2 is within the range of slice's capacity, your call is overlapping because the starting index is larger than the capacity.
For summarize:
If you call sub-slice like this -> a[x:]
then x must be within the range of length(inclusive)
If you call sub-slice like this -> a[:y]
then y must be within the range of capacity(inclusive)
If you call sub-slice like this -> a[x:y]
then x and y must be within the range of capacity(inclusive) and x<=y.

How to iterate array in precondition?

I want to iterate through array in a precondition.
But It seems precondition part doesn't allow use of "from" and "across" syntax.
Is there a way to iterate through array in precondition?
insert_last (s: STRING)
require
new_is_longer_than_prevs:
-- here I want to iterate through array "arr" and if length of s is longer than all other previously stored string values in array
do
arr.force (s, arr.upper + 1)
end
The version suggested in the other reply works in most cases (it assumes the lower index of the array is 1). However, the across loop can be used directly on the array rather than on its index range:
new_is_longer_than_prevs:
across arr as c all s.count > c.item.count end
This version works for any lower index and is slightly more efficient at run-time.
You can use 'across ... as ... all ... end' or 'across ... as ... some ... end' in precondition and postcondition. The 'all' version is used to valid if a condition is True for every iteration and the 'some' version is used to valid if the condition is True for at least one iteration. You can use some thing like this in your code:
insert_last (s: STRING)
require
new_is_longer_than_prevs:
across arr.lower |..| arr.upper as la_index all s.count > arr[la_index.item].count end
do
arr.force (s, arr.upper + 1)
end

Ada constant array of string literals

I have a large array in C that I wish to move into my ada project. That array is used to store filenames for assets which will later be loaded. It looks something like:
const char *filenames[NUMBER_OF_FILES] = {
/* file[0] */ "res/0.file",
/* ... */
/* file[n] */ "res/some_more/complicated/file.name"
};
I want to move this into an ada package body, but can't find a decent way to do it. Obviously my first attempt was:
filenames : constant array (File_Index) of String := (
index_0 => "res/0.file",
-- ...
index_n => "res/some_more/complicated/file.name"
);
But of course String is an unconstrained type, so Ada won't allow that. I switched it to use Unbounded_Strings, which worked, but was very ugly (having to wrap each string with To_Unbounded_String.
Is there any way to make an array of unconstrained types whose size will be known at compile time like this, or do I have to use unbounded strings?
It’s a bit low-level and repetitive, but perhaps you can create a little program (maybe even in Ada!) to generate something like
with Ada.Text_IO; use Ada.Text_IO;
procedure Lambdabeta is
F1 : aliased constant String := "res/0.file";
F2 : aliased constant String := "res/some_more/complicated/file.name";
type Strings is array (Positive range <>) of access constant String;
Filenames : constant Strings := (F1'Access,
F2'Access);
begin
for J in Filenames'Range loop
Put_Line (Filenames (J).all);
end loop;
end Lambdabeta;
See also this answer on minimising the pain of using To_Unbounded_String.
Arrays can't contain objects of indefinite types.
Basically you have two options:
Use another container than an array.
Encapsulate the strings in a definite type.
So you could use Ada.Containers.Indefinite_Vectors instead of an array:
with Ada.Containers.Indefinite_Vectors;
package Settings is
------------------------------------------------------------------
-- You may want to put this block in a separate package:
package String_Vectors is
new Ada.Containers.Indefinite_Vectors (Index_Type => Positive,
Element_Type => String);
function "+" (Left, Right : String) return String_Vectors.Vector
renames String_Vectors."&";
function "+" (Left : String_Vectors.Vector;
Right : String) return String_Vectors.Vector
renames String_Vectors."&";
------------------------------------------------------------------
File_Names : constant String_Vectors.Vector :=
"/some/file/name" +
"/var/spool/mail/mine" +
"/etc/passwd";
end Settings;
So you could use Ada.Strings.Unbounded.Unbounded_String instead of String:
with Ada.Strings.Unbounded;
package Settings_V2 is
function "+" (Item : in String) return Ada.Strings.Unbounded.Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
type String_Array is array (Positive range <>)
of Ada.Strings.Unbounded.Unbounded_String;
File_Names : constant String_Array :=
(+"/some/file/name",
+"/var/spool/mail/mine",
+"/etc/passwd");
end Settings_V2;
What has not yet been mentioned is that you can just use a function:
subtype File_Index is Integer range 1 .. 3;
function Filename (Index : File_Index) return String is
begin
case Index is
when 1 => return "res/0.file";
when 2 => return "res/1.file";
when 3 => return "res/some_more/complicated/file.name";
end case;
end Filename;
Using Filename (1) in your code is identical to accessing an array element.

String Arrays in Ada

I have a program in Ada95, in which I have to create an array of strings. This array can contain strings of variable length.
Example:
I have declared the array in which all the indexes can store strings of size 50. When I assign a smaller string to the above array, I get "Constraint Error".
Code:
procedure anyname is
input_array : array(1..5) of String(1..50);
begin
input_array(1):="12345";
end anyname;
I have tried to create the array of Unbounded_Strings. But that doesn't work either. Can anyone tell me how to store this "12345" in the above string array?
If you use Unbounded_String, you cannot assign a string literal to it directly. String literals can have type String, Wide_String, or Wide_Wide_String, but nothing else; and assignment in Ada usually requires that the destination and source be the same type. To convert a String to an Unbounded_String, you need to call the To_Unbounded_String function:
procedure anyname is
input_array : array(1..5) of Ada.Strings.Unbounded.Unbounded_String;
begin
input_array(1) := Ada.Strings.Unbounded.To_Unbounded_String ("12345");
end anyname;
You can shorten the name by using a use clause; some other programmers might define their own renaming function, possibly even using the unary "+" operator:
function "+" (Source : String) return Ada.Strings.Unbounded.Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
procedure anyname is
input_array : array(1..5) of Ada.Strings.Unbounded.Unbounded_String;
begin
input_array(1) := +"12345"; -- uses renaming "+" operator
end anyname;
Not everyone likes this style.
You can use Ada.Strings.Unbounded, illustrated here, or you can use a static ragged array, illustrated here. The latter approach uses an array of aliased components, each of which may have a different length.
type String_Access is access constant String;
String_5: aliased constant String := "12345";
String_6: aliased constant String := "123456";
String_7: aliased constant String := "1234567";
...
Input_Array: array (1..N) of
String_Access :=
(1 => String_5'Access,
2 => String_6'Access,
3 => String_7'Access,
-- etc. up to N
);
Strings in Ada are arrays of characters of fixed length. In order to use strings of variable length (which may often be the case when arrays of strings are needed, e.g. arrays of names, each name being of variable length), each individual string may be declared as an Unbounded_String. The only caveat is that this allocates from the heap memory. Below is a complete example of an array of strings in Ada.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO;
procedure arrayAda is
type DaysArray is array(1..7) of Unbounded_String;
days: DaysArray;
begin
days(1):=To_Unbounded_String("Sunday");
days(2):=To_Unbounded_String("Monday");
days(3):=To_Unbounded_String("Tuesday");
days(4):=To_Unbounded_String("Wednesday");
days(5):=To_Unbounded_String("Thursday");
days(6):=To_Unbounded_String("Friday");
days(7):=To_Unbounded_String("Saturday");
for index in 1..7 loop
Put(days(index));
Put(" ");
end loop;
end arrayAda;
This produces the following output:
$ ./arrayAda
Sunday Monday Tuesday Wednesday Thursday Friday Saturday
I've had a lot of joy from instantiating a container package, e.g.:
package String_Vectors is
new Ada.Containers.Indefinite_Vectors (Positive, String);
It's still a bit fiddly, compared to how easy it is to mess about with strings in a lot of other programming languages, but it's okay.
Fundamentally, Ada is a language designed to be usable without using the heap (at all :-) Most other languages would fall down in a, well, a heap, without the heap.

How to remove/delete particular member or element from static "Array of TPoint"?

I want to remove/delete particular member or element from static "Array of TPoint". How to do this?
If by "static" you mean "non-dynamic" (ie. fixed-size array), then you can't. The best thing you can do is to move it to the end of the array and then mark the last TPoint in some way that will allow you to ignore it on subsequent passes.
If you are talking about a dynamic (ie. variable-sized array, ie. VAR A : ARRAY OF TPoint;) then you can use the following routine:
TYPE
TPointArray = ARRAY OF TPoint;
PROCEDURE RemoveIndex(VAR ARR : TPointArray ; Index: INTEGER);
VAR
I : INTEGER;
BEGIN
IF (Index<LOW(Arr)) OR (Index>HIGH(Arr)) THEN EXIT;
FOR I:=Index TO PRED(HIGH(ARR)) DO ARR[I]:=ARR[SUCC(I)];
SetLength(ARR,PRED(LENGTH(ARR)))
END;

Resources