However, this doesn't - endless loop after printing the content of file.
set ch [open test.tcl]
while [expr ![eof $ch]] {puts [gets $ch]}
As far as I can see, these are logically TCL equivalent but obviously not.
Why?
> I'm new to TCL so, please be kind... I'm learning from reference document.
Which one, if I may ask?
> I'm having problem with something simple - a script to read contents of
> file:
> This works:
> set ch [open test.tcl]
> while {![eof $ch]} {puts [gets $ch]}
Here, the condition is quoted, so it's passed to the while command to
evaluate it.
Even better, if you don't have an enourmous file:
set ch [open testfile]
puts [read $ch]
> However, this doesn't - endless loop after printing the content of file.
> set ch [open test.tcl]
> while [expr ![eof $ch]] {puts [gets $ch]}
Because the expr is evaluated before it's even passed to while in this
case, not being 'protected' by the {}. So the while command actually
"looks like" the while true line below.
while [expr ![eof $ch]] {puts [gets $ch]}
| |
| __________/
| /
while true {puts ...}
(if you use a fixed width font at least:-)
Does that help?
--
David N. Welton
Personal: http://www.dedasys.com/davidw/
Apache Tcl: http://tcl.apache.org/
Free Software: http://www.dedasys.com/freesoftware/
Linux Incompatibility List: http://www.leenooks.com/
What's the relationship between lists and strings inTCL?
I wrote this:
set zz "alex was here"
to me, this isn't a list because there's only one "word", yet, when I
perform
foreach i $zz {puts $i}
I get:
alex
was
here
so, a single "word" is now a list?
When you call foreach with that 'word', it gets treated like (turned
into) a list.
Or is that all strings are lists and spaces are element delimiters?
Relationship between strings and everything else in Tcl is quite simple
- everything is string. More precisely, everything has string
representation, which should be alone sufficient to reconstruct the
object (may be using some internal state of interpreter, such as channel
handles need).
So, numbers are strings, and lists are strings.
But non every string is list, as well as not every string is number.
If string can be broken into number of tokens using Tcl syntax rules, it
is a proper list. Otherwise it is not.
(One should never assume, that arbitrary string read from user (or some
user-supplied file, unless we are sure that some Tcl application wrote
correct string representation of some list there) is a list.
Try to run foreach for instance on string
"some string with unbalanced \{"
or "some string with \"quotes and\"such"
and it would give you an error message.
In your line:
set zz "alex was here"
there were three words. The third word just had sub-words if you will,
and it is these sub-words that are the words of the list. :^)
If it looks like a list, it is a list. It might simultaneously be many
other things too (in this case, it's also a string - because *absolutely
everything* can be a string - and could even be a valid script if you
happen to have a command called 'alex' defined.) Unlike many other
languages, types in Tcl are more a matter of convention than strict rules.
If you're going to go back and forth between lists and strings a lot,
you probably want to look at the [split] and [join] commands. These help
stop you getting tripped up by the fact that not all strings are lists,
and other such stuff.
PS, it's good to see that you're trying these things out in a shell and
asking questions here on c.l.t; these are two of the very best ways of
learning.
Donal.
Yes. Lists are specially formatted strings.
> Or is that all strings are lists and spaces are element delimiters?
No. Here's a string (in quotes) that isn't a list: "a \{b c"
Donal.
No, not always. The general rule is "all lists are strings but not all
strings are lists". It is possible to construct strings that cannot be
converted into lists (at least, not through tcl's automatic
list-to-string conversion facilities).
> Or is that all strings are lists and spaces are element delimiters?
Spaces are treated as element delimiters in a normal case. If you have a
well formed list where one of the elements has a space in it, then
obviously (?) that space won't be treated as a delimiter then.
The best rule of thumb is to never use list commands on strings. You can
often get lucky but it's best not to rely on that luck. Better to
explicitly convert a string to a list unless you know for a fact that a
string is a well formed list.
Once you get through all of the other replies in this thread, take a
look at the wiki page on "shimmering." <http://wiki.tcl.tk/shimmer>
Based on which command you use to access a variable, you will access
different internal representations. e.g. switching between the "fast"
and the string internal representation inside of a loop may bog down
your script.
--
sheila
This has prompted me to ask something I have put off for some time:
Is there an efficient command for testing equality of whole lists?
I have been using [string equal] which, despite the shimmering,
is much faster than element-by-element comparison in a [foreach]
loop. Also, [string equal] is inappropriate for lists of numbers.
I could really go for a comparison that compares each element
based on its internal representation (if both numeric, then uses
numeric comparison; else string comparison).
Donald Arseneau as...@triumf.ca
Good question. I don't have a good answer though. Instead, I'd start by
focussing on the issue of what equality of lists actually means. After
all, you want to have the right type of equality, yes? :^)
> I have been using [string equal] which, despite the shimmering,
> is much faster than element-by-element comparison in a [foreach]
> loop. Also, [string equal] is inappropriate for lists of numbers.
>
> I could really go for a comparison that compares each element
> based on its internal representation (if both numeric, then uses
> numeric comparison; else string comparison).
And it seems you're taking the same sort of approach that I do. :^)
If you have lists of strings, it might be fastest to do one of these:
expr {[lrange $list1 0 end] eq [lrange $list2 0 end]}
(and you can optimize that quite a lot if one list is relatively static,
which is often the case.)
Otherwise you have to do an element-wise comparison. For example, here's
a basic comparator for numeric lists (ignoring float-comparison issues):
proc numlisteq {list1 list2} {
# Do this check first; it's fast!
if {[llength $list1] != [llength $list2]} {
return 0
}
foreach i1 $list1 i2 $list2 {
if {$i1 != $i2} {return 0}
}
return 1
}
For general numeric values, that's probably optimal. (And no, this isn't
simple to solve in other languages either. Well, not unless you're lucky
enough to be able to use someone else's pre-built solution.)
Donal.