Programming Course

Chapter 4

Program Design

Lisp III - Selective Structures

4.1 Program Development

4.1.1 Modular Programming

A computer program consists of a series of instructions or statements in a selected programming language. A computer executes each statement in a program and perhaps displays the results of its execution process. In order to write programs that do all the things that are desired and that do them all correctly, it is extremely useful to develop programs in a modular fashion.

It may be helpful to consider what we normally do when confronted with a problem such as: buying fruits. We immediately think of subtasks: need money to pay, select fruits, transport, and others. Perhaps the next thing we may do is to order these subtasks, i.e. which subtask needs to be done before any other subtasks. For example, we cannot pay for the fruits before we have selected the ones we want. Modular program development follows a process that is quite similar.

The concept of modular program development is very simple and very powerful. It involves dividing a task into smaller subtasks and then developing solutions for each of the subtasks. A subtask may be divided into further subtasks and so on. The advantages of this strategy in problem solving (since that is why we write computer programs) are two-fold. First, it is easier to find a solution for a small problem than a bigger one. Second, it is possible that the solutions developed for smaller problems (or subtasks) of one big problem may be reusable in another big problem.

While modular programming provides a good strategy for developing and writing programs, two additional concepts need to be understood as well. These are data models and control structures supported in a specific programming language.

4.1.2 Data Models

Computer programs manipulate data like numbers, characters, strings, lists, and others. Further, there are operations on these data that are supported in a given programming language such as two numbers can be added, two character strings can be compared for length or similarity. These issues are referred to as the data model of a programming language.

Consider the problem of buying the fruits. We need a list of names of fruits (i.e. character strings), amount of each fruit we wish to buy (which may be a whole number like 6 bananas or a number with fractions like 1.25 kilograms of berries). Further, we may have a list that we consult during the shopping trip and upon which we tick off each fruit as we take it off the shelf. These are the different kinds of data types we normally use in computer programs.

An additional thing to note here with the example of shopping for fruits is that we keep a list of fruits from the start till the end, i.e. it is available at all times during the shopping trip. In computer programs such data items are referred to as global variables. On the other hand, while selecting the berries, we may look for one kind of color or smell, and for the bananas, a different color or smell. These kinds of data items in computer programs are referred to as local variables, i.e. data that are useful in one context but that are different or not needed in another context.

4.1.3 Control Structures

A computer program consists of a series of statements that are executed by a computer in the order in which each statement is written in a program. The following are the three possibilities to control the flow of execution of computer programs in most programming languages.

4.2 Program Planning Techniques

The first step in writing any program, even a simple program, is planning. By properly planning a program, strategies can emerge that will help you make your program more efficient and reduce the time required for writing and debugging it. A good plan should provide at least:

  1. An overview of the entire project,
  2. Clear subdivisions that allow modular testing,
  3. Checks for variable and value passing,
  4. A systematic and easy to use naming convention for functions and variables.

Programs can be planned once the general Problem Statement has been identified. Problem Statements describe the purpose of a programming task. Generally, they do not refer to exactly how the task should be solved. Examples of Problem Statements include: Buy fruit; Draw a two-dimensional shape; Make the macro that you wrote in exercise three into a program (exercise three); Write a program that translates AutoCAD data into PolyTRIM format; and so on.

The program planning techniques that are introduced here use natural language to describe the structure of a program. Flow charts and structure diagrams employ visual aides as well. All of these techniques help to organize a project and to isolate possible flaws in the logical structure of a problem solving strategy. They may also reveal opportunities for streamlining a program. At least one of these techniques should be employed to fully outline a new programming task before ANY programming is done.

4.2.1 Pseudo-Code

Pseudo-code describes the structure of a proposed program in natural language terms. Because LISP code can contain comments preceded by a semicolon (;), pseudo-code that follows this convention can be used directly in the program code itself as a program outline. This technique provides a built-in overview of what has been done, as well as what is still to be done. A problem statement can be expanded with pseudo-code to any number of sublevels. The following is an example of a simple pseudo-code for the Problem Statement: Buy Fruit and an extended version of the same.
;Problem Statement: Buy Fruit.

;Make a fruit list.

;Go to the market.

;Select fruit.

;Pay for the Fruit.

;Go home.

;Problem Statement: Buy Fruit.

;Make a fruit list.

;List fruits to buy.

;List the quantities.

;Go to the market.

;If open enter.

;If closed go to another market.

;If three are closed: Go home.

;find fruit department.

;Select Fruit.

;find fruit. (loop for each fruit on list.)

;if available and good: select.

;if not: find next fruit on list.

;Pay for the Fruit. (if selected.)

;if fruit found:

;pay for it.

;else, go to another market or Go home.

;If all fruit not found:

;go to another market or Go home.

;Go home.

Pseudo-code will help identify what types of data structures and variables might best suit the task at hand. For instance, the problem illustrated above could employ a global variable called *fruit_list*, that is a list of sublists; each sublist containing the name of a fruit and the desired quantity. The need for various local variables, such as counters, can also be derived from such a format.

Actions that are described more than once in such a description, indicate the chance to write a general operation that can be used for all required occasions. For instance, "Go home" appears four times in the above pseudo-code, indicating a cancellation or successful conclusion of the Buy Fruit program. By checking if all (or any) of the desired fruit has indeed been purchased, a single, generalized go_home function can be written to fit all of the situations in which it might be called.

The degree to which a given programming task should be expanded depends greatly on the complexity of the given task and the programmer's own experience. The goals are clarity, simplicity and legibility.

4.2.2 Flow Charts

Flow charts, though some what out of fashion, are still a useful tool for beginning programmers, especially for those with a strong ability to visualize problems and solutions. Flow charts make use of a number of simple symbols that indicate actions to be taken or decisions to be made. These symbols are connected by a series of lines and arrows that indicate the direction of a program's flow. The following are the most common flow chart symbols:

