Learning a programming language is similar to learning a natural language such as English. Although the "vocabulary" of a programming language is usually less than 100 words, there are many rules that govern the correct formation of sentences or "statements" in the language. This "grammar" of a programming language is quite complex, and the grammatical rules are very detailed and precise. During natural language conversation you can get away with considerable ambiguity or grammatical inaccuracies and still be understood. Not so with programming languages. The myriad rules which dictate what constitutes a correct program statement must be followed with unerring accuracy.

Anyone who has learned to speak a second language will agree that the best way to gain fluency is to visit a country where that language is spoken. By surrounding yourself with others who are speaking and listening in that language you are creating a situation that offers abundant feedback for learning. By conversing in a new language on a daily basis you get lots of practice and receive lots of feedback about your attempts.

In the same way, you can best learn a programming language by creating a situation that offers lots of feedback. Fortunately, the computer itself offers such a situation, since its interactive nature provides lots of feedback about our attempts to "speak" this new language. You could try to learn all the rules for formulating correct program statements solely from the book, but that would be like trying to learn correct English grammar from a book without ever speaking or listening to the language.


The Empirical Method

As you take advantage of the computer's interactive nature you are engaging in the same empirical methods that are relied on by all sciences. The way you learn a programming language is analogous to a scientist studying the natural world. A physicist, for example, performs "experiments" by dropping balls in an attempt to understand how gravity works. The physicist is gathering evidence about some physical phenomenon in order to gain insight into the underlying physical principles which govern the behavior being observed.

In the case of studying a programming language, the phenomenon being observed is a computer program. When executed on the computer this program has certain externally observable behaviors. By systematically experimenting with the program, gathering evidence about its behavior, you can construct inferences about the principles or rules which dictate how it operates.

Eventually the scientist will conceptualize his or her observations into a theory from which one can make predictions about the behavior of the phenomenon in different situations. Similarly, as you are "discovering" how the programming language behaves, your understanding of the language can be measured by the degree to which you can make accurate predictions about the way the language features operate in different circumstances. Eventually this will enable you to construct programs of your own design.


Lab Notebook

A scientist working in a lab proceeds in an organized, systematic fashion, making exhaustive notes of his or her procedures and observations. The reasons for this are twofold. First, when exploring an unknown area, one never knows which details may be significant, so it's important to record everything. Secondly, detailed documentation of the procedures followed and results obtained is necessary to enable others to replicate the scientist's findings.

Similarly for your explorations, proceeding in a careful, organized manner is the key to being effective. If your work habits are sloppy, unorganized, or haphazard, your work won't make any sense and trying to arrive at any meaningful understanding will be difficult. The unrelenting precision and accuracy required by the computer demands from us an uncommon degree of care and attention to detail. Without a systematic approach, tiny but significant details of the programming language may elude you and lead to misunderstanding and confusion.

Although you may not have to share your findings with other researchers, good lab notes become a valuable reference to which you can return when you encounter similar situations in the future. All too often you will find yourself staring at an obscure error message from the computer that seems familiar, but you can't recall what it means. If you have kept good lab notes, then it's a simple matter to look up how you dealt with that problem at an earlier time. If you end up seeking outside assistance with computer problems, having a detailed record of your difficulties will make it much easier for an expert to diagnose your problems.

It is strongly recommended that you use a notebook to record your explorations. Use a cloth bound or spiral bound notebook to record everything you do at the computer. Write legibly and include enough detail that another student reading your notes could replicate your experiment. For each experiment you undertake, document the purpose of the experiment, the expected results, the step by step procedures that were followed, the actual results you observed, and the conclusions -- what you learned. (You might also keep track of dates and time spent as an aid in time management). Several lab notebook examples are included at the end of this chapter.

At first you may feel reluctant to keep a lab notebooks, as it seems like tedious busywork. However, as you learn how "picky" and demanding the computer is, if you make the effort to keep a good notebook you will find it enormously helpful.

In the laboratory, it is said "there are no mistakes, only surprises." Every thing you do on the computer is a chance to learn something, regardless of whether or not it turns out as you expected. So don't try to make your lab notes look like everything worked perfectly. Lab notes are not intended for publication, they are the place where you document your learnings. It's important that you clearly explain every "surprise" you encounter and what you learned from it. Eventually all these little insights will start to accumulate into a coherent understanding of the computer and the software controlling it.


