Movatterモバイル変換


[0]ホーム

URL:


Jump to content
WikibooksThe Free Textbook Project
Search

Pascal Programming/Records

From Wikibooks, open books for an open world
<Pascal Programming

The key to successful programming is finding the "right" structure of data and program.

—Niklaus Wirth[1]

After you have learned to use anarray, this chapter introduces you to another data typestructure concept calledrecord.Like anarray, the use ofrecords primarily serves the purposes of allowing you to write clean,structured programs.It is otherwise optional.

Concept

[edit |edit source]

You briefly saw arecord in thefirst chapter.While anarray is ahomogenous aggregation of data, that means all members have to have thesame base data type, arecord is potentially, but not necessarily an aggregation of data having variousdifferent data types.[2]

Declaration

[edit |edit source]

Arecord data type declaration looks pretty much like acollection of variable declarations:

programrecordDemo;type(* a standard line on a text console *)line=string(80);(* 1st grade through 12th grade *)grade=1..12;(* encapsulate all administrative data in one structure *)student=recordfirstname:line;lastname:line;level:grade;end;

The declaration begins with the wordrecord and ends withend.Inbetween you declare fields, ormembers, member elements of the entirerecord.

Here again the semicolon has the function ofseparating members. The keywordend will actuallyterminate arecord declaration. Note, how in the followingcorrect example there is no semicolon after thelast member’s declaration:
programrecordSemicolonDemo;typesphere=recordradius:real;volume:real;surface:realend;
Despite that, it is a frequent practice to put a semicolon there anyway, even though it is not necessary. You would otherwise too often simplyadd a new member below the last line,forgetting to add the semicolon in the preceding line and thus provoking a syntax error.

Allrecord members have to bear distinct nameswithin therecord declaration itself.For instance in the example above, declaringtwo “variables”, member elements of the namelevel will be rejected.

There is no requirement on how many fields you have to declare.An “empty”record is also possible:[fn 1]

typeemptyRecord=recordend;

Many fields of the same data type

[edit |edit source]

Similar to thedeclaration of variables you can define multiple fields of thesame data type at once by separating identifiers with a comma.The previous declaration ofsphere could also be written as:

typesphere=recordradius,volume,surface:real;end;

Most Pascal veterans and style guides, however,discourage the use of this shorthand notation (both for variable as well asrecord declarations, but also in formal parameter lists).Itis only reasonable if all declared identifiersabsolutely always have same data type;it is virtually guaranteed you will never want to change the data type ofjust one field in the comma-separated list.If in doubt, use the longhand.In programming, convenience plays a tangential role.

Use

[edit |edit source]

By declaring arecord variable you immediately have theentire set of “sub”‑variables at your hand.Accessing them is done by specifying therecord variable’s name, plus a dot (.), followed by therecordfield’s name:

varposterStudent:student;beginposterStudent.firstname:='Holden';posterStudent.lastname:='Caulfield';posterStudent.level:=10;end.

You already saw the dot notation in the previous chapter onstrings, where appending.capacity on a name of astring() variable refers to the respective variable’s character capacity.This is not a coincidence.

However, especially beginners occasionally confuse thedata type name with thevariable’s name. The following code highlights the difference. Remember that adata typedeclaration does not reserve any memory and is mainly informative for the compiler, whereas avariable declaration actually sets some chunk of memory aside.
programdotNoGo(output);{ This program does not compile. }typeline=string(80);quizItem=recordquestion:line;answer:line;end;varresponse:line;challenge:quizItem;beginwriteLn(line.capacity);{ ↯ `line` is not a variable }writeLn(response.capacity);{ ✔ correct }writeLn(quizItem.question);{ ↯ `quizItem` refers to a data type }{ Data type declarations (as per definition) do not reserve any memory }{ thus you cannot “read/write” from/to a data type. }writeLn(challenge.question);{ ✔ correct }end.
And, as it has always been, you first need to assign a value to a variable before you are allowed to read it. The source code above ignores that to focus on the main issue. The key point is, the dot (.) notation is only validif there is memory.[fn 2]


Advantages

[edit |edit source]

But why and when do wewant to use arecord?At first glance and in the given examples so far it may seem like a troublesome way to declare and usemultiple variables.Yet the fact that arecord is handled asone unit entails one big advantage:

Evidently you want togroup data together that always appeartogether.It doesnot make sense to groupunrelated data, just because we can.Another quite useful advantage is presented below in the section onvariant records.

Routing override

[edit |edit source]

As you saw earlier, referring to members of arecord can get a little tedious, because we are repeating the variable name over and over again.Fortunately, Pascal allows us abbreviate things a bit.

With-clause

[edit |edit source]

Thewith-clause allows us to eliminate repeating a common prefix, specifically the name of arecord variable.[3]

beginwithposterStudentdobeginfirstname:='Holden';lastname:='Caulfield';level:=10;end;end.

All identifiers that identify values are first looked for in therecord scope ofposterStudent.If there is no match, all variable identifiers outside of the givenrecord are considered too.

