I created a simple Ada program that allows a user to populate an array with a maximum of 100 non negative and non zero integers and then prints them out. When I call the function to print out the numbers it prints them out but at the same time it also prints out a bunch of strange and seemingly random numbers. What mistake have I made in my code that is causing the program to output such strange results? This is my first time writing in Ada. For example when I populate the empty array with the numbers [1,2,3,4,5] is prints out this:
1
2
3
4
5
32624
911328835
32624
911328836
32624
67043328
134217726
134217726
2013265921
134217726
134217726
134217726
67043328
909181968
32624
2114692683
89452
914381552
32624
1543503876
2
14
2
14
I am using the gnatmake compiler on ubuntu and when compiling the source code it doesn't give me any error/warning messages.
Here is my source code, I know that I probably don't need to use seperate functions but I implemented them anyways for learning purposes.
with Ada.Containers.Vectors;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
use Ada.Containers;
procedure thing is
type Vector is array (1..100) of Integer;
A: Vector;--array doesn't need to be completely filled up
K: Integer;
--array filling function below
function mkarr return Vector is --asks user to fill empty array with positive and non zero integers
begin
Ada.Text_IO.Put ("enter numbers to fill array, negative or 0 will stop process: ");
for I in 1..100 loop
Ada.Integer_Text_IO.Get(K);
if K>0 then
A(I) := K;
end if;
if K<=0 then
return A;
end if;
end loop;
return A;
end;
--array printing prodcedure below
procedure printarr(A: in out Vector) is
begin
for I in A'Range loop
if A(I)>0 then
Ada.Integer_Text_IO.Put(A(I));
New_Line(1);
end if;
end loop;
end printarr;
B: Vector := mkarr;
--main method below
begin
printarr(A);
end thing;
In mkarr, you use a 0 or negative value to mark the end of the input, but you don't store that value in the array. If whatever garbage is stored in the array after the end of the values you entered happens to be positive, there's no way to tell that it's not a valid value.
In printarr, if you encounter a 0 or negative value you don't print it -- but you continue printing the remaining positive values (which are garbage).
If you store a sentinel value in mkarr, and quit printing in printarr when you encounter the sentinel, the program should work.
Some other notes:
A and K are used only inside mkarr. They should be local to mkarr.
You never use Ada.Containers.Vectors. You can delete the corresponding with and use directives.
For more advanced usage, you can have mkarr return an array containing only the entered data, by making Vector an unconstrained array type and returning a slice. It's still easier to define a fixed-length array inside mkarr. Allowing arbitrarily many inputs is tricker -- but Ada.Containers is probably a good way to do that. (Disclaimer: I haven't looked at Ada.Containers.)
Expanding on Keith’s answer:
You want a variable-length result, so the more Ada-like way is to use an "unconstrained array":
type Vector is array (Positive range <>) of Integer;
This means that you can create instances of the array of any size you like, so long as the bounds are positive: 1 .. 100, 42 .. 43 etc. You can even create a zero-length array by specifying the last bound (called ’Last) to be less than the first (called ’First). In this special circumstance, you’re allowed to use a ’Last outside the range of the index type (in this case, 0, or even -42 if you want to confuse people!).
function mkarr return Vector is
We have to have an actual array to accumulate the values, so set an upper limit of 100.
Result : Vector (1 .. 100);
We need to know how many elements there are.
Last : Natural := Result'First - 1;
More or less as before,
K : Integer;
begin
Ada.Text_IO.Put
("enter numbers to fill array, negative or 0 will stop process: ");
for I in Result'Range loop
We can return an array with at most Result’Length elements.
Ada.Integer_Text_IO.Get (K);
if K > 0 then
We can add one more element.
Last := Last + 1;
Result (Last) := K;
else
There are fewer than 100 elements, so return just that slice.
return Result (Result'First .. Last);
end if;
end loop;
We’ve accumulated 100 results, so return all of them.
return Result;
end mkarr;
As noted here, your example mentions Ada.Containers.Vectors with no further reference to the package. If you pursue such a solution, discussed here, you might first instantiate the generic package Vectors to get a resizable container of integers:
package Integer_Container is new Vectors (Natural, Integer);
Your function to make an array can then declare a Vector named A and initialize its elements and length to specific values:
function Make_Array (Size : Count_Type) return Vector is
A : Vector := To_Vector (New_Item => -1, Length => Size);
…
You can use Iteration in Ada 2012 to simplify your data collection in Make_Array:
for E of A loop
Get (K);
if K > 0 then
E := K;
else
exit;
end if;
end loop;
Similarly, a procedure Print_Array (A : in Vector) might loop like this:
for E of A loop
Put (E, 4);
end loop;
A typical usage might look something like this:
Print_Array (Make_Array (42));
Additional details may be found here and here.
Related
I posted the other day here because I was confused behind the syntax of Fortran and got an answer that helped a lot. But now I'm stuck in the same pickle, but with Ada this time.
Below is the gist of my program. I'm trying to read a sentence from the user and convert each character in it into morse code.
with Ada.Text_IO;
with Ada.Strings;
with Ada.Strings.Unbounded;
with Ada.Strings.Unbounded;
with Ada.Command_Line;
use Ada.Text_IO;
procedure MorseCode is
tempSentence : array(1 .. 20) of string;
last : Integer;
size : Integer;
i : Integer;
begin
Put("Enter the size of your sentence: ");
Get(size);
Put("Enter the sentence you would like to convert to morse code: ");
Get_Line(tempSentence, last);
Put_Line(tempSentence);
While_Loop :
while i < size loop
if tempSentence(i .. i) = 'A' or tempSentence(i) = 'a' then
Put(".- ");
elsif tempSentence(i .. i) = 'B' or tempSentence(i) = 'b' then
Put("-... ");
elsif tempSentence(i) = 'C' or tempSentence(i) = 'c' then
Put("-.-. ");
end if;
end loop While_Loop;
end;
I probably shouldn't, but the problems here go far beyond simple syntax errors so here's a few pointers to a more Ada-like approach.
Words in bold are worth reading about in more detail in a good Ada book.
First, stop thinking at such a low level. Strings are arrays of characters, and arrays in Ada offer facilities such as array attributes to make programming simpler - and especially program maintenance.
Arrays are fixed in length when you create them, but that length doesn't have to be determined until then. So you can declare an array and initialise it with a function call - it takes its size from the result of the function call.
Use this facility and eliminate size as a separate thing altogether.
When the declare block ends, the array goes out of scope, so it's freed automatically, and if it's in a loop, the function call can return a different size string, so the array bounds can be different each time - to problem.
We need to loop over all characters in the array, but we don't know its size ... so just ask it, for example via its range attribute.
And rather than writing every test twice, use a function from the Ada.Characters.Handling package to ensure we only hve to deal with lower case.
Also, if statements are a poor choice for this task, a case statement is simpler and shorter - I've shown both below
with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Characters.Handling;
procedure MorseCode is
begin
Put("Enter the sentence you would like to convert to morse code: ");
declare
Sentence : String := Get_Line;
begin
Put_Line(Sentence);
for i in Sentence'range loop
if Ada.Characters.Handling.To_Lower(Sentence(i)) = 'a' then
Put(".- ");
-- elsif etc ... not very nice
end if;
case Ada.Characters.Handling.To_Lower(Sentence(i)) is
when 'b' => Put("-...");
when 'c' => Put("-.-.");
when others => null;
end case;
end loop;
end;
end;
There are also bounded and unbounded strings which you don't need in this simple example. They are a little tedious to use sometimes, you need type conversions between them and plain Strings.
How can we implement a function to average all elements of a dynamic multidimensional array in Delphi 6? Such as:
Function Average(arr:any_array):extended;
Where arr is a dynamic array and may have 1 or more dimensions.
Linked question: how can we linearize a multidimension array? Do you have any example?
I ask this because I have many different arrays which I must average, if possible, with the one same function.
If you have one dimensional array you can simply use Mean function located in Math unit.
But for multidimensional array I'm afraid there is no already available function. The main reason is that Delphi treats multidimensional arrays in a rather specific way.
While in many other programming languages multidimensional array are in fact just one dimensional array with modified mechanism for accessing array elements which works something like this:
Actual Array Item Index = First parameter + (Second parameter * Size of first dimension)
In Delphi multidimensional arrays are represented as multiple one dimensional array referenced by another array like so:
Array of One Dimensional Array
EDIT 2: As mentioned by David in his comment these type of arrays are also known as Jagged arrays or Ragged arrays.
Why is this so different? Why is it so important? Because those One Dimensional Arrays that from multidimensional array in Delphi don't need to be of the same size. So data in your multidimensional array can actually look like this (where each X represents one array item):
XXXX
XXXXXXX
XXXX
XXXXX
So I'm afraid you will have to write your own function for this. But that should not be so hard.
All you need is a mechanism to iterate through every item in your arrays (I bet you have this already implemented in your code somewhere) so you can sum up the value of all elements in your arrays. And then you divide that sum with the number of elements in your arrays in order to get the arithmetical average of all items.
EDIT 3: I'm not sure if you could have single code to iterate through all of your dynamic arrays. I mean at least you need to know how many dimensions does your array have. This is required because for iterating through multiple dimensions of an array you actually need to recursively iterate through each separate array that represents separate dimensions.
The code for summing up elements in a 3D dynamic array would look like this:
type
TDyn3Darray = array of array of array of Single;
implementation
function SumOf3DArray(Dyn3DArray: TDyn3DArray): Single;
var X,Y,Z, a: Integer;
ArrItem: Single;
begin
Result := 0;
SetLength(Dyn3Darray,5,8,40);
for X := Low(Dyn3Darray) to High(Dyn3Darray) do
begin
for Y := Low(Dyn3Darray[X]) to High(Dyn3Darray[X]) do
begin
for Z := Low(Dyn3Darray[X,Y]) to High(Dyn3Darray[X,Y]) do
begin
Result := Result + Dyn3Darray[X,Y,Z];
end;
end;
end;
end;
I believe you will be capable to modify the code accordingly for different number of dimensions yourself.
EDIT 1: As for linearizing of multidimensional arrays in which I believe you mean changing the multidimensional array in one dimensional array you would just copy elements of separate dimensions and add them into one dimensional array.
But doing that just in order to get average value of all elements would not be practical because for first you would need double the amount of memory and as second in order to copy all the elements from multiple dimension into one you would probably have to iterate through every element in the first place. And then whatever method would you use for calculating average value would also have to iterate through all those copied elements again and thus just slowing down the process for no good reason.
And if you are interested in replacing multidimensional arrays with one dimensional arrays while still threating its elements as if they are in multidimensional array check my explanation of how some other programming languages treat multidimensional array.
NOTE that this would require you accessing all items of such arrays through special methods and will thus result in large changes of your code where you interact with these arrays. Unless if you would wrap these arrays in a class with default multi indexed property that will allow you accessing the array items. Such approach would require additional code for initialization and finalization of such classes and require a slightly modified approach when resizing of such arrays.
Delphi does not provide easy ways for you to inspect the dimensions of an arbitrary dynamic array. Indeed it provides no means for you to pass around an arbitrary dynamic array. However, in practice, you will encounter one and two dimensional arrays commonly, and seldom anything of higher dimensions. So you need to write only a handful of functions:
type
TDoubleArray1 = array of Double;
TDoubleArray2 = array of TDoubleArray1;
TDoubleArray3 = array of TDoubleArray2;
function Mean(const arr: TDoubleArray1): Double; overload;
function Mean(const arr: TDoubleArray2): Double; overload;
function Mean(const arr: TDoubleArray3): Double; overload;
Implement like this:
function Mean(const arr: TDoubleArray1): Double;
var
i: Integer;
begin
Result := 0.0;
for i := low(arr) to high(arr) do
Result := Result + arr[i];
Result := Result / Length(arr);
end;
function Mean(const arr: TDoubleArray2): Double;
var
i, j, N: Integer;
begin
Result := 0.0;
N := 0;
for i := low(arr) to high(arr) do
for j := low(arr[i]) to high(arr[i]) do
begin
Result := Result + arr[i,j];
inc(N);
end;
Result := Result / N;
end;
function Mean(const arr: TDoubleArray3): Double;
var
i, j, k, N: Integer;
begin
Result := 0.0;
N := 0;
for i := low(arr) to high(arr) do
for j := low(arr[i]) to high(arr[i]) do
for k := low(arr[i,j]) to high(arr[i,j]) do
begin
Result := Result + arr[i,j,k];
inc(N);
end;
Result := Result / N;
end;
It would astound me if you really needed higher dimensions than this, but it is easy to add.
Some points:
I'm using Double rather than Extended for reasons of performance, as the latter has a size of 10 which leads to a lot of mis-aligned memory access. If you cannot bring yourself to avoid Extended you can readily adapt the code above.
You could probably find low-level methods using RTTI to write a single function that iterates over any dynamic array, however, the RTTI would likely impact performance. I personally don't see any point in going that route. Use a handful of functions like this and be done.
I have a programm that reads an array of letters (it can be any text). Then I need to compare the 1st and the 4th element of each line of code but the programm doesn't allow me to do this. How can I get an access to those elements in order to compare them?
Program acmp387;
uses crt;
var
n, i, answer : integer;
letters : array[1..1000] of string;
Begin
read(n);
for i:=1 to n do
begin
read(letters[i]);
if ord(letters[i][1]) = ord(letters[i][4])
then answer := answer + 1;
end;
writeln(answer);
readkey;
End.
I'm interested in this line:
if ord(letters[i][1]) = ord(letters[i][4])
Your access is OK (if all strings have at least four characters, for strings with 0 to 3 characters there may be an error/message). May be you have a problem to run your program and it does not behave as expected.
Your program will work as expected if you replace the read statements by readln. A read statements makes sense only in limited situations, in interactive programs you will almost always use readln. With these changes and the input
5
abcdef
abcabc
0101010101010101
10011001
123456
you will get the result display 2 (the lines/strings abcabc and 10011001 meet the criterion and will increment answer).
I have 2 or more dynamic string array that fill with some huge data , i want to merge this 2 array to one array , i know i can do it with a for loop like this :
var
Arr1, Arr2, MergedArr: Array of string;
I: Integer;
begin
// Arr1:= 5000000 records
// Arr2:= 5000000 records
// Fill MergedArr by Arr1
MergedArr:= Arr1;
// Set length of MergedArr to length of ( Arra1 + Arr2 )+ 2
SetLength(MergedArr, High(Arr1)+ High(Arr2)+2);
// Add Arr2 to MergedArr
for I := Low(Arr2)+1 to High(Arr2)+1 do
MergedArr[High(Arr1)+ i]:= Arr2[i-1];
end;
but it is slow on huge data , is there faster way like copy array memory data ?
First of all string is special, so it should be treated specially: Don't try outsmarting the compiler, keep your code unchanged. String is special because it's reference counted. Every time you copy a string from one place to an other it's reference count is incremented. When the reference count reaches 0, the string is destroyed. Your code plays nice because it lets the compiler know what you're doing, and in turn the compiler gets the chance to properly increment all reference counts.
Sure, you can play all sorts of tricks as suggested in the comments to gabr's answer, like filling the old arrays with zero's so the reference count in the new array remains valid, but you can't do that if you actually need the old arrays as well. And this is a bit of a hack (albeit one that will probably be valid for the foreseeable future). (and to be noted, I actually like this hack).
Anyway, and this is the important part of my answer, your code is most likely not slow in the copying of the strings from one array to the other, it's most likely slowly somewhere else. Here's a short console application that creates two arrays, each with 5M random strings, then merges the two arrays into a third and displays the time it took to create the merge. Merging only takes about 300 milliseconds on my machine. Filling the array takes much longer, but I'm not timing that:
program Project26;
{$APPTYPE CONSOLE}
uses SysUtils, Windows;
var a, b, c: array of string;
i: Integer;
Freq: Int64;
Start, Stop: Int64;
Ticks: Cardinal;
const count = 5000000;
begin
SetLength(a,count);
SetLength(b,count);
for i:=0 to count-1 do
begin
a[i] := IntToStr(Random(1));
b[i] := IntToStr(Random(1));
end;
WriteLn('Moving');
QueryPerformanceFrequency(Freq);
QueryPerformanceCounter(Start);
SetLength(c, Length(a) + Length(b));
for i:=0 to High(a) do
c[i] := a[i];
for i:=0 to High(b) do
c[i+Length(a)] := b[i];
QueryPerformanceCounter(Stop);
WriteLn((Stop - Start) div (Freq div 1000), ' milliseconds');
ReadLn;
end.
You can use built-in Move function which moves a block of memory to another location. Parameters are source and target memory blocks and size of data to be moved.
Because you are copying strings, source arrays must be destroyed after the merging by filling them with zeroes. Otherwise refcounts for strings will be all wrong causing havoc and destruction later in the program.
var
Arr1, Arr2, MergedArr: Array of string;
I: Integer;
begin
SetLength(Arr1, 5000000);
for I := Low(Arr1) to High(Arr1) do
Arr1[I] := IntToStr(I);
SetLength(Arr2, 5000000);
for I := Low(Arr2) to High(Arr2) do
Arr2[I] := IntToStr(I);
// Set length of MergedArr to length of ( Arra1 + Arr2 )+ 2
SetLength(MergedArr, High(Arr1)+ High(Arr2)+2);
// Add Arr1 to MergedArr
Move(Arr1[Low(Arr1)], MergedArr[Low(MergedArr)], Length(Arr1)*SizeOf(Arr1[0]));
// Add Arr2 to MergedArr
Move(Arr2[Low(Arr2)], MergedArr[High(Arr1)+1], Length(Arr2)*SizeOf(Arr2[0]));
// Cleanup Arr1 and Arr2 without touching string refcount.
FillChar(Arr1[Low(Arr1)], Length(Arr1)*SizeOf(Arr1[0]), 0);
FillChar(Arr2[Low(Arr2)], Length(Arr2)*SizeOf(Arr2[0]), 0);
// Test
for I := Low(Arr1) to High(Arr1) do begin
Assert(MergedArr[I] = IntToStr(I));
Assert(MergedArr[I] = MergedArr[Length(Arr1) + I]);
end;
// Clear the array to see if something is wrong with refcounts
for I := Low(MergedArr) to High(MergedArr) do
MergedArr[I] := '';
end;
An excellent maxim is that the fastest code is that which never runs. Since copying is expensive you should look to avoid the cost of copying.
You can do this with a virtual array. Create a class which holds an array of array of string. In your example the outer array would hold two string arrays.
Add a Count property that returns the total number of strings in all of the arrays.
Add a default indexed property that operates by working out which of the outer arrays the index refers to and then returns the appropriate value from the inner array.
For extra points implement an enumerator to make for in work.
E.g. I have this array:
type
OptionRange = array[ 1..9 ] of integer;
How do I check if array[x] exists?
Actually, I want to limit user input with the array index. Am I doing the wrong thing? Is there a better practical solution?
In Free Pascal and the Borland dialects (and perhaps elsewhere as well), you can use the Low and High functions on the array type or a variable of the array type. I see this used most often to determine the bounds for for loops:
var
range: OptionRange;
i: Integer;
begin
for i := Low(range) to High(range) do begin
range[i] := GetOptionRangeElement(i);
end;
end;
You can also define a subrange type and then use it to define both the array and the index variables you use on the array:
type
OptionRangeIndex = 1..9;
OptionRange = array[OptionRangeIndex] of Integer;
var
range: OptionRange;
i: OptionRangeIndex;
Then, when you have range checking enabled (assuming your compiler offers such a feature) and you use a value that's outside the range for OptionRange indices, you'll get a run-time error that you can catch and handle however you want.
I'm not really sure what an option range is or why an array of nine integers would be used to represent one, but I figure that's a name-selection issue.