Constructing Experiments

The most effective way to learn programming is to get your hands on the computer, practice "speaking" this new language, and observe the feedback you get. If you are taking a formal course in programming, the instructor will no doubt provide you with exercises and activities to perform on the computer as a way of giving you hands-on practice. Some textbooks are published with an accompanying laboratory "workbook." If such a workbook is available to you, undertaking the suggested activities would be a good place to begin your lab explorations. The workbook activities are usually very well structured and you simply work through them in a cookbook fashion.

But if you are an independent learner, you may not have the luxury (or burden, depending on your viewpoint) of instructor assigned activities. You may not have a workbook with already prepared activities for you to perform. Without these externally prescribed guidelines for proceeding, where do you begin?

Perhaps the best approach to exploring a programming language is to take advantage of sample programs provided in a textbook. Most textbooks, tutorials, and other programming handbooks present numerous example programs to illustrate the major language features. These examples alone, even without the accompanying explanations, are a valuable resource for learning.

Unfortunately, the textbook doesn't explain how to take advantage of these sample programs. The examples are presented as though just by reading them you will gain the necessary understanding. What the textbook doesn't say is that you should interact with these examples. Reading them isn't enough. In order to truly understand these examples, you need to enter them in to the computer and explore them.

What is being suggested is an open ended kind of inquiry, not a rigid method or procedure. There is no right or wrong way to proceed. Just as many a curious child has pulled the back off a new toy to try to see how it works, the textbook examples should be seen not as "tablets of wisdom," but as toys begging to be taken apart and experimented with.

Unfortunately, this kind of playful exploration requires an inquisitive frame of mind that most school experiences do not encourage. After a dozen years of public school your curiosity may have retreated into a back closet in your mind. So the challenge of proceeding independently to explore the computer may ask you to dredge up old strategies you have not used in a long time.

Another mind-numbing characteristic of formal schooling is the reduction of complex ideas into simple multiple choice and fill-in-the-blank questions. You may have become adept at figuring out how to succeed on tests, yet still often feel unsure that you grasped the entire picture. Writing a computer program, on the other hand, is a very holistic, creative endeavor, and the fragmented approach to understanding doesn't work very well. You may have to abandon your habitual "plug and chug" approach to problem solving.

The next section will present some specific exploration strategies to help you get in the frame of mind for interacting with the textbook examples. You can use these strategies as you construct your own experiments. These guidelines will not tell you exactly what to do, instead they describe strategies and tactics. It's up to you to create the actual experiments that you carry out for each new topic you investigate. For each program that you explore, you will need to consider what important new language features or techniques are being illustrated and build an experiment which will focus on the behavior of particular interest.

This may be a new challenge for you since most teachers don't require students to invent their own assignments. But with programming languages, an essential learning skill is to be able to design your own experiments to learn about the rules of the language that you don't understand. The guidelines below provide some strategies that you can use, but applying these strategies will require some creativity and imagination on your part, as each program you encounter will be different. Your own natural curiosity and inquisitiveness will usually be all the spark you need to get your creative juices flowing.



Typing example programs.

The first very simple, yet surprisingly helpful activity, is to enter a program into the computer by typing from a source listing in the textbook. Especially when you are just beginning, you will discover that there are very precise punctuation rules for the language you are studying. Every symbol must appear in exactly the correct relationship to other symbols. There is no better way to familiarize yourself with the tiny details of programming syntax than by sitting at the keyboard and reproducing a program exactly as it is shown in a textbook example.

Select any of the early examples from the textbook, enter it into the computer with a text editor, then compile it. (Refer to section X.X). If you have made no mistakes, the program should compile without errors. If you made a typing mistake, or didn't accurately copy the code from the textbook, then compile errors may result. You should be able to resolve these errors easily enough by double-checking your work, comparing what you typed against the textbook.

After you have typed your first few programs, you will start to feel comfortable with the punctuation rules for the language, and typing programs by hand will become less worthwhile. Most textbooks provide a diskette which contains the source code to the example programs. If such a diskette is available to you, it will save you from manually typing in all the examples. If you don't have a source diskette, you might contact the instructor to find out if one is available. Recently some textbook authors have also begun to make the textbook examples available over the Internet.


Running example programs.