Of course it is still possible to denote arecord member by itsfull name.E. g. in the source code above it would be perfectly legal to still writeposterStudent.levelwithin thewith-clause.Concededly, this would defeat the purpose of thewith-clause, but sometimes it may still be beneficial to emphasize the specificrecord variable just for documentation.It is nevertheless important to understand that the FQI, the fully-qualified identifier, the one with a dot in it, doesnot lose its “validity”.

In principle, all components of structured values “containing dots” can be abbreviated withwith.This is also true for thedata typestring you have learned in the previous chapter.

programwithDemo(input,output);type{ Deutsche Post „Maxi-Telegramm“ }telegram=string(480);varpost:telegram;beginwithpostdobeginwriteLn('Enter your telegram. ','Maximum length = ',capacity,' characters.');readLn(post);{ … }end;end.

Here, within thewith-clausecapacity, and for that matterpost.capacity, refer topost.capacity.

Multiple levels

[edit |edit source]

If multiplewith-clauses ought to be nested, there is the short notation:

withsnakeOil,sharpToolsdobeginend;

which is equivalent to:

withsnakeOildobeginwithsharpToolsdobeginend;end;

It is important to bear in mind, first identifiers insharpTools are searched, and if there is no match,secondly, identifiers insnakeOil are considered.

Variant records

[edit |edit source]

In Pascal arecord is the only data type structure concept that allows you to, so to speak, alter its structure during run-time,while aprogram is running.This super practical property ofrecord permits us to write versatile code covering many cases.

Declaration

[edit |edit source]

Let’s take a look at an example:

typecentimeter=10..199;// order of female, male has been chosen, so `ord(sex)`// returns the [minimum] number of non-defective Y chromosomessex=(female,male)// measurements according EN 13402 size designation of clothes [incomplete]clothingSize=recordshoulderWidth:centimeter;armLength:centimeter;bustGirth:centimeter;waistSize:centimeter;hipMeasurement:centimeter;casebody:sexoffemale:(underbustMeasure:centimeter;);male:();end;

Thevariant part of arecord starts with the keywordcase, which you already know fromselections.After that follows arecord member declaration, thevariant selector, but instead of a semicolon you put the keywordof thereafter.Below that follow all possible variants.Each variant is marked by a value out of the variant selector’s domain, herefemale andmale.Separated by a colon (:) follows avariant denoter surrounded by parentheses.Here you can list additionalrecord members that are only available if a certain variant is “active”.Note thatall identifiers acrossall alternatives must be unique.The individual variants are separated by a semicolons, and there can beat most one variant part which has to appearat the end.Because you will need to be able to list all possible variants, the variant selector has to be an ordinal data type.

Use

[edit |edit source]

Using variant records requires you to firstselect a variant.Variants are “activated” by assigning a value to the variant selector.Note, variants are not “created”; they all already exist atprogram startup.You merely need to make a choice.

boobarella.body:=female;boobarella.underbustMeasure:=69;

Onlyafter assigning a value to the variant selector andas long as this value remainsunchanged, you are allowed to access any fields of the respective variant.It is illegal to reverse the previous two lines of code and attempt accessing theunderbustMeasure field even thoughbody is not defined yet and, more importantly, does not bear the valuefemale.

It is certainly permissible to change the variant selector later in yourprogram and then use a different variant, but all previously stored values in the variant partrelinquish their validity and you cannot restore them.If you switch back the variant to a previous, original value, you will need to assign all values in that variant anew.

Application

[edit |edit source]

This concept opens up new horizons:You can design your programs more interactively in a neat fashion.You can now choose a variant based on run-time data (data that is readwhile theprogram is running).Because at any time (after the first assignment of a value to the variant selector) onlyone variant is “active”, yourprogram will crash if it attempts reading/writing values of an “inactive” variant.This is adesirable behavior, becausethat is the whole idea of having distinct variants.It guarantees your programs overall integrity.

Anonymous variants

[edit |edit source]

Pascal also permits having anonymous variant selectors, that is selectors not bearing any name.The implications are

  • you cannot explicitly select (nor query) any variant, so
  • in turnall variants are considered “active” at the same time.

“But wasn’t this the object of the exercise?” you might ask.Yes, indeed, since there is no named selector yourprogram cannot keep track which variant is supposed to work and which one is “defective”.You are responsible to determine which variant you can sensibly read/write at present.

See also the chapterType conversion inComputer Programming.
Anonymous variants are/were frequentlyabused to implement “typecasts”. If you have an anonymous variant part, you can declare members bearingdifferent data types which in turn determine the underlying data’s interpretation method. You can then exploit the fact that many (but not necessarily all) compilers putall variants in thesame memory block.

Code:

programanonymousVariantsDemo(output);typebitIndex=0..(sizeOf(integer)*8-1);exposedInteger=recordcaseBooleanoffalse:(value:integer;);true:(bit:setofbitIndex;);end;vari:exposedInteger;begini.bit:=[4];writeLn(i.value);end.

