03 Basics - Lesson 2

After noticing that lesson #1 was rather dry, I decided to send
lesson #2 as soon as possible, to give you some more information, so
that you can start playing around with some scripts, even if they
will still be very simple and basic. In this lesson, you'll learn
about variables, a simple loop construct, and how to access the
parameters given to your script when it is invoked from the command
line.


Lesson #2: Data (II) - Variables, Lists, and Loops

Programming means (among thousand other aspects) specifying detailed
sequences of actions which, once written, can be repeatedly applied to
many different sets of data.

In this scenario, it is rather inconvenient to have the data we operate on
written as literals into our programs and scripts - the only way to run
them with different data would be to edit the files again and change
everything by hand. Luckily, we have something that can replace the
literals in our code.

Variables

A variable acts as a placeholder. It has a name and a value. The value
can be just one thing, or a whole collection of things. To set a variable
to a single value, you use the 'set' command as follows:

set variable_name "Value of the variable"
set name "Barney"

It is possible to put more than one item into a variable. To do so, Tcl
offers a construct called "lists". The assignment of a list to a variable
is also done with 'set':

set list_name {"Value 1" "Value 2" "Value 3"}
set names {"Barney" "Fred"}

A list is enclosed in curly brackets ("{" and "}"), its elements are
separated by spaces. The order of elements is preserved. When passed to a
command, everything from "{" to "}" is considered to be one parameter.

(Please note that different languages tend to make the usage of different
constructs to group related data together very easy, and others more
clumsy, if at all possible. Also, there is no consistent terminology
across languages, which can be confusing (not only) to beginners. For the
time being, please accept a list as a way to group all kinds of data
(strings, numbers, other lists) together in a specified order. However,
keep in mind that it is only one possible way of doing this.)

When you want to access the value of a variable, you use the name of
the variable with a prefixed "$":

puts $variable_name
puts $names

('puts' gladly accepts both a single and a list value as parameter. Most
Tcl commands do, and try to act as sensible as possible on the data they
receive.)

When you try to access a variable you didn't set before, you get an
error message from the interpreter.

This concept is called "variable substitution" (see

$ man Tcl

Section [7] for a detailed description), and must be explicitly
invoked with the "$" character in Tcl. In other languages, it is
implicitly decided from the context whether the name or the value of
a variable is used. The fact that Tcl syntax, in this and other cases,
forces you to decide what you actually want is one of the reasons I
think it is a very good language for beginners. (The other main one
being the very small set of commands to be learned.)


Repeated action: loops

One major reason why it is desirable to put related data into some
kind of collection is the ability to access the seperate elements
one after another and do something with them. Imagine some people we wish
to greet. Without variables, we'd have written:

puts "Hello, Fred.\n"
puts "Hello, Barney.\n"
puts "Hello, Wilma.\n"

Rather repetitive. Introducing variables with single variables doesn't
help much, but only doubles the lines of code we need:

set person_1 "Fred"
set person_2 "Barney"
set person_3 "Wilma"

puts "Hello, $person_1\n"
puts "Hello, $person_2\n"
puts "Hello, $person_3\n"

(Don't laugh, similar constructs have been sighted in the wild of
production code...)

Things get much easier if we use a list: Tcl has a special command
for doing something with each element of a list:

foreach element $listname {
# do something with $element
}

(Now, we have several new things at once that are unrelated to the
'foreach' command but need an explanation nonetheless:

1. A line with a "#" sign as first character is treated as comment
and not executed at all

2. Irrespective of my telling you that a newline means a new command
to Tcl, everything enclosed within "{" and "}" may span multiple
lines and is still considered one argument to a command. In this
case, it's a series of commands given to 'foreach' to be executed
with every element of the list.

3. The Tcl interpreter still wants this parameter to start on the same
line as the command itself, so the following code would raise an
error:

foreach element $listname
{
# do something with $element
}

This is especially important if you are used to other languages
where this usage of "{" and "}" is legal (and common). While
curly brackets are used to group things together in many languages,
Tcl is definitely very picky about where they are placed. (Also note
the space before the opening "{". Parameters are separated by spaces,
and everything from "{" to "}" is just another parameter. To Tcl.)

4. While I'm at it, note that variable substitution also takes place
within quotes, i.e. in string literals. If you ever need to use a
"$" as such in a string, you have to escape it with a backslash:

puts "This widget costs \$25"

Now, let's go on...)

In practice, 'foreach' is used as follows:

set $people {"Barney" "Fred" "Wilma" "Pebbles" "Dino"}

foreach person $people {

puts "Hello, $person!\n"

}

(I am aware that to most of you, those examples are about as practicable
as calculating Fibonacci numbers or factorials. Please bear with me, I
hope to get you out of that stage as soon as possible.)

'foreach' is a special case of loop command designed to work with lists.
It is not present in all languages. Looping over some set of data,
however (yes, "to loop over" is a real, existing verb :)), is one of the
basic methods to control the order in which actions are taken in your
program ("flow control"). There are other common commands to achieve that,
among them 'for' and 'while', which are not restricted to lists and will
be covered in a later lesson.


Getting data from the command line

You might have noticed that we still only use data from inside the
script, even if we now assign it to variables and put it into lists.

One way to get data from the outside into your script is passing it on
the command line. The program 'tclsh' containing the Tcl interpreter and
running your scripts up to now kindly provides us with a special variable
called 'argv' ("argument values") (with some help from the operating
system, this is the standard way to access command line parameters under
unixoid systems). 'argv' contains, as a list, everything that was typed
on the command line after the actual name of your script. You find details
on this in the documentation to tclsh:

$ man tclsh

or:

http://dev.scriptics.com/man/tcl8.3/UserCmd/tclsh.htm

So, you can just echo the parameters you were given with:

puts $argv

or put each argument on a single line:

foreach argument $argv {
puts "$argument\n"
}

or assume you were given a list of names, and greet each person:

foreach person $argv {
puts "Hello, $person!\n"
}

Exercises:

Again, put every example I give in the text into a runnable Tcl script
and watch it run. This shuld include a script that can be called with

$ ./greet Fred Barney Wilma

and puts out the following lines:

Hello, Fred.
Hello, Barney.
Hello, Wilma.

If you feel bored, try to break the examples by introducing syntax errors
and watch the reactions / error messages of the interpreter. Are the errors
always what you'd expect them to be?

I will continue providing direct links into the Tcl documentation if
there is considerably mor information to be found than I am able to
reproduce here. Feel free, though, to read the documentation of every
command introduced so far, on:

http://dev.scriptics.com/man/tcl8.3/

or with the 'man' command. (All tcl commands should be in section n,
so with

$ man n <commandname>

you should always get the right man page.)

-------------------------------------------------------------------------------
This text is work in progress, comments are most welcome. Redistribution for
non-commercial use is fine, as long as I know of it and this footnote is
included. Copyright Sonja Krause-Harder, 2002.