[Courses] [Basics] Lesson #2: Data (II) - Variables, Lists, and a Loop

Sonja Krause-Harder skh at gmx.com
Sun Apr 7 17:30:39 EST 2002


On Sat, Apr 06, 2002 at 09:29:31AM -0800, Dave North wrote:
> Sonja, thanks for the fun introduction to tcl (I've been thinking about
> playing with it for some time, but didn't, and your introduction is
> really a great way to get started!

I'm glad that you like it (even if it is _still_ not intended as
a Tcl class ;-) - maybe I introduce a new language every 10 lessons just
to make you think about general concepts instead of syntax and keep you
awake... still, we need something to start with - so, yes, this is also
an introduction to Tcl. And it's good to hear that it is fun.)

> 	However, I get a bit of weird behavior when I try the following:

It may seem weird at first glance, but it is logical when you consider what
really happens. First, let's look at how the Tcl interpreter handles things
(complete coverage can be found, again, in the Tcl man page):

1. In the beginning, everything is one chunk of text. This text is split
   up at newline (and ";") characters into commands. So, every line in
   your script is a command.

2. Next, every command that starts with a "#" is thrown away. These are
   comments, meant for humans, and of no use for the interpreter.
   Empty lines are thrown away as well.

3. The remaining lines - the commands - are then split up at whitespace
   characters (spaces and tabs) into words. If a word starts with a
   double quote character, everything up to the next double quote belongs
   to this word. If a word starts with a "{", everything up to the first
   occurrence of "}" belongs to this word. So, both double quotes and
   curly brackets are used to group several words together into one that
   would otherwise be split up at whitespace characters.

   Grouping with double quotes and curly braces even overrides the rule
   of "one command per line", i.e. you could define a list as follows:

	set names {
		"Peter"
		"Paul"
		"Mary"
	}

	The characters used for grouping, i.e. the outermost pair of quotes
	or brackets, are removed from the word. If there are nested grouping
	characters between the outermost ones, they are treated as ordinary
	characters and left alone. So, until now, our list is just a string
	that still has its whitespace characters and is waiting to be split
	up into pieces.

4. The first word in the command is taken to be the command name. If
   it is known to Tcl, it is called and given the remaining words on
   that line as parameters. If it is unknown, the interpreter gives you
   an error, complaining about an "invalid command name".

5. It is now up to the command whether to further split up its argument
   at whitespaces or not. If it does, remember that Tcl stripped the
   outermost grouping characters before passing on the argument to the
   command. So, if our list is given to a command that cares about
   lists, it is now split up at whitespace characters, and any grouping
   characters are taken away from the list elements. 'foreach' does care
   about lists; 'puts' doesn't, to be honest.

Two questions may arise now:

1. What is the difference between lists and commands (list elements
   and "words" as used above)?

   None, actually. A command is a list that happens to have a command
   name as its first element. The words of a command are the elements
   of that list. How these lists have to look like to be understood
   by the interpreter is specified in the "Synopsis" section of the
   man page to each command name.

2. What is the difference between curly brackets and double quotes?

   While both are used as grouping characters, "{" and "}" will cause
   even "$", "[" and "]" to be treated as ordinary characters. No
   variable substitution will take place, and nested commands within
   "[" and "]" won't be executed (the lessons didn't cover nested
   commands yet). Within double quotes, these substitutions do take
   place. Consider these lines ("to consider" here includes putting
   the example into a script and running it):

	set foo "Hello"

	puts "The length of $foo is [string length $foo] characters."
	puts {The length of $foo is [string length $foo] characters.}

(Re-reading sections [1] - [5] of the Tcl man page now might help
further in understanding what is going on. Once you've grasped
how to construct and mentally navigate through nested lists, Tcl
syntax is pretty trivial.)

> > 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"}
> 
> Here's what comes out:
> % puts "Dog"
> Dog
> % set animal "Dog"
> Dog
> % puts $animal
> Dog
> % set animals {"Dog" "Cat"}
> "Dog" "Cat"
> 
> Note that puts prints the quotes!
> 
> % puts $animals
> "Dog" "Cat"
> 
> ...and again. 

(Note to the others: what we see here is tclsh in interactive mode. You can
just type

	$ tclsh

and enter tcl commands at the tclsh prompt, as seen above. In this mode, the
command 'set' echoes the value the variable was set to directly to the
screen, so the output above looks different from what you see when you just
use executable files.

I did not tell you about interactive mode because the Tcl interpreter
behaves quite differently in this mode, and because tclsh then can also 
act as command shell - as an example, you can just type 'ls' at the 
prompt and get your directory listing in tclsh. Used in a script, 'ls' 
is not known and you get an error from the interpreter. I think this 
is a possible source of unnecessary confusion now, so when this sounds 
complicated to you, don't worry. If you are curious, however, don't 
hesitate to experiment with tclsh in interactive mode.)


>So, just for the heck of it, I got rid of the quotes:
> 
> % set animals {Dog Cat}
> Dog Cat
> % puts $animals
> Dog Cat
> % foreach creature $animals {
>         puts "I have a $creature.\n"
> }
> I have a Dog.
> 
> I have a Cat.
> 
> %

However...:

% set unquoted_animals {Dog Cat}
Dog Cat
% set quoted_animals {"Dog" "Cat"}
"Dog" "Cat"
% puts $unquoted_animals
Dog Cat
% puts $quoted_animals
"Dog" "Cat"
% foreach creature $unquoted_animals {
	puts "I have a $creature."
}	
I have a Dog.
I have a Cat.
% foreach creature $quoted_animals {
	puts "I have a $creature."
}
I have a Dog.
I have a Cat.
% 

See the point? As soon as 'foreach' gets its hands on the list, it doesn't
matter whether you used quotes or not. Try it with

	set bracketed_animals {{Dog} {Cat}}

Does it matter to 'foreach'?

> Okay, that looks a little more like what I would have expected. Is my
> interpreter doing strange things? 

Well, many people will state that Tcl always does strange things ;-) 
I hope that my rather lengthy explanation above made it a bit clearer 
to understand.  And no, your interpreter is behaving as it should.

> Or do you not need quotes when assigning a list in curly braces?

In fact, you never need quotes when your list elements don't contain
spaces. It is just easier to spot string data in your code when it is
always enclosed in quotes, and makes the mental switch to other
languages easier.

Apart from that, you will need to use grouping characters when your
dog is a border collie and your cat a Maine Coon... (If you don't see
immediately why, write a foreach loop that gives out these lines:

	I have a border collie.
	I have a Maine Coon.

)

Did this answer your questions?

kind regards,

Sonja



More information about the Courses mailing list