[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