I have a problem with my very simple Pascal code. (I just started to learn Pascal.)
So it's about an age comparison code then rest can be seen through the code.
program Test;
uses crt;
var
age : real;
Begin
writeln('Enter your age: ');
readln(age);
if age>18 then
if age<100 then
Begin
clrscr;
textcolor(lightgreen);
writeln('Access Granted');
end
else
if age<18 then
Begin
clrscr;
textcolor(lightred);
writeln('ACCESS DENIED');
writeln('Reason:You are way to young');
end
else
Begin
clrscr;
textcolor(lightred);
writeln('ACCESS DENIED');
writeln('Reason:You are way to old');
end;
readln;
end.
When I enter a value below 18 as the age, I expect the program to respond:
ACCESS DENIED
Reason:You are way to young
but I don't get any output. Why?
Sometimes text indentation helps you to see the issue. Here's your code with indentation added:
program Test;
uses crt;
var
age : real;
Begin
writeln('Enter your age: ');
readln(age);
if age>18 then
if age<100 then
Begin
clrscr;
textcolor(lightgreen);
writeln('Access Granted');
end
else
if age<18 then
Begin
clrscr;
textcolor(lightred);
writeln('ACCESS DENIED');
writeln('Reason:You are way to young');
end
else
Begin
clrscr;
textcolor(lightred);
writeln('ACCESS DENIED');
writeln('Reason:You are way to old');
end;
readln;
end.
And to make the implemented logic more obvious, I will now represent the nested ifs without the code that they execute:
if age>18 then
if age<100 then
... // Access Granted
else
if age<18 then
... // You are way too young
else
... // You are way too old
;
It is easy to see now that the branch marked as You are way too young is never reached. It is supposed to be executed when age is less than 18, but that if statement is nested into another if which will call it only when age is greater than 18. So, age should first qualify as greater than 18, then less than 18 in order for that branch to execute – you can see now why you do not get the expected result!
The intended logic could possibly be implemented this way:
if age>18 then
if age<100 then
... // Access Granted
else // i.e. "if age >= 100"
... // You are way too old
else // now this "else" belongs to the first "if"
... // You are way too young
;
I believe you should be able to fill in the missing code blocks correctly.
Just one last note: you might want to change age>18 to age>=18, so that 18 proper does not qualify as "too young".
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 currently have set of words inputted inside my MemoBox.
Question is how do I check the specific letters on each lines of the TMemobox Lines property. If letter is not similar on the word being specified then it should be wrong and not be counted in and if answer correct how to let the word appear on the wordlabel?
Also how to limit the guess-label property into only inputting 5 letters?
Currently making a hang-man game :)
Set of Words here
I currently have this code:
{***************** CheckALetter ***************}
procedure TForm1.CheckLetter(ch:char);
var
i:integer;
s:string;
goodguess:boolean;
begin
goodguess:=false;
if not (ch in GuessedLetters)
then
begin
GuessedLetters:=GuessedLetters+[ch];
guessesLabel.caption:=guesseslabel.caption+ch+',';
s:=Wordlabel.caption;
for i:=1 to length(TheWord) do {see if the letter is in the word}
begin
if ch=Theword[i] then
begin
s[2*i-1]:=ch; {fill in the letter in display}
goodguess:=true;
end;
end;
wordlabel.caption:=s;
{if not goodguess then drawAPiece(clred);}
If pos('_',WordLabel.caption)=0 then {all underscores replaced by letters}
showmessage('A reprieve!')
else {If piececount=Hangmanlist.count}
begin
showmessage('Oh, oh Goodbye!'+#13 +'(The word was '+theword+')');
{deadlbl.visible:=true;}
end;
end
else messagebeep(mb_IconExclamation);
end;
//On key presss
procedure TForm1.guess_typeKeyPress(Sender: TObject; var Key: Char);
begin
key:=upcase(key);
guess_type.text:='';
If not (key in ['A'..'Z']) then
begin
key:=#00;
messagebeep(mb_iconexclamation);
end
else CheckLetter(key);
end;
end.
not sure where the TMemo fits in. The TMemo.Lines is just a normal TStringList, you can make a fo
for i:=1 to MyMemo.Lines.Count do
s := MyMemo.Lines[i-1];
// Do somenthing with s..
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've been making a stock-keeping program for my school (In Virtual Pascal) and as part of that, I want to be able to display today's date along with the current time at the main menu of the program. Now, I've been able to display the correct date as that was pretty simple.
But when I use this code for displaying Time, it only displays the time at which the program is compiled, and does not refresh to display the current time.
Procedure getTheTime;
VAR
Hour, Minute, Second, Sec100 : WORD;
BEGIN
GetTime( Hour, Minute, Second, Sec100 );
TEXTCOLOR(lightgreen);
whereY;
whereX;
WRITE;WRITE(' ');
WRITE( Hour, ':', Minute, ':', Second, '.', Sec100);
END;
Basically, it displays this: 19:8:41.75
And I want the time to refresh as time goes by.
Any help would be appreciated.
The procedure where I call the getTheTime procedure.
Procedure mainMenu;
BEGIN
REPEAT
CLRSCR;
getTheDate;
getTheTime;
TEXTCOLOR(15);
GOTOXY(18,2);
WRITELN('Welcome To RAK Academy''s School Shop');
TEXTCOLOR(11);
GOTOXY(18,3);
WRITELN('------------------------------------');
WRITELN;
WRITE('A ':25);
TEXTCOLOR(15);
WRITELN(': Customers');
WRITELN;
TEXTCOLOR(11);
WRITE('B ':25);
TEXTCOLOR(15);
WRITELN(': Products');
WRITELN;
TEXTCOLOR(11);
WRITE('C ':25);
TEXTCOLOR(15);
WRITELN(': Orders');
WRITELN;
TEXTCOLOR(11);
WRITE('X ':25);
TEXTCOLOR(15);
WRITELN(': Exit');
WRITELN;
GOTOXY(0,3);
WRITE('Enter Choice: ':23);
MenuChoice:=UPCASE(READKEY);
sndPlaySound('F:\School\IB 1\HL subjects\Computer Science\Pascal programs\InternalAssessment\sound files\beep.wav', snd_Async or snd_NoDefault );
TEXTCOLOR(11);;
WRITELN(MenuChoice);
TEXTCOLOR(15);
DELAY(200);
CASE MenuChoice OF
'A' : CustomersMenu;
'B' : ProductsMenu;
'C' : OrdersMenu;
'X' : BEGIN
sndPlaySound('F:\School\IB 1\HL subjects\Computer Science\Pascal programs\InternalAssessment\sound files\end.wav', snd_Async or snd_NoDefault );
WRITELN;
WRITELN('Program Is Shutting Down');
GOTOXY(25,15);
DELAY(750);
WRITE('.');
DELAY(750);
WRITE('.');
DELAY(750);
WRITE('.');
DELAY(750);
END
ELSE
BEGIN
WRITELN;
TEXTCOLOR(12);
WRITELN('ERROR - Only Enter A-B Or X To Exit. Please Try Again.');
TEXTCOLOR(11);
READKEY;
END;
END;
UNTIL menuChoice='X';
END;
It is not the time at which the program is compiled, but the time at which the program is run.
The code between BEGIN-END runs once and then updates the time.
If you want the time to update you need to add a timer that updates the time when the OnTimer event triggers.
From memory, to give you an idea how to do this:
var updatecounter : integer;
mustexit : Boolean;
updatecounter:=10; mustexit:=false;
repeat //eventloop start.
if keypressed then
begin
c:=readkey;
if c=#0 then
c2:=readkey; // C=#0 is functionkey, read second value
mustexit:=processkey(c,c2); // process whatever key is pressed
end
else
begin
sleep(10); // windows/winprocs unit? Don't know VP that well.
// pauses 10ms
dec(updatecounter);
if updatecounter=0 then // every 10*10ms update time
begin
updatetime; // update the time.
updatecounter:=10;
end;
end;
until mustexit;
You can implement the various procedures and play with constants to get the responsivenes you want.
I am trying out following code. However, if I click on form's close button while this code is running, nothing happens. How can I correct this? I need to close the form even when this loop is executing.
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i := 0 to 9999999 do
begin
Memo1.Lines.Add('hi');
Application.ProcessMessages;
end;
end;
Have a look at what's going on inside Application.ProcessMessages.
When you close the main form, windows sends a WM_QUIT message to the program. The relevant part of TApplication.ProcessMessages looks like this:
if Msg.Message <> WM_QUIT then
begin
//skipped
end
else
begin
{$IF DEFINED(CLR)}
if Assigned(FOnShutDown) then FOnShutDown(self);
DoneApplication;
{$IFEND}
FTerminate := True;
end;
I assume this is not a CLR program, so the only thing that happens at this point is setting FTerminate := True on Application. This is reflected in the Application.Terminated property.
When the application shuts down, one of the things it does in order to shut down safely is wait for all threads to finish. This code happens to be running in the main thread, but the principle would be the same in any thread: If you're doing a long-running task that might have to finish early, you have to explicitly check for early termination.
Knowing this, it's easy to figure out how to fix your code:
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i := 0 to 9999999 do
begin
Memo1.Lines.Add('hi');
Application.ProcessMessages;
if Application.Terminated then
Break;
end;
end;
Also, beware of using Application.ProcessMessages in the first place, as it will process all messages for the application. For a simple idea of what might go wrong, try adding IntToStr(i) instead of 'hi' to Memo1.Lines, knock a couple of orders of magnitude off the counter, and then click the button two or three times in rapid succession and watch the output...
Check for Apllication Terminated:
for i := 0 to 9999999 do
begin
Memo1.Lines.Add('hi');
Application.ProcessMessages;
if Application.Terminated then Exit;
end;
You need to run any tight loop in a thread. This will solve the problem.
BUT if you want to keep the code as it is, Application.ProcessMessages will make your loop terribly slow. So you need to run Application.ProcessMessages not so often:
Counter:= 0;
for i := 0 to 9999999 do
begin
DoSomeStuff;
{ Prevent freeze }
inc(Counter);
if counter > 10000 then
begin
Counter:= 0;
Application.ProcessMessages;
if Application.Terminated then Exit;
end;
end;