7.14.6. PROGRAMMING STANDARDS

Purpose

A programmer must understand why adhering to a set of standards is beneficial .

Initially, it takes a little extra time to incorporate such things as informative user prompts and comments into a program. However, when the program is fully debugged and tested, it is completely finished; there is no need to go back an 'clean it up'.

Last minute 'clean up' efforts spoil the satisfaction of having completed the program. They have also been known to produce program flaws - a line of good code can accidentally be overwritten in the editing process.

Adhering to programming standards makes a program much more readable, and pleasant to use. The person you are writing the program for will certainly enjoy this, but why not do it from the beginning, so that you can enjoy the same benefits?

Program Format

1. 'Pretty Print' is a term used to describe the pleasing appearance of a program. A program that is easy to read is likely to be easy to understand. Here are some guidelines:

Use vertical alignment and indentation to make the code readable. For example:

if condition then

do this

and this

and this

else

do this

and this

and this

end if

Put each END statement on a separate line and make sure that it is vertically aligned with its 'partner'.

Use blank lines to improve readability.

Do include a reasonable number of comments in your code; program, function, and procedure headers are mandatory, but you might also wish to add some explanatory comments before particularly complex sections of code.

Do not clutter up a program with too many comments; a comment for each line of code is definitely overdoing it.

2. Program header - a program should begin with a block of comments which includes the following information:

/***********************************************************

Filename: rootsearch.t

Author: your name

Student #: 123 456 789

Purpose: include a brief explanation of the purpose of the program

************************************************************/

If you have any functions or procedures in your program, their names should be included in the program header.

Functions: function1, function2, ...

Procedures: procedure1, procedure2, ...

Use asterisks in the top and bottom comment lines to set the header off. The start of the main program should be set off in a similar manner. e.g.

/******************* main program **********************/

3. Procedure, Function headers - procedures and functions should begin with a block of comments which includes the following information:

/* ------------------------------------------------------------------------------------------------------

Filename: rootsearch.t <- necessary when the module is in a separate file

Name: function or procedure name

Purpose: include a brief explanation of the purpose of the module

------------------------------------------------------------------------------------------------------* /

Use hyphens in the top and bottom comment lines to set the header off.

4. Choose meaningful variable names. An acceptable exception to this rule is that loop counters can have single character names. The variable names I, J, and K are generally recognized as loop counters.

5. Variable declarations should be grouped - all integers together, all reals together, and so on.

6. Avoid global variables whenever possible. A large program will consist of many modules (functions and procedures). A global variable can be changed in any module which makes a program harder to debug. Always start the name of a global with a capital letter so it can be easily recognized as being global.

Program Execution

1. Begin your program with a brief explanation of the purpose.

e.g. "Program to solve quadratic equations."

2. Use informative user prompts. Say what values are expected and how they are to be entered. e.g. "Enter 3 coefficients separated by spaces: "

3. Explain any output i.e. do not just print a value by itself; accompany it with a brief but clear explanation of the value.

e.g. "The roots are: "

4. Error messages must be meaningful. A message such as "bad value" is next to useless. What value is being referred to, why is the value bad? Make your error messages concise, but informative.

5. If you choose to stop the program execution under some conditions, display a message indicating why the program has been terminated.

6. Avoid goto statements whenever humanly possible. Too many gotos result in a tangled mess of convoluted program flow sometimes referred to as "spaghetti code".

Example:

/* **************************************************************

Filename: average.t

Author: Joe Blow

Student #: 123 456 789

Purpose: To average 2 numbers.

************************************************************/

/************************* main program *****************/

var num1, num2: int

var the_sum: real

put "This program will average 2 numbers."

put "Enter 2 numbers, separated by a space:"

get num1, num2

the_sum := (num1 + num2) / 2

put "The average is ", the_sum

Programming Assignments

In all programs you do for class assignments, you must follow the standards just described.

For lab assignments: Documentation and comments will not normally be required - this is only because of time constraints. Also, you do not need to include rigorous error checking in a lab assignment, unless this is specified in the assignment sheet. You should however, in all programs, attempt to format your code e.g. indent the code inside IF statements, FOR loops, and so on.

Programming Principles

Section 2.2. of the Users' Manual appears, at first glance, to be somewhat facetious. Despite the apparently light hearted approach, it contains many relevant observations. For example:

