Post Edit Home Help

Key Pages

Projects

Changes [Nov 25, 2009]

Working environment
Contents
Related work
Help
Comparison to other...
Historical notes
Larry Virden
   More Changes...
Changes [Nov 25, 2009]: Working environment, Contents, Related work, Help, ... MORE

Find Pages

Chapter 2: A classic program

We are going to make our first program and extend that to produce larger ones. This is a typical way of working: most programmers will build a "small" program first and then extend it progressively, so that it will do what they want it to do.

This first program of ours is a "Hello, world!" program, after the famous sample program that Kernighan and Ritchie, published in The C Programming Language in 1978, used to introduce their programming language called C.

In Tcl, the interpreted language that we shall use, it looks like this:

   puts "Hello, World!"

You can program this by editting a disk file (in this case with our basic editor) and then running the commands:

   % ed hello

(Show picture)

   % run hello
   Hello, World!

Or, and in this case it is much easier, type in the command directly:

   $ tclsh
   % puts "Hello, World!"
   Hello, World!

This completes our very first program. Too simple? Yes, it is not a very useful program per se, but it does show a lot of the properties you want from useful programs:

Does this mean that simple programs like this always produce the same output? No, type at the prompt:

   % clock seconds

wait a while and do it again.

The "clock" command (with the subcommand "seconds") reads the computer's clock and produces a number that represents the number of seconds since some time in the past (typically, the first of january 1970, so the number is very large!).

Is this command not predictable? It is, though the outcome is probably different each time you run it: we expect the outcome to change in a predictable way and it actually does!

Okay, so much for these very simple programs and commands. Let us move on: we want to create games and the computer should "speak" to us or our game players. In other words: we want to be able to print text like "Okay, John, what game do you want to play? Memory or Space invaders?"

We can do that in several ways:

We always prefer the latter: we can arrange it that the name of the player is set at one place in the program only and therefore changing the program to fit a new player is much easier.

How do we do that? And, even more so, what do we do? What is a variable? As explained in Chapter 1, a variable is something with a name and a value. We change the value of the variable and then some other text will be printed:

   set name "John"
   puts "Hello, $name"
   set name "Mary"
   puts "Hello, $name"

See for yourself that these four lines give:

Hello, John
Hello, Mary

as output, even though the second and fourth lines are identical!

This is because of the "magic" word "$name". When Tcl runs the line

   puts "Hello, $name"

it knows that "$name" is to be replaced by the value of the variable "name". Since that variable gets a different value on the third line, the effect of the last [puts] statement is different.

The [set] command allows you to set the value of a variable. And our original question can therefore be solved via:

   set name John
   puts "Okay, $name, what game do you want to play?"
   puts "Memory or Space invaders?"

Hm, this still requires us to change the program with every new player. Even if we have to change it in one place only, we do not want to do that! Variables are useful, but is there no other way?

[digression]

There is much and much more to say about variables. The appendix Of variables and values provides an overview (read it after you have gained some more experience with programming though - it is kind of complicated).

But here is a bit more on variables:

What happens if we type in

   puts "Hello, $stranger"

and we have not set the variable "stranger"? In Tcl, this results in an error:

can't read "stranger": no such variable

You see: a variable must have a value, just using its name somewhere is not enough.

What happens if we exchange the values of two variables like this:

   #
   # First give them values
   #
   set a "AAA"
   set b "BBB"
   #
   # Exchange the two values
   #
   set a $b
   set b $a
   puts "$a - $b"

If you run this, the result is:

BBB - BBB

Why? By setting variable "a" to the value of "$b", the original value is lost. The variable "b" then gets set to the new value of "a". Which is of course "BBB"!

The proper way to do this, is:

   set tmp $a
   set a   $b
   set b   $tmp

We use a third variable to hold the original value of "a", for when we need it. Now we can safely set "a" to the value of "b" and use the variable "tmp" to set "b". We do not need "tmp" any more after this. (Such variables are often called "temporary variables", but that only tells you something about the way they are used, they remain ordinary variables just like "a" and "b".)

[end digression]

Of course there is! We (or rather the program) can ask the player him/herself!

So, here is a program that asks the user for his or her name and then uses it to display more "personal" messages:

   puts "Hello, what is your name?"
   gets stdin name
   puts "Hello, $name"

Type the program in and run it. Now, let us analyse the program:

[digression] I have not explained as yet, what "stdin" is all about. That is actually a long story that is explained briefly in the appendix with historical notes. For all intents and purposes, just remember: stdin stands for standard input and there are two other specific names, stdout for standard output and stderr for standard error. These can be thought of as:

[end digression]

