I'm trying to code a procedure which goes through a record of numbers and finds which one is highest, the code currently is below. The issue I have is that it just seems to list the last score on the record (not the highest). Any help is greatly appreciated.
Procedure FindTopScore(Var TopScores : TTopScores);
Var
Count : Integer;
Highest : Integer;
Name: String;
Begin
For Count := 1 to MaxSize Do
If TopScores[Count].Score > Highest Then
Highest := TopScores[Count].Score;
Name := TopScores[Count].Name;
Writeln('Higest score is by ' ,TopScores[Count].Name, ' of ', TopScores[Count].Score);
End;
You're not outputting Highest, but TopScores[Count].Score. Just use
Writeln('Highest is ', Highest, ' for ', Name);
Also you should put the name into a variable Name inside the if-statement (it actually is outside).
Addon: If you want all names in case of a tie you can use e.g. the following code
Highest := 0;
For Count := 1 to MaxSize Do Begin
If TopScores[Count].Score = Highest Then Begin
Name := Name + ' and ' + TopScores[Count].Name;
End;
If TopScores[Count].Score > Highest Then Begin
Highest := TopScores[Count].Score;
Name := TopScores[Count].Name;
End;
End;
In addition to Howard's answer, set '0' to 'Highest' before beginning the loop. Being uninitialized, it is having an arbitrary value, probably higher then the highest score.
In addition to the accepted answer, make sure you turn on your warnings and hints, and you'll see:
testhighest.pp(16,39) Warning: Local variable "Highest" does not seem to be initialized
which is the
If TopScores[Count].Score > Highest Then
line
Related
sorting system and the main problem starts from the "Until" function. I would like to hear someones opinion about what I did wrong, and if there is an easier solution, I will appreciate if u told me about it.
The idea of the problem is: you have n number of people, and u need do introduce each one from the keyboard. Then, I need to sort them alphabeticlly
uses crt;
type Data = record
day : 1..31;
month : 1..12;
year : integer;
end;
Persoana = record
Name : string;
BirthDate : Data;
end;
ListaPersoane = array [1..50] of Persoana;
var x : ListaPersoane;
n:1..50;
i,z,j,l,a,v:integer;
y, k : longint;
aux : string;
begin
writeln('Program created on: 13/10/2020;');
writeln('give the number of people (max. 50):');
readln(n);
for i:=1 to n do begin
ClrScr;
writeln('Insert the name of person ', i, ': '); readln(x[i].Name);
writeln('Insert the date o birth:'); writeln('day:'); readln(x[i].BirthDate.day);
writeln('month:'); readln(x[i].BirthDate.month);
writeln('year:'); readln(x[i].BirthDate.year);
ClrScr;
end;
writeln('_______________________');
for i:=1 to n do begin
writeln(i, ') ', x[i].Name, ' ', x[i].BirthDate.day, '/', x[i].BirthDate.month, '/', x[i].BirthDate.year, ';');
writeln('_______________________');
end;
writeln();
repeat
k:=0;
for i:=1 to n do begin
j:=1;
repeat
Inc(j);
until (x[i].Name[j]>x[i].Name[j]) or (x[i].Name[j]<x[i].Name[j]);
if(x[i].Name[j]>x[i+1].Name[j]) then begin
aux:=x[i].Name;
x[i].Name:=x[i+1].Name;
x[i+1].Name:=aux;
z:=x[i].BirthDate.day;
x[i].BirthDate.day:=x[i+1].BirthDate.day;
x[i+1].BirthDate.day:=z;
l:=x[i].BirthDate.month;
x[i].BirthDate.month:=x[i+1].BirthDate.month;
x[i+1].BirthDate.month:=l;
a:=x[i].BirthDate.year;
x[i].BirthDate.year:=x[i+1].BirthDate.year;
x[i+1].BirthDate.year:=a;
Inc(k);
end;
end;
until (k=0);
writeln('_______________________');
for i:=1 to n do begin
writeln(i, ') ', x[i].Name, ' ', x[i].BirthDate.day, '/', x[i].BirthDate.month, '/', x[i].BirthDate.year, ';');
writeln('_______________________');
end;
writeln();
end.
I would expect that PascalABC can compare two strings and return which one is "smaller" or "bigger", without looping through the characters.
But to draw your attention to (at least) three issues in your sorting code, consider this code of yours:
j := 1;
repeat
Inc(j);
until (x[i].Name[j] > x[i].Name[j]) or (x[i].Name[j] < x[i].Name[j]);
Issue 1:
You initialize j := 1 before the loop. Then before you use j to index a character, you increment it. Thus you never attempt to compare the first character.
Issue 2:
Your repeat loop doesn't take into consideration that names have a limited, and often different length.
Issue 3:
Will either of these conditions, on the until row, ever be true:
(x[i].Name[j] > x[i].Name[j])
or this:
(x[i].Name[j] < x[i].Name[j])
In the subsequent code you correctly compare a character in x[i] with x[i+1]
I leave the correction of these errors for you, yourself, to correct. Consult with your tutor if needed.
You have a repeat .. until which terminates when k=0. You start with k assigned 0, then never change k. Perhaps your repeat is terminating because you don’t change k in the loop.
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.
I have a Question about Delphi StringLists and sorting them. I am sorting a list of attributes (with duplicate entries) so I need to retain their original index before the sort. Here is a sample of what I am trying
procedure TestFind;
var
i, iIndex :integer;
slStrings : TStringlist;
begin
slStrings := TStringList.Create;
slStrings.Sorted := False;
slStrings.Add('Zebra');
slStrings.AddObject('Zebra',TObject(1));
slStrings.Add('Bat');
slStrings.AddObject('Bat',TObject(2));
slStrings.Add('Cat');
slStrings.AddObject('Cat',TObject(3));
slStrings.Add('Hat');
slStrings.AddObject('Hat',TObject(4));
slStrings.Add('Aat');
slStrings.AddObject('Aat',TObject(5));
slStrings.sorted := True;
if slStrings.Find('Zebra',iIndex) then
begin
while slStrings.Strings[iIndex] = slStrings.Strings[iIndex + 1] do
begin
i := ObjectToInt(slStrings.Objects[iIndex]) ;
AddMemoData ('Stringlist Found at Position: ' + inttoStr(i) + ' Current index position is: ' + inttoStr(iIndex), false);
inc(iIndex);
end;
i := ObjectToInt(slStrings.Objects[iIndex]) ;
AddMemoData ('Stringlist Found at Position: ' + inttoStr(i) + ' Current index position is: ' + inttoStr(iIndex), false);
end;
end;
When I run this I get a Value of 0,8 for Zebra, this makes no sense to me, I would expect a message of 1,4
I really can't work out what your code is trying to achieve, but it is accessing beyond the end of the list. To avoid that your while test can be modified like so:
while (iIndex<slStrings.Count-1)
and (slStrings.Strings[iIndex] = slStrings.Strings[iIndex + 1]) do
Your use of Objects[] will work. Values placed there are kept with their matching Strings[] values when the list is sorted.
However, if I were you I would not use a string list for this task. I would declare a record like this:
TMyRec = record
Name: string;
Index: Integer;
end;
I would hold them in a TList<TMyRec> and then sort them using a custom comparer.
I note that you add each object twice, once with an associated index, and once without. Those latter instances will get a default index value of 0. I also observe that the code you present will not execute because of the out of bounds error that I identified. Further, even when you fix that it does not give output of the form that you claim.
In other words, it appears that the code you posted is very different from the code that you are running. I've answered based on the code that you included in the question. I hope that you can accept an answer on that basis and don't expect help with the code that you have, that we cannot see. Perhaps I should just have voted to close.
Anyway, perhaps the main problem is here:
slStrings.Add('Zebra');
slStrings.AddObject('Zebra',TObject(1));
slStrings.Add('Bat');
slStrings.AddObject('Bat',TObject(2));
slStrings.Add('Cat');
slStrings.AddObject('Cat',TObject(3));
slStrings.Add('Hat');
slStrings.AddObject('Hat',TObject(4));
slStrings.Add('Aat');
slStrings.AddObject('Aat',TObject(5));
This is equivalent to:
slStrings.AddObject('Zebra',TObject(0));
slStrings.AddObject('Zebra',TObject(1));
slStrings.AddObject('Bat',TObject(0));
slStrings.AddObject('Bat',TObject(2));
slStrings.AddObject('Cat',TObject(0));
slStrings.AddObject('Cat',TObject(3));
slStrings.AddObject('Hat',TObject(0));
slStrings.AddObject('Hat',TObject(4));
slStrings.AddObject('Aat',TObject(0));
slStrings.AddObject('Aat',TObject(5));
Did you actually mean to write this:
slStrings.AddObject('Zebra',TObject(1));
slStrings.AddObject('Bat',TObject(2));
slStrings.AddObject('Cat',TObject(3));
slStrings.AddObject('Hat',TObject(4));
slStrings.AddObject('Aat',TObject(5));
the Solution was this:
procedure TestFind;
var
i, iIndex, iStringSize :integer;
slStrings : TStringlist;
begin
slStrings := TStringList.Create;
slStrings.Sorted := False;
slStrings.AddObject('Zebra',TObject(1));
slStrings.AddObject('Bat',TObject(2));
slStrings.AddObject('Cat',TObject(3));
slStrings.AddObject('Hat',TObject(4));
slStrings.AddObject('Zebra',TObject(6));
slStrings.AddObject('Aat',TObject(5));
slStrings.AddObject('Zebra',TObject(7));
slStrings.sorted := True;
if slStrings.Find('Bat',iIndex) then
begin
//find lowest position of string matching found string
while iIndex > 0 do
begin
if (g_slVials.Strings[iIndex] = g_slVials.Strings[iIndex-1]) then
iIndex := iIndex - 1
else
break;
end;
iStringSize := slStrings.Count;
while iIndex < iStringSize -1 do //check for more matching strings in higher range
begin
if (g_slVials.Strings[iIndex] = g_slVials.Strings[iIndex+1]) then
begin
i := ObjectToInt(slStrings.Objects[iIndex]) ;
AddMemoData ('Stringlist Found at Position: ' + inttoStr(i) + ' Current index position is: ' + inttoStr(iIndex), false);
inc(iIndex);
end else
break;
end;
i := ObjectToInt(slStrings.Objects[iIndex]) ;
AddMemoData ('Stringlist Found at Position: ' + inttoStr(i) + ' Current index position is: ' + inttoStr(iIndex), false);
end;
end;
this allows me to find all matching strings and return their index position
I want to develop a setup package for conditionally upgrading an existing package. I want to check the existing software version against to-be-installed version. In order to do that, I have to compare the version strings.
How can I convert the string value to a numerical value in a Inno setup script?
RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Blah blah', 'Version', version)
version = 'V1.R2.12';
numVersion := ??string_to_numerical_value??(version);
This is a little more tricky, as you would want to handle versions like 'V1.R2.12' and 'V0.R15.42' correctly - with the simple conversion in the other answer you would get 1212 and 1542, which would not compare the way you would expect.
You need to decide how big each part of the version number can be, and multiply the parts by that value to get a correct end number. Something like this:
[Code]
function string_to_numerical_value(AString: string; AMaxVersion: LongWord): LongWord;
var
InsidePart: boolean;
NewPart: LongWord;
CharIndex: integer;
c: char;
begin
Result := 0;
InsidePart := FALSE;
// this assumes decimal version numbers !!!
for CharIndex := 1 to Length(AString) do begin
c := AString[CharIndex];
if (c >= '0') and (c <= '9') then begin
// new digit found
if not InsidePart then begin
Result := Result * AMaxVersion + NewPart;
NewPart := 0;
InsidePart := TRUE;
end;
NewPart := NewPart * 10 + Ord(c) - Ord('0');
end else
InsidePart := FALSE;
end;
// if last char was a digit the last part hasn't been added yet
if InsidePart then
Result := Result * AMaxVersion + NewPart;
end;
You can test this with the following code:
function InitializeSetup(): Boolean;
begin
if string_to_numerical_value('V1.R2.12', 1) < string_to_numerical_value('V0.R15.42', 1) then
MsgBox('Version ''V1.R2.12'' is not as recent as version ''V0.R15.42'' (false)', mbConfirmation, MB_OK);
if string_to_numerical_value('V1.R2.12', 100) > string_to_numerical_value('V0.R15.42', 100) then
MsgBox('Version ''V1.R2.12'' is more recent than version ''V0.R15.42'' (true)', mbConfirmation, MB_OK);
Result := FALSE;
end;
Whether you pass 10, 100 or 1000 for AMaxVersion depends on the number and range of your version number parts. Note that you must not overflow the LongWord result variable, which has a maximum value of 2^32 - 1.
I haven't tried that (and my Pascal knowledge is a bit rusty), but something like the following should work:
function NumericVersion(s: String): Integer;
var
i: Integer;
s1: String;
begin
s1 := '';
for i := 0 to Length(s)-1 do
if (s[i] >= '0') and (s[i] <= '9') then
s1 := s1 + s[i];
Result := StrToIntDef(s1, 0);
end;
Please not that you'll have to play with the start and end value for i as I'm not sure whether it is zero-based or not (s[0] may contain the length of the string if it is a "Pascal String").
I've implemented two version strings (actually one string and one dword value) in the registry to overcome complexity.
displayversion="v1.r1.0"
version="10100" (=1*10^4 + 1*10^2 + 0*10^0)
That's simple. Though not an answer to this question, however one might think the other way around when faced with complexity, which could be avoided in a simpler way.
I have to write a program in Pascal which has to detect how many words on a text (input by the user) start with a certain letter. I can't use arrays, can you give me any hints as to where to start?
If you know which letter, you merely need to keep a counter, no need for arrays.
If you don't know which letter, keep 26 counters. Stupid, but works as per your spec.
First thing to do is define the set of characters that constitute letters, or conversely which ones constitute non-letters.
Write a function that takes a character and returns a boolean based on whether that character is a letter. Then loop through the string and call it for each character. When you detect a letter right after a non-letter or at the start of the string, increment your counter if it is the target letter.
count instances of SPACE LETTER plus first word if it matches.
(S) is your input string;
Create a for loop that goes from 1 to the length of (S) - 1.
Inside loop, check is (S)[i] = ' ' and (S)[i+1] = 't' where i is the loop counter and 't' is the letter starting the word you want to count
If criteria in step two matches then increment a counter.
Note the minus one on the loop size.
Also, remember that the very first letter of the string may be the one you want to match and that will not get picked up by the loop defined above.
If you need to make your code smarter in that it can locate a specific letter rather than a hardcoded 't' then you can pass the requested character as a parameter to the function/procedure that your loop is in.
Off the top of my head - not tested
function WordCount(const S: string; const C: Char): Integer;
const
ValidChars: Set of Char [A..Z, a..z]; // Alter for appropriate language
var
i : Integer;
t : string;
begin
Result := 0;
if Length(S) <> 0 then
begin
t := Trim(S); // lose and leading and trailing spaces
t := t + ' '; // make sure a space is the last char
repeat
if (t[1] in ValidChars) and (t[1] = C then
inc(Result);
i := Pos(' ', t);
t := Copy(t(i+1, Length(t));
until Length(t) = 0;
end;
end;
Why would you need an array or a case statement?