under 'Design': "Understand the problem. The best design is often the simplest."

under "Project Management': "Furious activity is no substitute for understanding."

under 'User Interface': "Details count."

under 'Coding': "If you can't write it down in English, you can't code it."

under 'Debugging': "Testing shows the presence of bugs, not their absence."

under 'Documenting': " Don't over decorate. Avoid over-documentation."

Section 2.2 is worth reading - use what you can to develop and improve your own programming style.

7.14.7. SUBPROGRAMS

When there are too many statements in a program, the program become difficult to understand and update. One way to solve this problem is using subprograms.

Other advantages of using subprograms include:

dividing the program into parts that can be written by different people and over different time periods;

factoring out commonly used parts of programs so they can be used in different places within one program and in different programs;

separating program into different parts so that each part can be tested individually.

There are two ways for communicating data between the main program and a subprogram, one is using global data, the other is using parameters of the subprogram.

Some subprograms are available along with the programming language. These subprograms are said to be predefined or built-in or predeclared.

Subprograms can be divided into two classes: procedures and functions.

7.14.7.1. PROCEDURES

A procedure is a subprogram that consists of a set of statements. These statements act together completing a desired task.

The general form of a procedure is:

procedure procedure_name [ ( [var] parameter : type ... ) ]

[ local variable declarations ]

statement(s)

end procedure_name

The square brackets in the preceding description indicate that parameters may, or may not, be present, and that a parameter may, or may not, be preceded with the keyword `var'. Also, a procedure may, or may not, contain local variables. A local variable exists only within the scope of the procedure.

If a parameter is preceded with var, then the procedure will be able to alter the value of the variable; otherwise, the procedure will be able to obtain the value of a parameter, but will not be able to change the value.

Here is a simple procedure, without parameters, named Heading.

procedure Heading

put " ": 10, "The average mark"

put " ": 14, "in class"

put " ": 16, "for student"

end Heading

Line 1 is the procedure header. Line 5 is the procedure's end. Lines 2 through 4 are the procedure body. The procedure can be called by including the name of the procedure as a statement like this:

/* another part of the program */

Heading

The preceding line causes the computer to execute lines 2 through 4 and display the following message:

__________The average mark

______________in class

________________for student

The complete program would appear as:

/**************************************************************

Filename: heading.t

Author: Joe Blow

Student #: 123 456 789

Purpose: To experiment with procedures.

***************************************************************/

/*----------------------------------------------------

Name: Heading

Purpose: To print a heading

---------------------------------------------------------*/

procedure Heading

put " ": 10, "The average mark"

put " ": 14, "in class"

put " ": 16, "for student"

end Heading

/************************* main program *****************/

Heading /* this line just calls the procedure */

Now let's look at how parameters may be used in a procedure.

Remember that if a parameter is preceded with var, then the procedure will be able to alter the value of the variable; otherwise, the procedure will be able to obtain the value of a parameter, but will not be able to change the value.

If a parameter is a dynamic array, then in the header the upper limit of the array is given by an asterisk.

For example, given this procedure heading:

procedure GetMarks(className: string,

var studentList: array 1 .. * of int,

listSize : int)

the variable "className" can be obtained by the procedure, but not changed;

the "studentList" is a dynamic array, its values can be altered in the procedure;

the "listSize" variable can be accessed within the procedure, but not changed by it.

A call to the procedure must supply appropriate values for the parameters.

e.g.

GetMarks("Computer Science", studentList, listSize)

Let's look at a complete example.

	

/* **************************************************************

Filename: classes.t

Author: Joe Blow

Student #: 123 456 789

Purpose: To manipulate a set of student grades.

************************************************************** */

/*--------------------------------------------------------

Name: GetMarks

Purpose: To obtain a set of grades

---------------------------------------------------------------*/

procedure GetMarks(className: string,

var studentList: array 1 .. * of int,

listSize : int)

put "Enter the marks for ", className, ":"

for i : 1 .. listSize

get studentList(i)

end for

end GetMarks

/******************** main program ***************/

var listSize : int

put "Enter the number of students: "

get listSize

var studentList : array 1 .. listSize of int

% Call the procedure 'GetMarks', giving it 3 parameters:

GetMarks("Computer Science", studentList, listSize)

for i: 1..listSize

put studentList(i)

end for

7.14.7.2. FUNCTIONS

Another type of subprogram is a function. The declaration of a function is very similar to that of a procedure. Other than the keywords "function" and "procedure", the difference between the two types of subprograms is that a function returns a value when it is called.

The general form of a function is:

function function_name [(parameter:type...)]:function_type<>

[ local variable declarations ]

statement(s)

[result variable_name or expression ]

end function_name

The function header must indicate the type of value that will be returned; this is the meaning of ": function_type " in the preceding description. Somewhere (it can be anywhere) in the function, the function must indicate, by the keyword result, the value that is to be returned; this is the meaning of "result variable_name or expression " in the preceding description.

In the preceding description of a function (as was the case with procedures):

The square brackets indicate that parameters may, or may not, be present, and that a parameter may, or may not, be preceded with the keyword `var'. Also, a function may, or may not, contain local variables. A local variable exists only within the scope of the function.