Output:

16
The value16 is (and this should be considered “a coincidence”)24{\displaystyle 2^{4}}. Westress that all Pascal standards do not make any statement regarding internal memory structure. A high-level programming language is not concerned about how data is stored, it even does not know the notion of “bits”, “voltage high”/“voltage low”.
WarningThus,if you are (intentionally) using any of this demonstrated behavior, you cannot say “I am programming in Pascal” anymore, but you are programmingspecifically for the compiler so-and-so. The memory layout of data structuresvaries among Pascal implementations.
The example above, for example, was designed for and works with theGPC and theFPC in their default configurations. Do not deem it as “Pascal”, but a descendant of it. There is a good chance that using a different compiler will produce different results.

This concept exists in many other programming languages too.In the programming language C, for instance, it is called aunion.

Conditional loops

[edit |edit source]

So far we have been exclusively using countingloops.This is great if you can predict in advance the number of iterations, how many times the loop’s body needs to be executed.Yet every so often it is not possible to formulate a proper expression determining the number of iterations in advance.

Conditional loops allow you to make the execution of thenext iteration dependent on aBoolean expression.They come in two flavors:

  • Head-controlled loop, and
  • tail-controlled loop.

The difference is, the loop’s body of a tail-controlled loop is executedat least once in any case, whereas a head-controlled loop might never execute the loop body at all.In either case, a condition is evaluated over and over again and mustuphold for the loop to continue.

Head-controlled loop

[edit |edit source]

A head-controlled loop is frequently calledwhile-loop because of its syntax.

The “control” condition appearsabove the loop body, i. e.at the head.

Code:

programcharacterCount(input,output);typeintegerNonNegative=0..maxInt;varc:char;n:integerNonNegative;beginn:=0;whilenotEOFdobeginread(c);n:=n+1;end;writeLn('There are ',n:1,' characters.');end.

Output:

$ cat ./characterCount.pas | ./characterCountThere are 240 characters.$ printf '' '' | ./characterCountThere are 0 characters.
The loop’s condition is aBoolean expression framed by the wordswhile anddo. The condition must evaluate totrue for any (subsequent) iteration to occur.As you can see from the output, in the second case, it may even bezero times: Evidently forempty inputn:=n+1 wasnever executed.

EOF is shorthand forEOF(input).This standardfunction returnstrue if there is no further data available to read, commonly calledend of file.It is illegal, and will horribly fail, toread from a file if the respectiveEOF function call returnstrue.

Unlike a counting loop, youare allowed tomodify data the conditional loop’s condition depends on.