Statements or structure blocks indicate either a definitive action, a further subset of the program code or an entire program. Statements are limited to one entry point and one point of exit each. Sequential programming is depicted by linking two or more statements together. Conditional constructs are indicated by diamond shapes that also have only one point of entry, but have more than one exiting point. Loops are indicated by combining a sequence of one or more statements and a directional arrow that connects the exit point of the last statement to the entry point of the first statement in a loop. The following is a flow chart equivalent to the simple form of the pseudo-code illustrated in the previous section:

Each statement or structure block of this flow chart can be extended into a more detailed representation. The following illustrates an expansion of the Select fruit statement:


Notice that the expanded statement, like the original, also has only one point of entry and one exit. The expanded version must require and produce exactly the same information that the simpler statement does. In this example, the *fruit_list* variable, created while executing the statement Make a list, was required in order to perform the action indicated by the first substatement of Select fruit: Look for 1st fruit on list. The *fruit_list* variable is also altered during the course of Select fruit, if fruit on the list is indeed found and selected. By passing the updated version of the *fruit_list* variable out of Select fruit, and into the next statement, Pay for the fruit will be able to check what must be paid for.

4.2.3 Structure Diagrams

Structure diagrams are similar in many ways to flow charts. They graphically represent the basic structure of the problem solving strategy selected for a given Problem Statement, using a slightly different graphic vocabulary. Structure diagrams tend to require terminology related more closely to the programming language than flow charts do. The following are representations used to illustrate some of the more common control structures in a program: Statement, if-then-else, conditions and loops.


As in the case of flow charts, statements can represent specific actions, subdivisions of a program or entire programs. Statements can be constructed out of any legal combination of the control constructs, as well as further embedded statements. Structure diagrams are built within a rectangular framework. This facilitates easy visual checks for completeness that the free form of a flow chart does not allow. The following structure diagram is equivalent to the expanded flow chart illustrated in the previous section.


Note that it is not possible to jump horizontally between statements or constructs in a structure diagram. All actions flow downward, with the exception of loops, towards the terminating bottom line. Complicated programs may require separate structure diagrams for substatements that can be referenced in the main diagram by number or name. Here too, it is important to remember that expanded structure blocks and their simplest statement forms must accept and produce the identical information and results.

4.3 Sequential Structures

In the examples presented so far, expressions are evaluated as they are passed to the interpreter. A series of expressions can be evaluated one after another one as in case of functions. This is a purely sequential control.

Expressions can be gouped in LISP in a function or using the progn function. The latter is often useful in if-then-else constructs when more than one expression needs to be specified (see below).

(progn
expression_1
expression_2
...
)

4.4 Selective Structures

In situations where we need to perform different actions depending on the context, a conditional control structure is needed. In LISP this can be expressed using the if-then-else construct.

4.4.1 if

(if logical_expression
do_expression
)

Using progn, the single do_statement can be replaced by a block of statements:

(if logical_expression
(progn
expressions
)
)

Examples:

(if (< 0 1.5) "yes") => "yes"

(if (= 2.5 3.5) "yes") => nil

(defun test_if (input)
(if (= input 0)
(progn
(princ "First, do this.\n")
(princ "Then, do that.")
(princ)
)
)
)

4.4.2 if (else)

(if logical_expression
then_expression
else_expression
)

Examples:

(if (= 1 2) "yes" "no") =>

Condition: If a user enters 0 then quit the program, else print a message.

(defun test_if_else (user_input)
(if (= user_input 0)
(progn
(princ "Do THEN action.\n")
(princ "QUIT the program!")
(princ))
(princ "Do ELSE action.\n"))
)

(test_if_else 0) =>

(test_if_else 999) =>

4.4.3 cond

If there are a number of conditions and corresponding actions which need to be expressed, a more useful form of conditional construct is cond:

(cond (logical_expression_1
do_expression_1)
(logical_expression_2
do_expression_2)
...
(t do_expression_n))

The LISP interpreter evaluates the condition part of each statement one after another. Only if an expression is found to be true, is the corresponding THEN part evaluated. Once a true condition and its action are completed, LISP exits the cond function without testing any remaining conditions. Note the last condition which uses only t; this means that if no other condition in previous statements evaluated as true, only then do this action. It is a good practice to include such a t condition.

(defun test_cond (user_input)
(cond ((= user_input 0)
(princ "Zero\n"))
((= user_input 1)
(princ "One\n"))
((= user_input 2)
(princ "Two\n"))
(t (princ "Everything else!\n")))
)

(test_cond 0) =>

(test_cond 1) =>

(test_cond -1) =>

4.5 Exercise 4 - Selective Structures

In this exercise, you will make use of conditional expressions to write a small interactive program. In order to get you started, a sample program is provided in which you will also see how to use three AutoLisp functions: getint, getreal, getpoint. These functions allow you to get input from a user which may be an integer or a floating-point number entered from the keyboard, or a point which may be entered as (x y z) coordinates from the keyboard or picked directly on screen with the mouse.

Your task is to write a program which does the following. It prompts the user to input four points defining the rectangular outline of a building. Next, it prompts the user to specify a height for the building. Furthermore, the user should be able to select a roof type where the choices are signified by an integer 1, 2 or 3. These choices correspond to the roof types shown in the following figure. When a gable or hip roof is selected, you will need to prompt the user for the height of the roof. You may assume the value of any other variable you need in order to specify the geometry of the roof. The walls and roof of the resulting building should be drawn on separate layers. The fourth roof type, Mansard, is optional.

You should write small functions first, testing various parts of what your program needs to do. Once you have them working properly, combine them into a final version. Submit your program together with a snapshot of a successful generation.