If a parameter is preceded with var, then the function will be able to alter the value of the variable; otherwise, the function will be able to obtain the value of a parameter, but will not be able to change the value.

Let's write a function that calculates the average of two numbers.

function avrg(num1, num2: real): real

var num_avrg: real

num_avrg := (num1+ num2) / 2

result num_avrg

end avrg

The first line is the header of the function. avrg is the name of this function. num1 and num2 in the parentheses are parameters. The colon and keyword real following them declare that num1 and num2 are variables of real type. The colon followed by the keyword real after the parentheses specifies that the result of this function is a real number. The second line declares that num_avrg is a variable of type real. When variables are declared within a function or a procedure, they can be only accessed within the function or procedure. These kind of declarations are called local declarations. The third line is the calculation. In the fourth line, result is a keyword that returns the value of num_avrg. We can shorten the preceding function as follows.

function avrg(num1, num2: real): real

result (num1+ num2) /2

end avrg

To use a function, you only need to call it. For example, to assign the result of function avrg to a variable z we write:

/* another part of the program */

var z: real

z := avrg(5, 6)

The value 5.5 is assigned to z now.

The complete program would appear as:

/* **************************************************************

Filename: functions.t

Author: Joe Blow

Student #: 123 456 789

Purpose: To experiment with functions.

************************************************************** */

/*-------------------------------------------------------

Name: avrg

Purpose: To compute the average of 2 numbers.

---------------------------------------------------------------*/

function avrg(num1, num2: real): real

result (num1+ num2) /2

end avrg

/******************** main program *****************/

var z: real

z := avrg(5, 6)

put "The average of 5 and 6 is ", z

Sometimes we need to use dynamic arrays or dynamic strings as parameters. This means that the size of the array or string can be varied. In the following example, a is a dynamic array parameter of the function sum.. In the body of the function, we can use the pre-defined function upper to get the upper bound of the formal parameter a.

%Calculate the sum of the elements in a dynamic array

function sum(a: array 1 .. * of real): real

var s : real := 0.

for i: 1 .. upper(a)

/* upper( ) is a pre-defined function

that returns the upper bound of an array */

s := s + a(i)

end for

result s

end sum

Here is an example of how to use a dynamic string. The procedure adds a comma in the string target at a place specified by the parameter place.

procedure dvd(var target: string(*), place: int)

target := target(1 .. place) + "," + target(place + 1 .. *)

end dvd

7.14.7.3. PRE-DEFINED FUNCTIONS AND PROCEDURES

Some very useful functions and procedures are available along with the programming language. These functions or procedures are called pre-defined. Here are some examples.

Pre-defined Function Example:

sin(arg: real): real

The sin function takes a "real" argument, arg, and returns a "real" value, the sine of arg.

Pre-defined Procedure Example:

randint(var randvar : int, bottom, top: int)

The randint procedure generates a random integer in the range specified by the second and third parameters, bottom and top: The random integer is placed in the first parameter, randvar .

A list of Turing pre-defined functions and procedures follows.

abs(arg) returns the absolute value of a given argument

arctan(arg) returns the arctangent (in radians) of a given argument

arctand(arg) returns the arctangent (in degrees) of a given argument

ceil(arg) returns an integer greater than or equal to the given real argument

chr(arg) returns the character value of a given integer argument

cos(arg) returns the cosine (in radians) of a given argument

