Pascal procedure pass variable arrays - pascal

When I try to pass variable arrays from procedure to the main program, a[1] in the procedure was supposed to be equal to arr[1] in the main program, like this:
a[1] = arr[1]
a[2] = arr[2]
a[3] = arr[3]
a[4] = arr[4]
a[5] = arr[5]
But the program actually acta like this:
a[1] = ''
a[2] = arr[1]
a[3] = arr[2]
a[4] = arr[3]
a[5] = arr[4]
I don't know what's wrong to the code, can someone point out the mistake?
The simplied code, same problem:
var
arr : array[1..5] of string;
i : integer;
procedure test(var a : array of string);
var
i : integer;
begin
a[1] := 'one';
a[2] := 'two';
a[3] := 'three';
a[4] := 'four';
a[5] := 'five';
for i := 1 to 5 do writeln(a[i]);
end;
begin
test(arr);
write('-----');
for i := 1 to 5 do
begin
writeln(arr[i]);
if arr[i] = '' then writeln('NOTHING');
end;
readln
end.

MartynA gave you the hint to look at "open array parameters" in the online help. But it is not necessary to do what he proposes, using ArrayLoBound etc. The declaration of the actual array can have any index range.
I would do this:
program OpenArrayTest;
{$APPTYPE CONSOLE}
var
{ Initialization like this only possible for global variables. }
Arr: array[11..15] of string = ('once', 'doce', 'trece', 'catorce', 'quince');
I: Integer;
procedure ModifyArray(var A: array of string);
var
I: Integer;
begin
for I := Low(A) to High(A) do
A[I] := A[I] + ' <-- ' + IntToStr(I);
end;
procedure ShowArray(const A: array of string);
begin
for I := Low(A) to High(A) do
Writeln(A[I]);
end;
begin
ModifyArray(Arr);
ShowArray(Arr);
Writeln('-----');
ShowArray(['one', 'two', 'three', 'four', 'five', 'six', 'seven']);
Readln;
end.
The output is:
once <-- 0
doce <-- 1
trece <-- 2
catorce <-- 3
quince <-- 4
-----
one
two
three
four
five
six
seven
In other words, use High() and Low() to access the items in the parameter. Do not use any fixed bounds, since the array can have any length. Also note that you can get the number of items in the array using Length(A) or Length(Arr). You can not only pass static arrays, like Arr, you can also pass dynamic arrays, or use an open array constructor (using [ and ]), like I did in the second call to ShowArray().
More about open arrays in my article "Open arrays and array of const".

Judging by your comment, you are in a bit of a muddle.
In short, if you declare a parameter to a procedure to be an "array of", the array is always zero-based, regardless of the structure of the array you pass to it as an argument, as in
test(arr);
Try the code below. You'll find that when it runs, you get a Range-Check error on the line
a[5] := 'five';
That's because although arr has five elements, they are numbered 0..4, so there is no element of arr with index 5.
Although there are other was to declare procedure parameters, if you want to pass arrays to it as arguments, you have to make sure that either you mentally translate the array indexes as you write the code or (better) you declare the arrays you pass to it as zero-based, as I've done.
And, try and make a habit of turning range-checking on. It will catch error you yourself may overlook.
I'm going to leave you to rewrite your test procedure so that it works correctly as an exercise, because I'm "guessing" that what you've posted is some kind of school- or course-work and you should really put some effort into finding out how to correct your error yourself. If you're still stuck after reading this and trying the obvious solution, ask.
Btw, if you are using Delphi, look up "Open array parameters" in the Online Help. That explains the restrictions on using "array of ..." procedure-parameters.
Also btw, Rudy Velthuis says in his answer "But it is not necessary to do what {MartynA] proposes, using ArrayLoBound etc." That is true, it is not necessary but he has missed my point. If you hard-code array bounds, with values like 1 and 5, and then change them later, it is easy to overlook other values that need updating too, like in your for loop. Defining these values as consts is a good habit to get into because it avoids introducing inconsistencies, but more importantly makes sure you think about what you are doing. IME ...
program arrayparam;
const
ArrayLoBound = 0;
ArrayHiBound = 4;
var
arr : array[ArrayLoBound..ArrayHiBound] of string;
i : integer;
{$R+} // Turn range-checking on
procedure test(var a : array of string);
var
i : integer;
begin
a[1] := 'one';
a[2] := 'two';
a[3] := 'three';
a[4] := 'four';
a[5] := 'five';
for i := 1 to 5 do
writeln(a[i]);
end;
begin
test(arr);
writeln('-----');
for i := ArrayLoBound to ArrayHiBound do
begin
writeln(arr[i]);
if arr[i] = '' then
writeln('NOTHING');
end;
readln
end.

