
So far, we have introduced the basics of computer hardware, the components that go to make a general purpose computer. However, these components would not be able to do anything if it were not for the instructions that allow it to perform any of several different operations. When we use pocket calculators, we can use the keys to instruct the calculator to add, multiply, etc. A computer has all those instructions available, plus many more - too many to have a key for each instruction. For that reason, the instructions must be entered in some other way.
When computers were first invented, the idea of having multiple instructions on a computer was one of its most important features. The instructions were expressed in terms of 0s and 1s, grouped into sets that could be loaded into the CPU's instruction register and executed. Each machine instruction consisted of different permutations of 0s and 1s, and each instruction could be loaded into the instruction register and executed. A second register, the program counter, was used to keep track of where to go to fetch the next instruction from main memory. This is still essentially the way that computers work today. However, there has been a change in the way that humans input instructions to computers. That evolution is the subject of this lecture.
Machine Language/ Machine Instructions - All 0s and 1s (binary). Initially, toggles on the front of the machines to enter the 1s and 0s.
Early computers (even the first microcomputers) had a series of switches on the console, grouped into sets of bits that corresponded to the number of bits in that computer's registers. A bit is recognized inside the computer as an electrical pulse that is either off (0) or on (1). Since it is easy to make circuits that can handle the on-off relationship, computers are built on the concept of binary arithmetic, based on bits. We group these bits into groups, so that they will be easier to display and manage. Instead of having a continuous string of binary numbers such as 0010101110101111, we use the convention of grouping bits in sets of 8, and call this group a byte. The number above would be displayed as 00101011 10101011. We have also seen a shorthand way of presenting these sequences that is even more convenient - octal (base 8) and hexadecimal (base 16).
The first programmers had to enter these numbers by setting switches on the front of the machine to represent the instruction they wanted to execute, then press an enter button on the computer to store the instruction in memory. They would then reset the switches for the next instruction, press the enter key and so on in order to create a program. This process was painfully slow, and it was prone to errors. Many of us have had to do this type of programming at one time or another, but none of use particularly enjoyed the process. Machine instructions are still with us, but new ways of entering data have enabled programmers to do their work more efficiently.
Assembly Languages - Each machine instruction in the instruction set for the computer's CPU, has a short English mnemonic to make it easier for people to remember, read, and write. In addition, one can use variables instead of only direct numeric addressing of data/programs.
Note: One assembly language instruction translates into exactly one machine instruction. The instructions are no more powerful, but the task of writing, reading, and debugging is much easier for people.
As you have already seen, a program can be written to perform a wide variety of tasks inside a computer. DOS is a program, quite complicated, but still just a program. All of the applications you will be using in this class are also programs. In fact, you will be writing programs yourself in the latter parts of the course. However, to write a complicated program by setting switches on the front of a computer is far too tedious to allow the millions of programs available today to be created or maintained. Some other approach should be possible. What if we were to assume that the user could use a keyboard, like that of a regular typewriter, and, by typing some codes that represented machine instructions, get the computer to store the machine instructions as before? This process is made possible by yet another type of program called an Assembler. The function of an assembler is to examine a user's line of input and translate the symbols typed by the user into the machine instructions that they are supposed to represent. Consider the following very simple assembly language program used to add the numbers 4 and 7, storing them in a specific memory location:
LOAD A,4 put the constant 4 in register A
LOAD B,7 put the constant 7 in register B
ADD A,B add the contents of register B to A
STORE 1621,A store contents of register A in memory location 1621
These instructions are read, one at a time, by the Assembler program, and converted to the equivalent machine instructions (a string of 0s and 1s). The exact code words used (LOAD, ADD, STORE, etc.) will very from one computer to another, but the general principle is the same.
Another example of an assembly language program is a program that detects a hardware bug in some Pentium CPUs. That program and documentation is available on the web at at: http://www.x86.org/secrets/Dan0411.html
An important fact to remember about assembly language instructions: each line of assembly code translates into exactly one machine instruction. It is much easier to remember code words such as LOAD and STORE than the binary machine instruction equivalent, but the programmer must still know each code word and must also write a line of code for each machine instruction that will appear in the final program.
In the example just discussed, the problem to be solved is simple, and it would not be difficult to write an assembly program to solve a problem of this type. However, suppose that we wanted to do something such as solving a quadratic equation using the formula
(-B +/- sqrt(B**2-4AC))/2A
Although the formula is not all that complicated, it contains a number of operations that cannot be readily represented by simple assembly instructions. The square root, for example, is not an assembly instruction, and it may well require an entire separate program just to obtain a square root of a single number.
Problems such as these can be solved by Assembly language programs, but they are difficult to write and hard to generalize so that they can be re-used for other similar applications. This fact, then leads us to the next level of programming.
High level languages - Most of the languages you have heard about are in this category, including C, Fortran, Scheme, and Common Lisp.
Note: One statement in a high-level programming language may translate into one or many machine instructions.
Several example Scheme programs are shown in the handout: sequence.scm (2 pages, text)
In the late 1950s, when computers were just starting to be widely used for accounting and other applications, a naval officer named Grace Hopper who was assigned to work with computer decided that it might be possible to create a higher level of language, using exactly the same approach that was used to create assembly languages. That is, she wrote a program which accepted as input a series of statements that were much more sophisticated than the assembly languages described in the previous section. A typical program statement in the language she invented might look like this:
MULTIPLY DOLLAR-AMOUNT BY 1.4 GIVING NUMBER-OF-SWISS-FRANCS
This statement, when bundled into a much longer program, would be read by a compiler, a program that converts statements in this language into machine instructions. This language was given the name COBOL, an acronym that stands for COmmon Business Oriented Language. Grace Hopper, who died recently, championed that language for nearly forty years and earned the name "Grandma COBOL" as a result.
Once COBOL had set the stage for high level, compiled languages, others followed in quick order. One of the early languages was FORTRAN, an acronym for FORmula TRANslation. A typical FORTRAN statement relating to the example given earlier would be
X1 = -B + SQRT(B**2-4*A*C) / A*2
and
X2 = -B - SQRT(B**2-4*A*C) / A*2
ForTran was designed to solve numerical computation problems, and
it is still widely used in that domain. Many other high level
languages evolved. Later in this class, we will provide a brief
overview to a few others besides the examples given here.
Fourth generation languages - A term sometimes used to describe database query languages or other specialized languages used for specific applications.
Natural Languages - Computers are not yet capable of producing or understanding human speech, or even written documents, although parts of this task have been accomplished. Natural language processing is an active area of research in the field of Artificial Intelligence.
This is a brief introduction to the different generations of programming languages that have evolved. At this point, you should remember that programming involves the execution of machine instructions, which can be generated in several different ways, the most common today being through the use of high-level languages.
We will see that some high-level languages are interpreted and others are compiled. Two figures shown in lecture illustrate the process of compiling a source program to produce an executable program (machine lanugage). The source code (programming commands) have to be submitted to a compiler which generates object code, then the object code can linked with libraries by a link program, to produce a file that can be executed. In DOS, we saw that many executable programs are characterized by file types such as .EXE, .COM, and .BIN.
Scheme is also a high-level language, but it differs in that it is an interpreted language, which means that commands can be executed as they are encountered (typed in directly or read from a stored program), without separate object code or executable files being generated. Interpreted languages are easier to learn due to the rapid response time - this immediate feedback helps you test questions and understand the important concepts necessary to program more quickly.
From this background, one can infer that a high level programming language must have commands, which will ultimately translate into machine instructions recognizable by the CPU of the computer in which they are executed. What else does a programming language need? How does it differ from a natural language? These are questions that we will explore in this and the following lectures.
There are many more programming languages in existence (hundreds) than are described above, both those in current use and those that have "fallen out of use". In addition, programming languages evolve over time, as is apparent by the different standards for the languages that have been present for many years.
The choice of which programming language to learn or to use for a particular task may not be an easy one. Which language to learn or use depends on what kinds of problems you want to solve. Each language has been designed to facilitate certain kinds of problem-solving tasks. It could take hundreds of pages of code in one language compared to only a few pages of code in another language to solve the same problem. The moral of the story: pick the language that helps you solve the problem most effectively!
The approximate number of pages in the defining standard or draft standard of common programming languages is shown in the table below.
Scheme Approx. 50 Pascal 120 C 220 Fortran 8x 300 ADA 340 BASIC 360 PL/1 420 Fortran 77 430 COBOL 810 Common Lisp over 1000
The length of a programming language's standard is an indication of the size of the language (number of different statements and data types) and of the complexity (how the language statements are executed) of the language. Larger languages can be more powerful (accomplish more with fewer instructions), but are more difficult to use and learn. Scheme has the smallest length standard of the above programming languages and contains many powerful features.
Over a thousand different programming languages have been created so far. The ones we've summarized above are of historical interest, or they have "staying power" - they have continued to fill a need and are still in wide use. There is a famous saying: "I don't know what language engineers and scientists will be using in the year 2000, but it will be called ForTran."
It will be interesting to see what happens with programming languages in the future. ForTran, Lisp, and COBOL have withstood the tests of time the longest. As programming language theory has progressed and program writing styles have improved (this is called software engineering), the features of the languages have increased and been added to the languages through the standardization process. Computer languages evolve in this fashion, somewhat similar to the evolution of natural languages.
How many of these languages will survive the next 20 years? Will Java, the newest language (its only a few years old) still be around and in use five years from now? Whatever the outcome, the programming concepts you learn while studying Scheme will give you the ability to evaluate languages for specific tasks and to learn any computer language in depth in the future, should you choose to. It will be interesting to see what happens with programming languages in the next few years and in the next century.
Sequential programming languages using the imperative programming paradigm include most programming languages you have heard of (ForTran, Pascal, Basic, Mumps, C, Scheme). In these languages, instructions are executed one after another, in sequence, until a flow control statement (selection, iteration, branching) is reached or another function is called. In these programming languages, procedures perform computations and variables are used to hold data, including sometimes complex data structures. These programs use mainly side effects to process data.
A functional programming style is also encouraged in Scheme, where most of the work is accomplished with the return values of functions via function calls as opposed to creating and using variables in sequences of code in one procedure.
Parallel programming requires special parallel programming languages as well as special parallel computers (computers with multiple CPUs). Computer architectures with multiple CPUs are a relatively recent development. Potential problems can arise, so these machines require special software to take advantage of the parallel hardware. It is possible to solve problems much faster with multiple CPUs. Some machines are known as "massively parallel" and contain hundreds or thousands of CPUs that work on one or more problems at the same time.
The primary units of programming in parallel languages are still procedures and data. Oftentimes the CPUs are computing the same operations on large sets of data in the multiple CPUs.
Object-oriented programming (OOP) modularizes code in a different way than imperative or parallel programming does. An object is a self-contained unit that contains both data and the functions that manipulate that data. This is quite different from the way procedures are separated from data in the more "traditional" programming styles described above.
Some computer languages have special features to support OOP. If the features are not built-in to a language, the same style can be coded using other language constructs, although this is more difficult by comparison. OOP has been especially useful for simulation types of problems.
Common languages with OOP support include Simula, Smalltalk, Common Lisp (includes the Common Lisp Object System or CLOS), Scheme (see below), C++, and Eiffel. The Scheme object system is called SCOOPS - Scheme Object Oriented Programming System.
Some advantages of OOP include data abstraction - the internal details of the data structures are hidden from the outside. This can make programs more reliable and robust by preventing some other procedure from "accidentally" changing data where it was not expected. Objects that are created can often be re-used in other programs or situations, making writing large programs much easier, faster, and requiring less debugging.
In OOP, objects interact by sending messages to each other. The flow of control in a program is not fixed ahead of time as is true of the three types of flow control statements we've seen in sequential languages (sequential, selection, iteration). Instead, the order that computations are carried out depends on the messages sent to the objects and the state of each object and its data. Each object may respond to messages by sending one or more messages to other objects. Objects can be related in a hierarchy. This organization can save programming time and resources by sharing common functions between related classes of objects.
Imagine needing to keep a list of items that are ordered in such a way that the last one you add is the first one that you will want to look next. The order items are added and removed is nicknamed LIFO for last-in, first-out and is also called a stack, and works like plates in a cafeteria). An alternative, and also popular ordering is called FIFO - first-in first-out, like an (idealized) grocery checkout line, also called a queue. Each can be programmed readily in Scheme and other programming languages.
A Stack In the Sequential Programming Paradigm using Scheme:
We could easily do this in Scheme using procedures and data as follows:
We see in this example that the procedures and the data are defined separately, as is the norm in imperative programming languages.
A Stack in the Object-Oriented Programming Paradigm
In object-oriented programming, we would create a class called stack with the operations implemented for creation, addition (push), removal (pop), and getting the first element (top). Each object of the stack type would then respond appropriately when it received a message involving an operation.
Both of these stacks should do the same thing, as long as they are coded properly. The difference between them is in how the code is organized and executed. Because of the modularity enforced with the designing and creation of objects, some programs can be significantly easier to develop using object-oriented programming. You may hear a great deal about object-oriented programming in the future.
Applications of Object-oriented Programming
An example of OOP is a simulation game. The players (pieces) "know" how they are allowed to move (procedures) and can have internal data to save their position on the board, for example. The board object may keep track of the location of each piece. A control procedure starts the game by creating a certain number of players. Then each player is given a turn, in the same order, until the game os over. Each piece may have a different "strategy" programmed in its procedures, to help it win.
Your instructor has programmed such a game with truck objects on a circular "track". Each truck tries to be the first one to reach a certain number of laps on the track, ahead of all other trucks. Each truck has certain "sensing" abilities it can use, like "look ahead" or it may decide to take action by moving, removing obstacles in the path (load), or placing obstacles in the path (unloading) based on its own strategy. Each programmer worked out a strategy for their own truck and we played against each other in a tournament. One of the most successful stategies turned out to be simply "go, go, go"!
Using the object-oriented style of programming, complex behavior can result from a combination of seemingly "simple" objects. Because any number of instances of each object can be created once the object has been defined, this style is particularly useful for solving problems with many similar components. Each instance of an object has the same behavior as all objects of that type, but each instance is also different due to its stored data.
Another example of OOP is a graphics program (many draw programs are implemented in this programming style). If each visible figure (circle, square, window, etc.) is an object, each one contains the operations to draw itself, re-size, or re-position itself. The user need not know or care how the object displays itself. And you can have multiple circles, squares, etc. in the same figure without explicitly duplicating all the code for the procedures and data.
Go to the index of
lectures for ECS15 - Fall 1997 .
Go to the homepage for ECS15 - Fall 1997 .