cosd(arg) returns the cosine (in degrees) of a given argument

exp(arg) returns the value of e taken to the power of the given argument

floor(arg) takes a real value and returns an integer less than or equal to the real

frealstr(arg1, arg2, arg3) returns a string `equivalent' to the real specified by arg1; string length will be n (specified by arg2) characters long; number of fractional digits to be converted specified by arg3

index(arg1, arg2) returns the starting position, in the character string specified by arg1, of the character string specified by arg2

intreal(arg) returns the real value of the given integer argument

intstr(arg) returns a string `equivalent' to the given integer argument

length(arg) returns the number of characters in the string in the given argument

ln(arg) returns log to the base e of the given argument

max(arg1, arg2) returns whichever is the larger of the two given arguments

maxint returns the largest integer value possible in the current installation

min(arg1, arg2) returns whichever is the smaller of the two given arguments

ord(arg) returns the integer value of a given character argument

rand(var) places a random number (between 0 and 1) in the given variable

randint(var, arg1,arg2) places a random integer (between the arg1 value and the arg2 value) in the given variable

randomize ensures that a different set of random numbers will be generated the next time the program is run

realstr(arg1, arg2) returns a string `equivalent' to the real specified by arg1; string length will be n (specified by arg2) characters long

repeat(arg1, arg2) returns a concatenated string composed of n (specified by arg2) occurrences of the string specified by arg1

round(arg) returns the nearest integer value to the given argument

sign(arg) returns: 1 if argument is positive, 0 if = zero, or -1 if negative

sin(arg) returns the sin (in radians) of a given argument

sind(arg) returns the sin (in degrees) of a given argument

sqrt(arg) returns the square root of a given argument

strint(arg) returns a integer `equivalent' to the given string argument