All good answers but just to be complete: the asker can get exactly the result asked for.
It could be that accessing them using 1 to 5 is important for the asker's purposes.
Make the changes below and it will print out as originally expected.
type
TArr = array[ 1..5 ] of string;
var
arr : TArr;
procedure test( var a : TArr );
I agree that defaulting to 0 based arrays is simply easier and using the functions low / hi make it bulletproof.
But I can also see that sometimes indexing in your own way could be of use / important.

Related

How to remove spaces from string with while do operator? Pascal

I have text and I need to remove spaces from beginning of text and from end of text. And I can do it only with while do operator. How can I do that? Here's program code
program RandomTeksts;
uses crt;
var
t:String;
l, x, y:Integer;
const tmin=1; tmax=30;
label
Start,
end;
begin
Start:
clrscr;
writeln('write text (from ',tmin,' to ',tmax,' chars): ');
readln(t);
l:=length(t);
if (l<tmin) or (l>tmax) then
begin
writeln('Text doesn't apply to rules!');
goto end;
end;
clrscr;
begin
randomize;
repeat
x:=random(52+1);
y:=random(80+1);
textcolor(white);
gotoxy(x,y);
writeln(t);
delay(700);
clrscr;
until keypressed;
end;
ord (readkey)<>27 then
goto Start;
end:
end.
Academic problem: Remove leading and trailing spaces from a string using a while loop.
How do we approach this problem?
Well, we certainly would like to create a function that trims a string. This way, we can simply call this function every time we need to perform such an operation. This will make the code much more readable and easier to maintain.
Clearly, this function accepts a string and returns a string. Hence its declaration should be
function Trim(const AText: string): string;
Here I follow the convention of prefixing arguments by "A". I also use the const prefix to tell the compiler I will not need to modify the argument within the function; this can improve performance (albeit very slightly).
The definition will look like this:
function Trim(const AText: string): string;
begin
// Compute the trimmed string and save it in the result variable.
end;
A first attempt
Now, let's attempt to implement this algorithm using a while loop. Our first attempt will be very slow, but fairly easy to follow.
First, let us copy the argument string AText to the result variable; when the function returns, the value of result will be its returned value:
result := AText;
Now, let us try to remove leading space characters.
while result[1] = ' ' do
Delete(result, 1, 1);
We test if the first character, result[1], is a space character and if it is, we use the Delete procedure to remove it from the string (specifically, Delete(result, 1, 1) removes 1 character from the string starting at the character with index 1). Then we do this again and again, until the first character is something other than a space.
For example, if result initially is ' Hello, World!', this will make it equal to 'Hello, World!'.
Full code, so far:
function Trim(const AText: string): string;
begin
result := AText;
while result[1] = ' ' do
Delete(result, 1, 1);
end;
Now try this with a string that consists only of space characters, such as ' ', or the empty string, ''. What happens? Why?
Think about it.
Clearly, in such a case, result will sooner or later be the empty string, and then the character result[1] doesn't exist. (Indeed, if the first character of result would exist, result would be of length at least 1, and so it wouldn't be the empty string, which consists of precisely zero characters.)
Accessing a character that doesn't exist will make the program crash.
To fix this bug, we change the loop to this:
while (Length(result) >= 1) and (result[1] = ' ') do
Delete(result, 1, 1);
Due to a technique known as 'lazy boolean evaluation' (or 'short-circuit evaluation'), the second operand of the and operator, that is, result[1] = ' ', will not even run if the first operand, in this case Length(result) >= 1, evaluates to false. Indeed, false and <anything> equals false, so we already know the value of the conjunction in this case.
In other words, result[1] = ' ' will only be evaluated if Length(result) >= 1, in which case there will be no bug. In addition, the algorithm produces the right answer, because if we eventually find that Length(result) = 0, clearly we are done and should return the empty string.
Removing trailing spaces in a similar fashion, we end up with
function Trim(const AText: string): string;
begin
result := AText;
while (Length(result) >= 1) and (result[1] = ' ') do
Delete(result, 1, 1);
while (Length(result) >= 1) and (result[Length(result)] = ' ') do
Delete(result, Length(result), 1);
end;
A tiny improvement
I don't quite like the space character literals ' ', because it is somewhat difficult to tell visually how many spaces there are. Indeed, we might even have a different whitespace character than a simple space. Hence, I would write #32 or #$20 instead. 32 (decimal), or $20 (hexadecimal), is the character code of a normal whitespace.
A (much) better solution
If you try to trim a string containing many million of characters (including a few million leading and trailing spaces) using the above algorithm, you'll notice that it is surprisingly slow. This is because we in every iteration need to reallocate memory for the string.
A much better algorithm would simply determine the number of leading and trailing spaces by reading characters in the string, and then in a single step perform a memory allocation for the new string.
In the following code, I determine the index FirstPos of the first non-space character in the string and the index LastPos of the last non-space character in the string:
function Trim2(const AText: string): string;
var
FirstPos, LastPos: integer;
begin
FirstPos := 1;
while (FirstPos <= Length(AText)) and (AText[FirstPos] = #32) do
Inc(FirstPos);
LastPos := Length(AText);
while (LastPos >= 1) and (AText[LastPos] = #32) do
Dec(LastPos);
result := Copy(AText, FirstPos, LastPos - FirstPos + 1);
end;
I'll leave it as an exercise for the reader to figure out the precise workings of the algorithm. As a bonus exercise, try to benchmark the two algorithms: how much faster is the last one? (Hint: we are talking about orders of magnitude!)
A simple benchmark
For the sake of completeness, I wrote the following very simple test:
const
N = 10000;
var
t: cardinal;
dur1, dur2: cardinal;
S: array[1..N] of string;
S1: array[1..N] of string;
S2: array[1..N] of string;
i: Integer;
begin
Randomize;
for i := 1 to N do
S[i] := StringOfChar(#32, Random(10000)) + StringOfChar('a', Random(10000)) + StringOfChar(#32, Random(10000));
t := GetTickCount;
for i := 1 to N do
S1[i] := Trim(S[i]);
dur1 := GetTickCount - t;
t := GetTickCount;
for i := 1 to N do
S2[i] := Trim2(S[i]);
dur2 := GetTickCount - t;
Writeln('trim1: ', dur1, ' ms');
Writeln('trim2: ', dur2, ' ms');
end.
I got the following output:
trim1: 159573 ms
trim2: 484 ms

How to forbid equal numbers

I started learning Pascal :) and I was interested on making a kind of Euromillion... However, I don't know how to forbid the same numbers or stars...
I thought this (below) would solve it... But it didn't... Help?
Program euromillion;
var num: array [1..5] of integer;
Procedure numbers;
var i, j: integer;
Begin
write ('Digite o número 1: ');
readln (num[1]);
for i:=2 to 5 do
for j:=1 to (i-1) do
Begin
repeat
write ('Digite o número ', i, ': ');
readln (num[i]);
until (num[i]>=1) and (num[i]<=50) and ((num[i]=num[j])=false);
End;
End;
Begin
numbers;
readln();
End.
Thanks guys :)
Although it is tempting to try and write a single block of code, as you have, it is better not to. Instead, a better way to write a program like this
is to think about splitting the task up into a number of procedures or functions
each of which only does a single part of the task.
One way to look at your task is to split it up into sub-tasks, as follows:
You prompt the user to enter a series of numbers
Once each number is entered, you check whether it is already in the array
If it isn't, you enter it in the array, otherwise prompt the user for another number
Once the array is filled, you output the numbers in the array
So, a key thing is that it would be helpful to have a function that checks whether
a new number is already in the array and returns True if it is and False otherwise. How to do that is the answer to your question.
You need to be careful about this because if you use the array a second time in the
program, you need to avoid comparing the new number with the array contents from
the previous time. I deliberately have not solved that problem in the example code below, to leave it as an exercise for the reader. Hint: One way would be to write a procedure which "clears" the array before each use of it, e.g. by filling it with numbers which are not valid lottery numbers, like negative numbers or zero. Another way would be to define a record which includes the NumberArray and a Count field which records how many numbers have been entered so far: this would avoid comparing the new number to all the elements in the
array and allow you to re-use the array by resetting the Count field to zero before calling ReadNumbers.
program LotteryNumbers;
uses crt;
type
TNumberArray = array[1..5] of Integer;
var
Numbers : TNumberArray;
Number : Integer;
function IsInArray(Number : Integer; Numbers : TNumberArray) : Boolean;
var
i : Integer;
begin
Result := False;
for i:= Low(Numbers) to High(Numbers) do begin
if Numbers[i] = Number then begin
Result := True;
break;
end;
end
end;
procedure ReadNumbers(var Numbers : TNumberArray);
var
i : Integer;
NewNumber : Integer;
OK : Boolean;
begin
// Note: This function needs to have a check added to it that the number
// the user enters is a valid lottery number, in other words that the
// number is between 1 and the highest ball number in the lottery
for i := Low(Numbers) to High(Numbers) do begin
repeat
OK := False;
writeln('enter a number');
ReadLn(NewNumber);
OK := not IsInArray(NewNumber, Numbers);
if not OK then
writeln('Sorry, you''ve already chosen ', NewNumber);
until OK;
Numbers[i] := NewNumber;
end;
end;
procedure ListNumbers(Numbers : TNumberArray);
var
i : Integer;
begin
for i := Low(Numbers) to High(Numbers) do
writeln(Numbers[i]);
end;
begin
ReadNumbers(Numbers);
ListNumbers(Numbers);
writeln('press any key');
readkey;
end.

Separating numbers in a string. Pascal

I have a problem. I'm learning Pascal for only a couple of weeks and I don't know much. I have to write a program that has to calculate something out of 3 entered numbers. The problem is all 3 of them need to be entered in one edit with spaces in between. So basically I have a string 'number number number'. How do I separate these numbers as 3 separate strings so I can convert them into Integer.
In pascal there are built-in procedures to retrieve the input from the console.
The easiest way to get numeric inputs is to use Read()/ReadLn(), which also can make the conversion from string to a numeric value:
procedure GetNumbers(var x,y,z: Integer);
begin
WriteLn('Enter three numbers separated with space and then press enter.');
ReadLn(x,y,z);
end;
Here, the ReadLn() detects three inputs separated with a space, waits for the [Enter] key and assigns the integer values to the x,y,z variables.
Using the copy function is one way. Sorry about the formatting, I can't understand how to paste code snippets properly in these answer sections.
function TMyForm.Add( anEdit : TEdit ) : integer;
var
Idx : integer;
TempString : string;
function GetNext : integer;
begin
result := result + StrToInt( copy( TempString, 1, Idx - 1 ) );
TempString := copy( TempString, Idx + 1, MAXINT );
end;
begin
result := 0;
TempString := anEdit.Text;
repeat
Idx := pos( ' ', TempString );
if Idx > 0 then
result := GetNext;
until Idx = 0;
if trim( TempString ) <> '' then
//this is the last piece of it then
result := result + StrToInt( trim( TempString ) );
end;
You need to also take care that the values entered are numbers and not letters, usually done with try..except blocks.

Count item frequency

Hi I'm using Delphi and I have a StringList with this items:
45
A15
015
A15
A15
45
I want to process it and make a second stringlist that will have
the number of appearance of each element:
45 [2]
015 [1]
A15 [3]
How can I do this with Delphi?
You could use a dictionary:
Frequencies := TDictionary <String, Integer>.Create;
try
// Count frequencies
for Str in StringList do
begin
if Frequencies.ContainsKey (Str) then
Frequencies [Str] := Frequencies [Str] + 1
else
Frequencies.Add (Str, 1);
end;
// Output results to console
for Str in Frequencies.Keys do
WriteLn (Str + ': ' + IntToStr (Frequencies [Str]));
finally
FreeAndNil (Frequencies);
end;
The only problem might be that the order in which the results appear is completely random and dependes on the inner working of the hash map.
Thanks to daemon_x for the full unit code:
program Project1;
{$APPTYPE CONSOLE}
uses SysUtils, Classes, Generics.Collections;
var Str: String;
StringList: TStrings;
Frequencies: TDictionary <String, Integer>;
begin
StringList := TStringList.Create;
StringList.Add('45');
StringList.Add('A15');
StringList.Add('015');
StringList.Add('A15');
StringList.Add('A15');
StringList.Add('45');
Frequencies := TDictionary <String, Integer>.Create;
try
// Count frequencies
for Str in StringList do
begin
if Frequencies.ContainsKey (Str) then
Frequencies [Str] := Frequencies [Str] + 1
else
Frequencies.Add (Str, 1);
end;
// Output results to console
for Str in Frequencies.Keys do
WriteLn (Str + ': ' + IntToStr (Frequencies [Str]));
finally
StringList.Free;
FreeAndNil(Frequencies);
end;
end.
Sort the original list,
list1.sort;
create a new list
list2:=TStringList.Create;
iterate over the sorted list to count every different item
and store the a count in the objects field of the resulting list (or if you don't use it already, just typecast the count into a pointer and store it as the object).
previtem:=list1[0];
count:=1;
for i:=1 to list1.count-1 do
begin
if list1[i]=previtem then
inc(count)
else
begin
list2.addObject(previtem,pointer(count));
previtem:=list1[i];
count:=1;
end;
end;
list2.addObject(previtem,pointer(count));
finally, iterate again to add the count to the string
for i:=0 to list2.count-1 do
list2.items[i]:=list2[i]+' ['+inttostr(list2.objects[i])+']';
I coded this on my head as I don't have Delphi installed as of now. Let me know how it works for you.
Stringlist1 is the original list with the items, stringlist2 is empty and will be used to store what you want.
for i := 0 to stringlist1.Count - 1 do
begin
if (stringlist2.Values[stringlist1[i]] = '') then
stringlist2.Values[stringlist1[i]] := '1'
else
stringlist2.Values[stringlist1[i]] :=
IntToStr(StrToInt(stringlist2.Values[stringlist1[i]]) + 1);
end;

Lazarus - parse function based on delimiter

I am building a small app in Lazarus and need a parse function based on the underscore. For example:
array := Split(string, delimiter);
So string = "this_is_the_first_post" and delimiter is the underscore resulting in the array being returned as:
array[0] = this
array[1] = is
array[2] = the
array[3] = first
array[4] = post
Any one has any idea how to go about this? I have tried a few code examples and it always throws an error.
Thanks.
You can use the following code:
var
List1: TStringList;
begin
List1 := TStringList.Create;
try
List1.Delimiter := '_';
List1.DelimitedText := 'this_is_the_first_post';
ShowMessage(List1[0]);
ShowMessage(List1[1]);
ShowMessage(List1[2]);
ShowMessage(List1[3]);
ShowMessage(List1[4]);
finally
List1.Free;
end;
end;
In this example the output will be shown as a set of messages but you get the general idea.

Resources