const(* instead of a hard-coded length `64` *)(* you can write `sizeOf(integer) * 8` in Delphi, FPC, GPC *)wordWidth=64;typeintegerNonNegative=0..maxInt;wordStringIndex=1..wordWidth;wordString=array[wordStringIndex]ofchar;functionbinaryString(n:integerNonNegative):wordString;var(* temporary result *)binary:wordString;i:wordStringIndex;begin(* initialize `binary` with blanks *)fori:=1towordWidthdobeginbinary[i]:=' ';end;(* if n _is_ zero, the loop's body won't be executed *)binary[i]:='0';(* reverse Horner's scheme *)whilen>=1dobeginbinary[i]:=chr(ord('0')+nmod2);n:=ndiv2;i:=i-1;end;binaryString:=binary;end;

Then the loop’s condition depends on will be repeatedly divided by two.Because the division operator is an integer division (div), at some point the value1 will be divided by two and the arithmetically correct result0.5 is truncated (trunc) toward zero.Yet the value0 does not satisfy the loop’s condition anymore, thus there will not beany subsequent iterations.

Tail-controlled loop

[edit |edit source]

In a tail-controlled loop the condition appearsbelow the loop’s body, at the foot.The loop’s body is always run once before even the condition is evaluated at all.

programrepeatDemo(input,output);vari:integer;beginrepeatbeginwrite('Enter a positive number: ');readLn(i);enduntili>0;writeLn('Wow! ',i:1,' is a quite positive number.');end.

The loop’s body is encapsulated by the keywordsrepeat anduntil.[fn 4]Afteruntil follows aBoolean expression.In contrast to awhile loop, the tail-controlled loop always continues, always keeps going,until the specified condition becomestrue.A true condition marksthe end.In the above example the user will be prompted again and again until he eventually complies and enters a positive number.

Date and time

[edit |edit source]

This section introduces you to features of Extended Pascal as defined in theISO standard 10206.You will need anEP‑compliant compiler to use those features.

Time stamp

[edit |edit source]

InEP there is astandard data type calledtimeStamp.It is declared as follows:[fn 5]

typetimeStamp=recorddateValid:Boolean;timeValid:Boolean;year:integer;month:1..12;day:1..31;hour:0..23;minute:0..59;second:0..59;end;

As you can see from the declaration,timeStamp also contains data fields for a calendar date, not just the time as indicated by a standard clock.

NoteA processor (i. e. usually a compiler) may provideadditional (thus non-standard) fields. TheGPC for instance supplies, among other fields, a field calledtimeZone indicating the offset in seconds versusUTC (“world time”).

Getting a time stamp

[edit |edit source]

EP also defines a unaryprocedure that populates atimeStamp variable with values.GetTimeStamp assigns values to all members of atimeStamprecord passed in the first (and only) parameter.These values represent the “current date” and “current time” as at the invocation of this procedure.However, in the 1980’s not all (personal/home) computers did have a built-in “real time” clock.Therefore, theISO standard 10206 devised prior 21st century stated that the word “current” was “implementation-defined”.ThedateValid andtimeValid fields were specifically inserted to address the issue that some computers simply do not know the current date and/or time.When reading values from atimeStamp variable, it is still advisable to check their validity first after havinggetTimeStamp fill them out.

IfgetTimeStamp wasunable to obtain a “valid” value, it will set

Both are independent from each other, so it may certainly be the case that just the time could be determined, but the date is invalid.

Note that the Gregorian calendar was introduced during the year 1582 CE, so thetimeStamp data type is generally useless for any dates before 1583 CE.

Printable dates and times

[edit |edit source]

Having obtained atimeStamp,EP furthermore supplies two unary functions:

Both functions will fail and terminate theprogram ifdateValid ortimeValid indicate an invalid datum respectively.Note, the exact format ofstring representation is not defined by theISO standard 10206.

Putting things together, consider the followingprogram:

Code:

programdateAndTimeFun(output);varts:timeStamp;begingetTimeStamp(ts);ifts.dateValidthenbeginwriteLn('Today is ',date(ts),'.');end;ifts.timeValidthenbeginwriteLn('Now it is ',time(ts),'.');end;end.

Output:

Today is 11 Nov 2025.Now it is 08:06:42.
The output may differ. Here, theGPC was used and the hardware had anRTC. It is needless to say, but also you might seenothing if bothdateValid andtimeValid arefalse.

Summary on loops

[edit |edit source]

This is a good time to take inventory and reiterate all kinds of loops.

Conditional loops

[edit |edit source]

Conditional loops are the tools of choice if you cannot predict the total number of iterations.

head-controlled looptail-controlled loop
whileconditiondobeginend; 
repeatbeginenduntilcondition;
condition must evaluate totrue for any (including subsequent) iterations to occur.condition must befalse for anysubsequent iteration to occur.
comparison of conditional loops in Pascal

Itis possible to formulate either loop as the other one, but usually one of them is more suitable.A tail-controlled loop is particularly suitable if you do not have any data yet to make a judgment, to evaluate a propercondition prior the first iteration.

Counting loops

[edit |edit source]

Counting loops are good if youcan predict the total number of iterations before entering the loop.

counting up loopcounting down loop
forcontrolVariable:=initialValuetofinalValuedobeginend;
forcontrolVariable:=initialValuedowntofinalValuedobeginend;
After each non-final iterationcontrolVariable becomessucc(controlVariable).controlVariable must be less than or equal tofinalValue for another iteration to occur.After each non-final iterationcontrolVariable becomespred(controlVariable).controlVariable must begreater than or equal tofinalValue for another iteration to occur.
comparison of counting loop directions in Pascal
NoteBoth, theinitialValue andfinalValue expressions, are evaluated exactlyonce.[4] This is very different to conditional loops.

Inside counting loops’ bodies you cannot modify the counting variable, only read it.This prevents you from any accidental manipulations and ensures the calculated predicted total number of iterations will indeed occur.

NoteIt isnot guaranteed thatcontrolVariable isfinalValue “after” the loop. If there were exactlyzero iterations,no assignments tocontrolVariable were made. Thusgenerally presumecontrolVariable is invalid/uninitialized after afor-loop unless you are absolutely sure there was at least one iteration.

Loops on aggregations

[edit |edit source]

If you are using anEP-compliant compiler, you furthermore have the option to use aforin loop onsets.

programforInDemo(output);typecharacters=setofchar;varc:char;parties:characters;beginparties:=['R','D'];forcinpartiesdobeginwrite(c:2);end;writeLn;end.

Tasks

[edit |edit source]

You have made it this far, and it is quite impressive how much you already know.Since this chapter’s concept of arecord should not be too difficult to grasp, the following exercises mainly focus on training.A professional computer programmer spends most of his time onthinking what kind of implementation, usingwhich tools (e. g.array “vs.”set), is the most useful/reasonable.You are encouraged to think first, before you even start typing anything.Nonetheless, sometimes (esp. due to your lack of experience) you need to just try things out, which is fine if it isintentional.Aimlessly finding a solution does not discern an actual programmer.

Can you have arecord contain anotherrecord?
Like it was possible for anarray to containanotherarray, this is quite possible for arecord too. Write a testprogram to see for yourself. The important thing is to note that the dot-notation can be expanded indefinitely (myRecordVariable.topRecordFieldName.nestedRecordFieldName.doubleNestedRecordFieldName). Evidently at some point it becomes too difficult to read so use this wisely.
Like it was possible for anarray to containanotherarray, this is quite possible for arecord too. Write a testprogram to see for yourself. The important thing is to note that the dot-notation can be expanded indefinitely (myRecordVariable.topRecordFieldName.nestedRecordFieldName.doubleNestedRecordFieldName). Evidently at some point it becomes too difficult to read so use this wisely.


Write a loop thatnever ends, that means it isimpossible that the loop will ever terminate. If your test program does not terminate, you most likely have achieved this task. On a standard Linux terminal you can then pressCtrl+C to forcefullykill the program.
There are two flavors ofinfinite loops:
whiletruedobeginend;

The condidition needs to be negated in arepeatuntil loop:

repeatbeginenduntilfalse;
Infinite loops are very undesirable. While constant expressions like in these examples are easy to spot,tautologies, expressions that always evaluate totrue, or expressions that can never be fulfilled (in the case of arepeatuntil loop), are not. For instance, given thati was aninteger the loopwhilei<=maxIntdo will run indefinitely, becausei can never exceedmaxInt[fn 6] and thus break the loop’s condition. Therefore be reminded to carefully formulate expressions for conditional loops and ensure it will eventually reach a terminating state. Otherwise it can be frustrating for the user of yourprogram.
There are two flavors ofinfinite loops:
whiletruedobeginend;

The condidition needs to be negated in arepeatuntil loop:

repeatbeginenduntilfalse;
Infinite loops are very undesirable. While constant expressions like in these examples are easy to spot,tautologies, expressions that always evaluate totrue, or expressions that can never be fulfilled (in the case of arepeatuntil loop), are not. For instance, given thati was aninteger the loopwhilei<=maxIntdo will run indefinitely, becausei can never exceedmaxInt[fn 6] and thus break the loop’s condition. Therefore be reminded to carefully formulate expressions for conditional loops and ensure it will eventually reach a terminating state. Otherwise it can be frustrating for the user of yourprogram.


Rewrite the following loop as awhile-loop:
repeatbeginimagineJumpingSheep;sheepCount:=sheepCount+1;waitTwoSeconds;enduntilasleep;
The important thing is to realize is that theentire loop body is repeated above thewhile-loop even begins:
imagineJumpingSheep;sheepCount:=sheepCount+1;waitTwoSeconds;whilenotasleepdobeginimagineJumpingSheep;sheepCount:=sheepCount+1;waitTwoSeconds;end;
Do not forget to negate the condition when transforming a conditonal loop into the other kind. Obviously therepeatuntil-loop is more suitable in this case.
The important thing is to realize is that theentire loop body is repeated above thewhile-loop even begins:
imagineJumpingSheep;sheepCount:=sheepCount+1;waitTwoSeconds;whilenotasleepdobeginimagineJumpingSheep;sheepCount:=sheepCount+1;waitTwoSeconds;end;
Do not forget to negate the condition when transforming a conditonal loop into the other kind. Obviously therepeatuntil-loop is more suitable in this case.


If you are using a Linux or FreeBSDOS and anEP‑compliant compiler: Write aprogram that takes the output of the commandgetentpasswd as input and only prints thefirst field/column of every line. In apasswd(5) file, fields are separated by a colon (:). Yourprogram will list all known user names.
You can run the following program with the commandgetentpasswd|./cut1 (the file name of your executable program may differ).
programcut1(input,output);constseparator=':';varline:string(80);beginwhilenotEOF(input)dobegin{ This reads the _complete_ line, but at most}{ line.capacity characters are actually saved. }readLn(line);writeLn(line[1..index(line,separator)-1]);end;end.
Remember thatindex will return the index of the colon character which you do not want to print, thus you will need to subtract 1 from its result. Thisprogram will evidently fail if a line does not contain a colon.
You can run the following program with the commandgetentpasswd|./cut1 (the file name of your executable program may differ).
programcut1(input,output);constseparator=':';varline:string(80);beginwhilenotEOF(input)dobegin{ This reads the _complete_ line, but at most}{ line.capacity characters are actually saved. }readLn(line);writeLn(line[1..index(line,separator)-1]);end;end.
Remember thatindex will return the index of the colon character which you do not want to print, thus you will need to subtract 1 from its result. Thisprogram will evidently fail if a line does not contain a colon.


Based on your previous solution, extend yourprogram so only user names whoseUID is greater than or equal to1000. TheUID is stored in the third field.
The changed lines have been highlighted. A comment from the previous source code has been omitted.
programcut2(input,output);constseparator=':';minimumID=1000;varline:string(80);nameFinalCharacter:integer;uid:integer;beginwhilenotEOFdobeginreadLn(line);nameFinalCharacter:=index(line,separator)-1;{ username:encryptedpassword:usernumber:… }{         ↑ `nameFinalCharacter + 1` }{          ↑ `… + 2` is the index of the 1st password character }uid:=index(subStr(line,nameFinalCharacter+2),separator);{ Note that the preceding `index` did not operate on `line` }{ but an altered/different/independent “copy” of it. }{ This means, we’ll need to offset the returned index once again. }readStr(subStr(line,nameFinalCharacter+2+uid),uid);{ Read/readLn/readStr automatically terminate reading an integer }{ number from the source if a non-digit character is encountered. }{ (Preceding blanks/space characters are ignored and }{ the _first_ character still may be a sign, that is `+` or `-`.)}ifuid>=minimumIDthenbeginwriteLn(line[1..nameFinalCharacter]);end;end;end.
Recall from the previous chapter that the
third parameter insubStr can be omitted effectively meaning “give methe rest of astring.” Note that this programming task mimics (some of) the behavior ofcut(1).Use programs/source code that has already been programmedfor you whenever possible. Reinventing the wheel is not necessary. Nonetheless, this basic task is a good exercise. On aRHEL system you may rather want to setminimumID to500.
The changed lines have been highlighted. A comment from the previous source code has been omitted.
programcut2(input,output);constseparator=':';minimumID=1000;varline:string(80);nameFinalCharacter:integer;uid:integer;beginwhilenotEOFdobeginreadLn(line);nameFinalCharacter:=index(line,separator)-1;{ username:encryptedpassword:usernumber:… }{         ↑ `nameFinalCharacter + 1` }{          ↑ `… + 2` is the index of the 1st password character }uid:=index(subStr(line,nameFinalCharacter+2),separator);{ Note that the preceding `index` did not operate on `line` }{ but an altered/different/independent “copy” of it. }{ This means, we’ll need to offset the returned index once again. }readStr(subStr(line,nameFinalCharacter+2+uid),uid);{ Read/readLn/readStr automatically terminate reading an integer }{ number from the source if a non-digit character is encountered. }{ (Preceding blanks/space characters are ignored and }{ the _first_ character still may be a sign, that is `+` or `-`.)}ifuid>=minimumIDthenbeginwriteLn(line[1..nameFinalCharacter]);end;end;end.
Recall from the previous chapter that the
third parameter insubStr can be omitted effectively meaning “give methe rest of astring.” Note that this programming task mimics (some of) the behavior ofcut(1).Use programs/source code that has already been programmedfor you whenever possible. Reinventing the wheel is not necessary. Nonetheless, this basic task is a good exercise. On aRHEL system you may rather want to setminimumID to500.


See also the chapterSieve of Eratosthenes inDiscrete Mathematics.
Write a prime sieve. One routine does the calculations, another routine prints them. This exercise’s goal is to give you an opportunity to type, to write an adequate program. If necessary, you can peek at existingimplementations, but stillwrite it on your own, adding yourown comments to the source code.
The followingprogram meets all requirements. Note, an implementation using anarray[1..limit]ofBoolean would have been perfectly fine as well, although the shownsetofnatural implementation isin principle preferred.
programeratosthenes(output);type{ in Delphi or FPC you will need to write 1..255 }natural=1..4095;{$setLimit 4096}{ only in GPC }naturals=setofnatural;const{ `high` is a Borland Pascal (BP) extension. }{ It is available in Delphi, FPC and GPC. }limit=high(natural);{ Note: It is important that `primes` is declared }{ in front of `sieve` and `list`, so both of these }{ routines can access the _same_ variable. }varprimes:naturals;{ This procedure sieves the `primes` set. }{ The `primes` set needs to be fully populated }{ _before_ calling this routine. }proceduresieve;varn:natural;i:integer;multiples:naturals;begin{ `1` is by definition not a prime number }primes:=primes-[1];{ find the next non-crossed number }forn:=2tolimitdobeginifninprimesthenbeginmultiples:=[];{ We do _not_ want to remove 1 * n. }i:=2*n;whileiin[n..limit]dobeginmultiples:=multiples+[i];i:=i+n;end;primes:=primes-multiples;end;end;end;{ This procedures lists all numbers in `primes` }{ and enumerates them. }procedurelist;varcount,n:natural;begincount:=1;forn:=2tolimitdobeginifninprimesthenbeginwriteLn(count:8,'.:',n:22);count:=count+1;end;end;end;{ === MAIN program === }beginprimes:=[1..limit];sieve;list;end.
Appreciate the fact that because you have separated thesieve task from thelist task, both routine definitions and the main part of theprogram at the bottom remain quite short and are thus easier to understand.
The followingprogram meets all requirements. Note, an implementation using anarray[1..limit]ofBoolean would have been perfectly fine as well, although the shownsetofnatural implementation isin principle preferred.
programeratosthenes(output);type{ in Delphi or FPC you will need to write 1..255 }natural=1..4095;{$setLimit 4096}{ only in GPC }naturals=setofnatural;const{ `high` is a Borland Pascal (BP) extension. }{ It is available in Delphi, FPC and GPC. }limit=high(natural);{ Note: It is important that `primes` is declared }{ in front of `sieve` and `list`, so both of these }{ routines can access the _same_ variable. }varprimes:naturals;{ This procedure sieves the `primes` set. }{ The `primes` set needs to be fully populated }{ _before_ calling this routine. }proceduresieve;varn:natural;i:integer;multiples:naturals;begin{ `1` is by definition not a prime number }primes:=primes-[1];{ find the next non-crossed number }forn:=2tolimitdobeginifninprimesthenbeginmultiples:=[];{ We do _not_ want to remove 1 * n. }i:=2*n;whileiin[n..limit]dobeginmultiples:=multiples+[i];i:=i+n;end;primes:=primes-multiples;end;end;end;{ This procedures lists all numbers in `primes` }{ and enumerates them. }procedurelist;varcount,n:natural;begincount:=1;forn:=2tolimitdobeginifninprimesthenbeginwriteLn(count:8,'.:',n:22);count:=count+1;end;end;end;{ === MAIN program === }beginprimes:=[1..limit];sieve;list;end.
Appreciate the fact that because you have separated thesieve task from thelist task, both routine definitions and the main part of theprogram at the bottom remain quite short and are thus easier to understand.


Write aprogram that reads an infinite number of numerical values frominput and at the end prints onoutput the arithmetic mean.
programarithmeticMean(input,output);typeintegerNonNegative=0..maxInt;vari,sum:real;count:integerNonNegative;beginsum:=0.0;count:=0;whilenoteof(input)dobeginreadLn(i);sum:=sum+i;count:=count+1;end;{ count > 0: do not do division by zero. }ifcount>0thenbeginwriteLn(sum/count);end;end.

Note that using a datatype excludingnegative numbers (here we named itintegerNonNegative) mitigates the issue thatcount may flip the sign, a condition known asoverflow. This would cause theprogram to fail ifcount:=count+1 became too large, and effectively falls out of the range0..maxInt.

There is, despitemaxReal, no programmatic way to tell thatsum became too large or too small rendering it severely inaccurate, becauseany value ofsum may be legit nevertheless.
programarithmeticMean(input,output);typeintegerNonNegative=0..maxInt;vari,sum:real;count:integerNonNegative;beginsum:=0.0;count:=0;whilenoteof(input)dobeginreadLn(i);sum:=sum+i;count:=count+1;end;{ count > 0: do not do division by zero. }ifcount>0thenbeginwriteLn(sum/count);end;end.

Note that using a datatype excludingnegative numbers (here we named itintegerNonNegative) mitigates the issue thatcount may flip the sign, a condition known asoverflow. This would cause theprogram to fail ifcount:=count+1 became too large, and effectively falls out of the range0..maxInt.

There is, despitemaxReal, no programmatic way to tell thatsum became too large or too small rendering it severely inaccurate, becauseany value ofsum may be legit nevertheless.


This task is a fine exercise for those using anEP-compliant compiler: Write atimefunction that returns astring in the “American” time format9:04 PM. This may look easy at first, but it can become quite a challenge. Have fun!
A smart person would try toreusetime itself. However, the output oftime itself isnot standardized, so we will need to define everything by ourselves:
typetimePrint=string(8);functiontimeAmerican(ts:timeStamp):timePrint;consthourMinuteSeparator=':';anteMeridiemAbbreviation='AM';postMeridiemAbbreviation='PM';typenoonRelation=(beforeNoon,afterNoon);letterPair=string(2);var{ contains 'AM' and 'PM' accessible via an index }m:array[noonRelation]ofletterPair;{ contains a leading zero accessible via a Boolean expression }z:array[Boolean]ofletterPair;{ holds temporary result }t:timePrint;begin{ fill `t` with spaces }writeStr(t,'':t.capacity);

This fallback value (in the casets.timeValid isfalse) allows the programmer/“user” of thisfunction to “blindly” print its return value. There will be anoticeable gap in the output. Another sensible “fallback” value would be an emptystring.

withtsdobeginiftimeValidthenbeginm[beforeNoon]:=anteMeridiemAbbreviation;m[afterNoon]:=postMeridiemAbbreviation;z[false]:='';z[true]:='0';writeStr(t,((hour+12*ord(hour=0)-12*ord(hour>12))mod13):1,hourMinuteSeparator,z[minute<10],minute:1,' ',m[succ(beforeNoon,hourdiv12)]);

This is the most complicated part of this problem. First of all, all number parameters towriteStr areexplicitly suffixed with:1 as the minimum-width specification, because there aresome compilers that would otherwise assume, for example,:20 as a default value. Since we know thattimeStamp.hour is in the range0..23 we can use thediv andmod operations as demonstrated. However, we will need account of anhour value of0, which is usually denoted as 12:00 AM (andnot zero). A conditional “shift” by 12 using the shownBoolean expression andord “fixes” this. Furthermore, here is a brief reminder that inEP thesucc function accepts a second parameter.

end;end;timeAmerican:=t;end;
Finally we will need to copy ourtemporary result, to thefunction result variable. Rememberthere must be exactly one assignment, although not all compilers enforce this rule.
A smart person would try toreusetime itself. However, the output oftime itself isnot standardized, so we will need to define everything by ourselves:
typetimePrint=string(8);functiontimeAmerican(ts:timeStamp):timePrint;consthourMinuteSeparator=':';anteMeridiemAbbreviation='AM';postMeridiemAbbreviation='PM';typenoonRelation=(beforeNoon,afterNoon);letterPair=string(2);var{ contains 'AM' and 'PM' accessible via an index }m:array[noonRelation]ofletterPair;{ contains a leading zero accessible via a Boolean expression }z:array[Boolean]ofletterPair;{ holds temporary result }t:timePrint;begin{ fill `t` with spaces }writeStr(t,'':t.capacity);

This fallback value (in the casets.timeValid isfalse) allows the programmer/“user” of thisfunction to “blindly” print its return value. There will be anoticeable gap in the output. Another sensible “fallback” value would be an emptystring.

withtsdobeginiftimeValidthenbeginm[beforeNoon]:=anteMeridiemAbbreviation;m[afterNoon]:=postMeridiemAbbreviation;z[false]:='';z[true]:='0';writeStr(t,((hour+12*ord(hour=0)-12*ord(hour>12))mod13):1,hourMinuteSeparator,z[minute<10],minute:1,' ',m[succ(beforeNoon,hourdiv12)]);

This is the most complicated part of this problem. First of all, all number parameters towriteStr areexplicitly suffixed with:1 as the minimum-width specification, because there aresome compilers that would otherwise assume, for example,:20 as a default value. Since we know thattimeStamp.hour is in the range0..23 we can use thediv andmod operations as demonstrated. However, we will need account of anhour value of0, which is usually denoted as 12:00 AM (andnot zero). A conditional “shift” by 12 using the shownBoolean expression andord “fixes” this. Furthermore, here is a brief reminder that inEP thesucc function accepts a second parameter.

end;end;timeAmerican:=t;end;
Finally we will need to copy ourtemporary result, to thefunction result variable. Rememberthere must be exactly one assignment, although not all compilers enforce this rule.

Sources:

  1. Wirth, Niklaus (1979). "The Module: a system structuring facility in high-level programming languages". proceedings of the symposium on language design and programming methodology. Berlin, Heidelberg: Springer. Abstract. doi:10.1007/3-540-09745-7_1. ISBN 978-3-540-09745-7. https://link.springer.com/content/pdf/10.1007%2F3-540-09745-7_1.pdf. Retrieved 2021-10-26. 
  2. Cooper, Doug. "Chapter 11. Therecord Type".Oh! Pascal! (third edition ed.). p. 374.ISBN 0-393-96077-3.[…] records have two unique aspects:
    First, the stored values can have different types. This makes records potentiallyheterogeneous—composed of values of different kinds. Arrays, in contrast, hold values of just one type, so they're said to behomogeneous.
    […]
    {{cite book}}:|edition= has extra text (help);line feed character in|quote= at position 269 (help);syntaxhighlight stripmarker in|chapter= at position 17 (help)
  3. Wirth, Niklaus (1973-07-00).The Programming Language Pascal (Revised Report ed.). p. 30.Within the component statement of the with statement, the components (fields) of the record variable specified by the with clause can be denoted by their field identifier only, i.e. without preceding them with the denotation of the entire record variable.{{cite book}}:Check date values in:|date= (help)
  4. Jensen, Kathleen; Wirth, Niklaus.Pascal – user manual and report (4th revised ed.). p. 39.doi:10.1007/978-1-4612-4450-9.ISBN 978-0-387-97649-5.The initial and final values are evaluated only once.

Notes:

  1. This kind ofrecord will not be able to store anything. In thenext chapter you will learn a (and the only) instance it could be useful.
  2. Indeed most compilers consider the dot as a dereferencing indicator and the field name denotes a static offset from a base memory address.
  3. In Standard (“unextended”) Pascal,ISO standard 7185, afunction can only return “simple data type” and “pointer data type” values.
  4. Actually the shownbeginend is redundant sincerepeatuntil constitute a frame in their own right. For pedagogical reasons we teach you to always usebeginend nevertheless wherever asequence of statements usually appears. Otherwise you might change yourrepeatuntil loop to awhiledo loopforgetting to surround the loop’s body statements with a properbeginend frame.
  5. Thepacked designation has been omitted for simplicity.
  6. According to most compilers’ definition ofmaxInt. TheISO standards merely require, that all arithmetic operations in the interval-maxInt..maxInt work absolutely correct, but it is thinkable (although unlikely) that more values are supported.
Next Page: Pointers | Previous Page: Strings
Home: Pascal Programming
Retrieved from "https://en.wikibooks.org/w/index.php?title=Pascal_Programming/Records&oldid=4354065"
Category:
Hidden categories:

[8]ページ先頭

©2009-2025 Movatter.jp