strreal(arg) returns a real `equivalent' to the given string argument

7.14.8. DATA STRUCTURES

Information may be arranged into different structures. Some comman data structures are introduced in this section.

7.14.8.1. QUEUES

A queue is a collection of data items kept in order of their arrival. Items leave from the head of the queue and new items enter at the tail of the queue. We call this discipline first-in-first-out (FIFO).

The following is a very simple implementation of a queue using an array. The length of the queue is fixed to a constant number.

type entryType: int

const length := 10

var entry: array 1 .. length of entryType

var head: 1 .. length := 1

var tail: 1 .. length := length

procedure move(in: entryType, var out: entryType)

tail := (tail mod length) + 1

head := (head mod length) + 1

entry(tail) := in

out := entry(head)

end move

7.14.8.2. STACKS

The discipline for a stack is last-in-first-out (LIFO). The following is an example of how to implement a stack. The two basic operations of a stack is push and pop. When you push an item, the item is put on topof the stack. When you pop an item, the item on top of the stack is picked out.

/**********************************************************

Filename: stack.t

Author: Anonymous

Date: July, 1995

Description: This example will print out an input string

in reverse order.

***********************************************************/

type entryType: string(1)

const len := 100

var entry: array 1 .. len of entryType

var top: 0 .. len := 0

procedure push(item: entryType)

pre top < len

top := top + 1

entry(top) := item

end push

procedure pop(var item: entryType)

pre top > 0

item := entry(top)

top := top - 1

end pop

var item: entryType

var count: int :=0

put "Enter a string no more than ",len," characters: "

loop

getch(item)

exit when item="\n" or count>len

count := count + 1

push(item)

end loop

put ""

put "The string in reverse order is: "

for i: 1..count

pop(item)

put item..

end for

/* end of program */

7.14.8.4. LINKED LISTS

A linked list is a list of elements. Each element contains two parts. One is the data part, the other is a pointer. The pointer locates the next element. There is also a pointer pointing to the first element. The pointer in the last element points to no location and is called a nil pointer.

The following is an example. In this example, the procedure insertData is used to insert new data value into an ordered list. The procedure deleteData is used to find an element in the list and delete it.

/**********************************************************

Filename: list.t

Author: Zhiwei Wang

Date: July, 1995

Description: This is an example using linked list to

maintain an ordered name list. The basic functions

are inserting an item in the list, deleting an item in the list, and printing the list. You can modify the data part by adding more fields in it, such as student number, address, etc. You can also add some more functions such as searching an item.

***********************************************************/

var node: collection of

record

data: string

link: pointer to node

end record

var first: pointer to node := nil(node)

% insertData is used to insert new data into linked list

procedure insertData(item: string)

var entry: pointer to node

% Obtain storage element for new data

new node, entry

% Place data in element

node(entry).data := item

% See if new data goes first in list

if first = nil(node) or item < node(first).data then

node(entry).link := first

first := entry

else

% Find a place to insert new data

var previous: pointer to node := first

var next: pointer to node := node(first).link

loop

exit when next = nil(node) or item < node(next).data

previous := next

next := node(next).link

end loop

% Adjust links to make insertion

node(previous).link := entry

node(entry).link := next

end if

end insertData

% deleteData is used to delete specified data from the list

procedure deleteData(item : string)

var previous: pointer to node

var old: pointer to node := first

loop

exit when old = nil(node) or item = node(old).data

previous := old

old := node(old).link

end loop

% Remove node from list

if old = nil(node) then

put item, " not found in link list"

return

elsif first = old then % Node first in list

first := node(old).link

else

node(previous).link := node(old).link

end if

% Free unused node

free node, old

end deleteData

% printData is used to print the linked list

procedure printData

if first = nil(node) then

put "Empty list"

else

var temp: pointer to node := first

loop

exit when temp = nil(node)

put node(temp).data

temp := node(temp).link

end loop

end if

end printData

% Procedure menu prints out a menu

procedure menu

put "***********************************************"

put " 1. Enter an item "

put " 2. Delete an item "

put " 3. Print list "

put " 4. Quit"

put "***********************************************"

end menu

/* main program */

var choice : string(1)

var name : string

cls

menu

loop

put " Enter your choice: " ..

getch(choice)

put ""

case choice of

label "1":

put "Enter an name"

get name

insertData(name)

label "2":

put "Enter the name to be deleted"

get name

deleteData(name)

label "3":

printData

label "4":

put "Thank you for using this program"

exit

label: put "Type 1, 2, 3, or 4, try again"

end case

end loop

/* end of program */

In the above procedure, the problem of trying to delete from a data item that is not present in the list is taken care of by outputting an error message.

7.14.8.5. TREES

A tree consists of many nodes, each node contains the data itself and two pointers, one pointing to the left subtree, the other pointing to the right subtree. If there is no subtree from a particular node, the corresponding pointer is set to nil.

See below for an example of how a tree, in this case a binary one, can be conveniently implemented by using pointers and collections.

Here are the declarations, the procedures and the main part of the program:

/**********************************************************

Filename: linesort.t

Author: Anonymous

Date:

Description: Read lines from a file called "text",

then output them in a sorted order in screen.

***********************************************************/

var node: collection of

record

data: string % Holds one line of data

left: pointer to node % Left subtree

right: pointer to node % Right subtree

end record

type nodePointer: pointer to node

var root: nodePointer := nil(node) % Tree starts empty

procedure enterLine(line: string)

% "line" is inserted into tree

const nilNode := nil(node)

var L: nodePointer

new node, L % allocate then fill in new node L

node(L).data := line

node(L).left := nilNode

node(L).right := nilNode

if root = nilNode then

root := L % Attach first node to root

else

var p: nodePointer := root

loop % Search for place to attach new node

bind var n to node(p) % Shorten name to n

if line < n.data then

if n.left = nilNode then

n.left := L % Attach new node here

exit

else

p := n.left % Search left subtree

end if

else

if n.right = nilNode then

% Attach new node here

n.right := L

exit

else

% Search right subtree

p := n.right

end if

end if

end loop

end if

end enterLine

procedure outputLines(p: nodePointer)

if p not= nil(node) then

outputLines(node(p).left) % Output left subtree

put node(p).data % Output this node

outputLines(node(p).right) % Output right subtree

end if

end outputLines

% Main part of the program

% the data structure used is a binary tree built using

% collections. Successive lines are inserted as new leaves

% in the tree. Read and enter lines; then output all lines.

var fileNo: int

open: fileNo, "text", get

assert fileNo not= 0

var line: string

loop

exit when eof(fileNo)

get: fileNo, line: * % Read next line

enterLine(line) % Insert line into binary tree

end loop

outputLines(root) % Output all lines in tree

/* end of program */

(end of section)