Select a program that is an interesting example of the topic you are studying. If your text includes "case studies" these often make excellent examples. The case study usually contains a partial or complete sample execution of the program. Read the case study to determine what the program is supposed to accomplish. Try to locate a sample execution that shows what input data is provided to the program, and what the output should look like.

Then compile the actual source code and run it. Enter the same input data as given in the sample run in the textbook. Obviously, the expectation is that your execution should yield the same results.

If the program doesn't compile then perhaps you have not followed the proper procedure. Double check that you are using the proper commands. (Refer to section X.X) It's also possible that the source code from the diskette differs in some small way from what is printed in the text. Scrutinize the code and compare it to the text. Often you can discover the discrepancy yourself.

If the execution doesn't yield the same results as the book, perhaps you entered different input data. Again, it's possible that the textbook could have a typographical error, or that the source code distributed for the example programs is not exactly the same as what is printed in the textbook. If you can't isolate the discrepancy easily, you might just skip it and try a different example.

If you are still unsuccessful after carefully reviewing your resources, you may need to enlist outside assistance from some knowledgeable person.

Testing example programs.

The next step in learning about an example program is to study how it behaves under different circumstances. By observing how it responds to various input conditions, you can gain a better understanding of what the program can accomplish as well as its limitations.

In the previous exploration, you succeeded in running the program using input data provided in the textbook. Now you are going to carry out your own testing on the program. First you must have a clear understanding of what the program is INTENDED to accomplish. Read the textbook explanation again and be sure to have a clear picture in your mind of the program's purpose. Next, determine what kind of input data is required and what outputs will be produced. Write a "test plan" in your lab notebook, by inventing appropriate input data items, and manually calculating the expected results. Construct as many test cases as you can think of to create a wide variety of input conditions. You may want to use a table format as in the attached examples, to record the purpose of each test case, the input data and the expected output.

Then compile and execute the program code. Carry out your test plan by entering the input data and recording the actual results provided by the computer. If the actual output is different than what you predicted, note this as a "discrepancy", (perhaps marking it with a question mark).

When you have carried out all your test cases, review the results to assess if the program is working properly. Write a "testing summary" in which you comment on which tests succeeded and which failed. Attempt to explain the nature or cause of each "discrepancy" as best you can. Summarize what you learned about the program's capabilities and limitations.

If there are aspects of the program you feel need repair or improvement, you might note these as potential enhancements for future explorations.


Examining source code.

Now that you are thoroughly acquainted with the externally observable behavior of the program, you should study the source code that is responsible for producing this behavior. At first glance much of it may appear mysterious, but in fact each facet of the source code somehow contributes to the program's overall operation. Ultimately, if you have a complete understanding of the program, you should be able to explain the role each statement plays in achieving the program's outcomes.

Begin by reading the source code and see how much of it makes sense to you. You might try to write a remark in English in which you explain each statement in the code. Write a short summary of what you are able to comprehend. Don't worry if it makes little sense to you at the moment. The explorations that follow will help you sort out the pieces of the puzzle.

You might try to identify which parts of the program correspond to its observable behavior. Can you determine where the inputs are obtained and the outputs displayed? Which parts are responsible for the major calculations? Where are the control or logic structures? For now, make a list of questions about everything that is unclear or puzzling, or about which you are simply curious.



"Bebugging" means intentionally trying to introduce errors into an example program from the book. Use a text editor to make some small superficial alteration of the syntax of the language statements, such as omitting a parenthesis, changing a comma into a semicolon, misspelling a keyword, or changing the order of elements in the program. The questions you generated in the previous step may give you ideas for things to try. Compile the program and observe if an error message is generated by the compiler. Carefully record each step in your notebook, particularly the verbatim error message. Undoubtedly you will encounter these same messages in the future, and if you have them written in your lab notebook it will be much easier for you to diagnose the problem.

If no errors result, run the program and observe any change in behavior. Describe the effects in your notebook. In this activity and the ones which follow, you are attempting to make systematic changes to the code and carefully note the results. You might think of it as a kind of detective work where you are gathering clues from which you can begin to make inferences about the underlying mechanisms in the program.

Syntax changes.

"Syntax" means the way the symbols are put together and relate to each other in a statement. It refers to spelling, punctuation, and other aspects of the form of the statements, not their underlying meaning. As you begin to grapple with understanding the workings of the program, the most obvious issues to explore are the syntactic aspects of the language feature you are studying. Usually programming languages don't allow much deviation from the rules for correct statements. Almost any variation from the precise rules will cause an error, as you discovered during "bebugging" experiments. However, it is sometimes possible to alter the syntax so that you DON'T cause an error. An important first step then is to determine which kind of syntax changes are legal and which are not.