Fascinating, right? This gives us some tools to make a simple menu of games:

   Okay, John, do you want to play:
   1. Space invaders
   2. Memory
   3. Star trek
   4. Poker

   (Type "Q" to stop)
   Your choice?

Exercise 2.1.

Make a program that reads the user's name and displays the above menu.

Exercise 2.2.

What are we missing right now to make it actually useful?

Exercise 2.3. (*) -- meaning "advanced"

The program is not very flexible, comment on the things you need to change if the order of the games changes or new games are introduced. What if John or Mary wants to play more than one game?

[digression] We have been typing Tcl commands without an explanation of what is called the syntax. We will do that as we introduce new features, but here is the explanation of what we have seen so far:

Things will get a bit more complicated, so the above is not a full explanation. [end digression]

2.1 Implementing the menu

We have seen, that we can print text on the screen and we can read an answer from the user. What we can not yet do, is actually run the game that was asked for. Basically, we lack a method to decide that some piece of code must run and others should not be run. So, let me introduce the if-statement:

   if { $answer == 1 } {
      puts "Okay, starting 'Space invaders'!"
      ... code to run the space invaders game ...
   }
   if { $answer == 2 } {
      puts "Okay, starting 'Memory'!"
      ... code to run the memory game ...
   }
   ... and so on ...

We can also encounter a situation where one answer means "do this" and any other answer means "do something else". We can not go on naming all possible answers, as there are much too much possibilities. So:

   if { $answer == "yes" } {
      puts "Okay, we go on!"
   } else {
      puts "Hm, I will stop (but I'd rather go on ...)"
   }

With the if-statement we can decide to do one part or another part of the code. But in the menu example we had a bunch of games to choose from. We would have to use a whole sequence of if-statements. Is there a "simpler" way? Yes, we can also use the [switch] statement:

   switch -- $answer {
   1 {
       puts "Run 'Space Invaders'
       ... start the game ...
     }
   2 {
       puts "Run 'Memory'
       ... start the game ...
     }
   3 {
       puts "Run 'Start trek'
       ... start the game ...
     }
   4 {
       puts "Okay, Poker it is!'
       ... start the game ...
     }
   default {
       puts "Hm, I do not know that one ..."
     }
   }

The switch statement collects a bunch of pieces of code all into one decision. This also makes it easy to check that the user made a useful choice: she/he could have typed "I don't know yet" instead of the numbers 1, 2, 3 or 4, and what then?