Think of a way to experiment with the statement syntax, changing it in some way, yet still producing the same result. The intent is to find some different yet still correct way to produce the desired result. For example,

Sum := Count + 2 * Last;

should give the same results as

Sum := Count + Last * 2;


FOR Count in 1 .. 10 LOOP

will repeat 10 times, and so will

FOR Counter in 21 .. 30 LOOP

Another example:

PRINT "How now "

PRINT "brown cow."

will produce the same output as

PRINT "How now brown cow."

Often your experiment might involve removing an entire line of code. A shortcut for doing this is to turn the line of code into a comment, by using the appropriate punctuation. For example, in Ada, putting two dashes at the beginning of a line indicates the rest of the line is not to be read by the compiler. Later, you can easily delete the dashes to restore the code to its original form.

Semantic changes.

"Semantics" refers to the underlying meaning of a statement. In English communication, humans are usually able to infer the underlying meaning even if the syntax is wrong. You can usually overlook spelling and punctuation errors and still understand the gist of the sentence. Compilers insist that the syntax be correct.

In English one can create sentences that are grammatically correct but whose meaning is ambiguous. For example, "Would you rather have a lion chase you or a bear?" can be interpreted several different ways. It might mean that you must choose which predator to escape from, or it could be asking who the lion should pursue.

Fortunately, programming statements are interpreted only one way by the compiler. After you have learned the correct syntax rules for the language, you can advance to learning what the statements mean. Exploring the semantics of the language is how you gain understanding of the way the compiler interprets each statement, and what effect that statement has on the program's execution.

Experiment with changing a statement so that while it is still syntactically valid, it produces different results. Predict the output and then run it to observe the actual result. Be sure to record your results, as it is crucial to understand the kinds of changes that the compiler WON'T flag as errors.

For example, change

Celsius := 5 / 9 * (Fahrenheit - 32);


Celsius := 5 / 9 * Fahrenheit - 32;


One important implication of these experiments is that the compiler has no way to verify that the semantic meaning is what you intended. The second formula above is syntactically correct, and will produce a working program, but the semantics do not perform the proper temperature conversion desired. It is important to keep in mind that a program which doesn't produce any compiler errors is not necessarily correct.


Code synonyms.

"Synonymous" implies having the same meaning. In this exploration, the goal is to change the semantics of the program so that a different approach is used to produce the same results. Despite the fact that the compiler is incredibly picky about syntax, one can usually find more than one way to achieve an intended result. For example, you can sometimes change the order of statements without changing the overall result. Sometimes you can think of a shortcut, or alternate way to code the solution which gives equivalent results.

For example, in C++ you can find the square of a number by multiplying it by itself, or by using the 'pow' function from the math library.

Square = number * number;


Square = pow(number,2)

Other examples (Ada):

Volume = 3.14 * radius * radius;


Volume = (3.14 * (radius*2) ** 2) / 4;

(Note that code synonyms are similar to the early "syntax changes" explorations. The difference is that code synonyms use a different semantics yet arrive at the same output. If this distinction isn't clear to you, don't worry about. It doesn't really matter. As long as you are coming up with ideas for your own experiments, go for it.)


Code Modifications.

Invent some minor variation which involves changing the code (without adding any new code). You can just follow your curiosity, or refer to the list of questions you created previously. It's okay to be playful, change things for no particular purpose, just to see what happens. Try to get a sense of what changes are cosmetic or inessential and which are more significant. Some of your changes might produce syntax errors, others not. When you make a modification, record your prediction of what effect it will have on the program's compilation or execution. Then compare the actual results with your prediction.

The semantic changes you made earlier are code modifications, too, though probably narrow in scope. The idea with code modifications is to broaden your focus a little bit, and grapple with entire statements or even sequences of statements. (If that's what you've been doing already, that's fine.)

Some examples could include changing the appearance or format of the output display, or the order in which input is entered. You could change the initial value assigned to variables, or change the computations in a formula. Once you learn about control structures, you can alter the number of times that a loop iterates, or change a decision statement so it does the opposite of what is intended. You might experiment with changing the type of numeric data from integer to float.

Code modification examples:

Change: IF HoursWorked > 40 THEN ...

to : IF 40 > HoursWorked THEN ...

Change: WHILE Today /= Saturday LOOP

to : WHILE Today < Saturday LOOP

Change: READ Name, Age

to : READ Age, Name



Alternate solutions.

"There's more than one way to skin a cat." As the old maxim implies, there are usually multiple solutions to any problem. Once you start to learn about different language features, see if you can invent a different approach which accomplishes the same result. (Again, the difference between "code synonyms" and "alternate solutions" is not important. The intent here is to attempt more ambitious changes to involve different language features.) For example, use a WHILE loop instead of a FOR loop to control the number of repetitions.


Code improvements.

Invent some small improvement to the program that involves adding new statements, but doesn't affect the overall structure of the program. For now, avoid changes that would alter the underlying logic of the solution. Of course there are many possible improvements, so you should focus on experiments which help you understand the language feature being demonstrated by the example program.

Approach this task in a systematic fashion, recording in your lab notebook what you intend to accomplish, how you go about it, what problems you encountered, and what results your observed. Be sure to have a test plan, so that you will know if your solution produces the correct results.


Improve a program that asks for a person's height, to have it obtain the person's weight as well.

Improve a program that computes the area of a circle, to have it compute the volume of a sphere with the same radius.

Improve a program that determines the hottest day of the year from a list of temperatures, to also have it compute the coldest day.

Improve a program that computes a total of some values to have it also compute the average.

Improve a program to be more flexible so that values previously coded as constants can be entered by the user.

Improve a program that relies on input being lower case so that it is case insensitive.



As you start to gain confidence in your programming skills, you might undertake more sophisticated explorations.

Program enhancements.

Enhance the program so that some new functionality is added. This usually requires reworking of the logic of the solution, changing or enhancing the underlying algorithm. Be sure to plan these changes on paper first, and check your logic carefully. Next translate algorithms into program code. Create a test plan to verify that your changes are correct.


Change a program that accepts a fixed number of input data items so that the user may specify how many data items are to be entered.

Perform validity checking on input data to make sure it is the proper range.

Change a program that receives all inputs from a data file to one which interacts with the user to obtain input.

Change a program that relies on "coded" inputs (such as MTWRF for days of the week) to be able to accept the actual name, e.g. "Monday."


Enhance a program so that when the computations are completed, it asks the user if they would like to perform another run before quitting.

Enhance a program so that the user has the option of saving the results to a data file.

Convert a program which displays vertical bar charts to display them horizontally.

Add an interactive "menu" to a program so that instead of carrying out its functions in a sequential manner, the user is allowed to select which operations to perform.

Program reformulation.

To "reformulate" a program is transform it in such a way that it performs a different task. This implies a major revision. Quite likely the overall structure and logic of the program will be altered.


Reformulate a program which calculates a water utility bill so that is calculates the electric bill.

Reformulate a program that pays salesman on an hourly wage to one where pay is based on commission.

Reformulate a program which determines student grades based on a curve to one where grade is based on a straight percentage.

Reformulates a program which assists in preparing the user's federal tax forms so that is prepares the state tax forms.

Reformulate a program that looks up items in an inventory file so that it can update items in the file.


Program assembly.

When building larger programs, it is desirable to not "reinvent the wheel." When possible, it is good to take advantage of already written code. Software "reuse" is a strategy for assembling parts of different programs together to create a new program. Prewritten software components often exist in libraries for common tasks such as math functions, text manipulation, graphics, and so on.

For these explorations you might experiment with incorporating prewritten routines into your own programs. You might simply invoke functions from a library, or you might "cut and paste" code from example programs in the textbook.



When you have finished your explorations, it's important to take time to reflect on what you learned. It's helpful to write a concluding paragraph that summarizes the main points you investigated and what you learned from your explorations.

The purpose of these lab explorations is to help you master the syntax and semantic rules for correctly written programs. There isn't a required number of exercises to work, or a specific set of assigned problems to complete. Instead, these guidelines are open ended to allow you to explore whichever topics are most interesting, or to investigate ideas that you find difficult. Each example program you investigate is designed to illustrate certain language features. Your explorations should focus on the key features of the example, and you should continue experimenting until you understand the proper usage of the language feature being illustrated

(c) Copyright 2000 by Dr John Dalbey