That is what the keyword "default" is for. It takes care of the situation that "answer" is none of the above. So, here we give a message saying that we do not know that game (a bit silly, but it is often important to let the user know that he/she did something wrong - otherwise he/she might think the computer program froze or got stuck somehow.

digression The reason for our indentation! end digression

Exercise 2.4

Write a small program to write the menu and handle the answer. Also take care of wrong answers.

We still lack some way to repeat the code - you know, print the menu again and again, until the user does want to play anymore. We will look into that in the next chapter.

Note: introduce procedures here

***2.2 "Hello, world!" the "modern" way

It is all very well to be able to print "Hello, world!" on the screen and then stop the program, but that is not the way for most programs you see nowadays. Many programs use a different approach, they use a graphical user-interface (or GUI) instead of a textual user-interface. Our shell uses a graphical user-interface (see the picture)

Picture of the shell

This shows there are at least two kinds of programs. But you can recognise another difference too: many programs wait for the user to do something and then they react to that,whereas our little program just did its job and ended. The first type of of programs is called interactive - you interact with the program, the second type is called batch programs - all the actions are done in one large process, without you having to do anything.

So far, so good. But can we make our little program behave with a graphical user-interface? Sure: we just write it like this:

   package require Tk

   label  .l -text "Hello, world!"
   button .b -text Exit -command exit
   pack   .l .b -side top

Run it and see what happens!

Nothing massively spectacular: you ought to see a window appear with the text "Hello, world!" and a so-called pushbutton with the text "Exit". If you click on the button, the whole window disappears.

Picture of GUI Hello

What is spectacular, is the program itself: it does not resemble the original one at all! Yes, that is the way of graphical user-interfaces: things get more complicated and that leads to more lines of code.

Let us analyse this (still) little program in detail:

Now if you have paid attention, you will notice that the program does not finish adter this last statement. Our first version did, so why does this one continue? Well, that has everything to do with the fact that we want a graphical user-interface: very sneakily, using Tk our working environment has started a so-called event-loop. This allows the program to set up the widgets and the window (a devilishly complicated task that is luckily completely hidden from us) and to wait for the user to push the button.

Perhaps we ought to compare our two "Hello, world!" programs with a situation youre surely know:

Suppose you are doing your homework, reading the newspaper, knitting a shawl or whatever - anyway, you are completely concentrating on one job ... and then the phone rings! What do you do? You can ignore it and just go on with your job. You can also pick up the phone and answer to whoever it is on the other side.

The first approach is the approach taken by our first program. Unless the computer is switched off or something, it will ignore signals from the outside world and go on with its job.

The second approach comes closer to the second program: interactive programs usually spend as little time as possible in some task (drawing a picture, say) and then turn to listen to the user again: wait for their commands, almost like a young dog eagerly waiting for the stick to be thrown.

2.3 What is happening?

Warning: this is a tough part. You may want to skip this on first reading. Do not worry, you should be able to get through with a bit more experience later.

Now that we have seen a few very simple programs, let us try something more exciting. Have a look at the code below:

   1  #
   2. # Procedure for updating the label
   3. #
   4. proc tick {} {
   5.    global time_label
   6.    set time_label [datetime]
   7.    set timeofday  [tod]
   8.    if { $timeofday == 1700 } {
   9.       msg "Time to go home!"
  10.    }
  11.    after 60000 tick
  12. }
  13.
  14. #
  15. # Set up the window
  16. #
  17. wm withdraw .
  18. toplevel .w
  19. label    .w.label -textvariable time_label
  20. pack     .w.label -fill both
  21.
  22. after 100 tick
  23. vwait forever

The lines have been numbered, so we can easily refer to them.

To get an idea of what this program is doing, you best put it in a little file and run it. (Well, for your convenience, it is already there: clock.tcl)

Yes, it brings up a window with the date and the time. Then after one minute, the time is updated. Now let us see how this program works:

Lines 1-3 and 14-16 are comments (the # character tells you so), that is, lines that are ignored when the program runs. Comments are useful for the programmer or someone who wants to understand the code. The first three introduce the line with "proc tick {} {" - it is the first line of a procedure, called "tick".

Because the name of the procedure is followed by braces (curly brackets) with nothing in between, the procedure "tick" has no arguments. The opening brace indicates the start of the actual code and must be matched with a closing brace (line 12).

Let us first explain the lines 17-20:

Line 21 uses the [after] command. This command takes two arguments:

The line means: call the procedure "tick" after 100 milliseconds (or 0.1 seconds). Calling "tick" with some delay means that the program gets the opportunity to create the window and the label first.

Then, the [vwait] command forces the program to go in a loop - it will wait until the variable "forever" is set (which is never) but meanwhile it handles all the other events as well, such as when the procedure "tick" is called because the alarm clock started by [after] goes off.

Now, the procedure "tick" itself:

  - the "time_label" variable must keep its value and if we had not said
    it is global, the procedure would use a local variable instead and
    that would disappear when the procedure finishes.
  - the label widget uses a global variable too, so with this statement
    we make sure that the procedure "tick" is updating the variable that
    the label widget looks at.
     Complicated? Yes and no. We will come back to this issue.
  - first the command in the square brackets is executed and its result
    replaces that entire command including the square brackets.
  - then the resulting command is executed.
  - So in steps:
       Code:     set time_label datetime
       Run:      datetime (returns, say, "2003/08/13 09:23")
       Replaced: set time_label "2003/08/13 09:23"
       Run:      set time_label
       Result:   variable "time_label" has the value "2003/08/13 09:23"

The nett effect: we have created a program that runs "in the background" (well as long the shell is active), because we can continue with other things.

Note:

The name of a window or widget must start with a dot (.). This is a strict convention in our programming language. Each window/widget also acts as a new command, so that we can do things with it. In this example, we do not need that.

Exercise 2.4

Add a few other messages, at various times of the day, like: time for lunch or a coffee break.

Exercise 2.5

One problem you may note is that the clock is not showing the same time as other clocks on your system. Of course, the 60 seconds are a bit long. Suppose you reduced the delay to 10 seconds, what would be the effect? What can you say about the messages?

Exercise 2.6

Enter the following commands at the prompt:

   toplevel .new
   entry    .new.entry  -variabel x
   button   .new.button -text "Hello" -command {puts "Hello, $x}
   pack     .new.entry .new.button -side top

Change the text in the entry field and press the button. What do you see?


Posted at Oct 22/2003 10:17 AM:
Scott Beasley: The Hello World program was in the 1978 K&R "White book" as well. :-)


Posted at Oct 22/2003 10:26 AM:
Scott Beasley: Do you want to have a chapter or at least a section on GUI's vs console with some GUI history, and GUI's the tickle way? With examples like a Hello Tickle program with a button and a entry field and others. Do this before you get in to deep with Tk.

New Page - Edit this Page - Attach File - Add Image - References - Print
Page last modified: Tue Aug 09/2005 04:43
You must signin to post comments.
Site Home > Young programmers project > A classic program