I'm trying to write up some routines for real-time display
of data and have dodgied something up. The routine below simply opens
up a file using fileevent and then executes some user code when the
data is available from the file.
Typically the code will update a vector or variable and then update
the screen. Rather than putting that into a seperate named procedure
I've done the following hack using format:
#
# ascync.tcl - provides support for asynchronous reading from files/processes
# and updating the screen.
#
proc async_open {file polltime code} {
set fd [open $file]
set s [format \
"fileevent %s readable {\n\
set async_eof \[gets %s async_data\]\n\
if {\$async_eof != -1} {\n\
%s \n\
} { \n\
after %d\n\
}\n\
}\n" \
$fd $fd $code $polltime]
eval $s
}
proc test0 {} {
async_open "xdata" 100 {
puts [format "xdata += %s" $async_data]
}
async_open "ydata" 100 {
puts [format "ydata += %s" $async_data]
}
}
Now in the TK tutorials they mention using list for this sort of thing
but I don't quite understand. I want to expand some variable
references and not others; is there a way to do this. What I would
like is something like a macro in lisp or a lambda function.
So finally:
Is the [format] method sound
How do you do it properly
--
Phil Maker Email: p...@cs.ntu.edu.au
School of I.T. Ph: +61 89 854939 (home)
Northern Territory University,Darwin Fax: +61 89 466630
Casuarina, Australia.
Hint 1: format isn't used very often in tcl. For example, instead of
puts [format "xdata += %s" $async_data]
one could just say
puts "xdata += $async_data"
Hint 2: Your technique has gotten you into quoting hell, which is always
a place to avoid. I see no reason not to execute the code in async_open
straight forwardly. The one issue is that you want to pass in some code
to async_open. But that can be executed at the proper point via
eval $code
which should do what you want. In this case, however, you'll need to
backslash quote the $ inside the code (ie. \$async_data) you pass in
since it references variables not defined at the point of the call to
async_open. (However, it strikes me that for the caller to have to know
the name of some variable local to the called program is a violation of
seperation of concerns and information hiding and generally a bad idea,
too!)
Hint 3: the command "after n" will cause the system to actually stop and
do nothing for n milliseconds. Do you want that or do you want it to go
off and do other things and come back in n milliseconds? In that case,
you may really want to use "after n script" (where script is usually a
procedure call).
--
Marc H. Graham ma...@neovision.com
VP Software Engineering 412 621 8333
NeoVision Hypersystems 412 621 8337 (fax)
5001 Baum Blvd
Pittsburgh, PA 15213
There is absolutely nothing wrong with using format. Simply stating
that you don't see it very often in Tcl programs means that you yourself
haven't bothered to find out what circumstances it might be useful in.
I use it quite often when I find myself descending into quoting hell, as
format gives you a quick and easy way out. Your mistake is using double
quotes to quote the format string. Since format works like printf,
there is no reason to want or expect to use variable substitution within
the format string, so you should always quote the format string with
{}. Then, you can remove all of those nasty \n\ constructs that you
have scattered throughout that code, since {} automatically allow
continuation across line boundaries.
So, making the changes suggested by Marc (use a procedure call on the
after), your command becomes:
eval [format {
fileevent %s readable {
set async_eof [gets %s async_data]
if {$async_eof != -1} {
%s
} {
after %d %s
}
}
} $fd $fd $code $polltime $proc]
Note that I moved the eval so it evaluates the return of the format.
There is no need to store the value of format in a variable. Note also,
that by using {} to quote the format string, the code looks much neater
and you don't have to stand on your head to get [] and $ substution at
the right time.
/Joe
Well, I love philosophical debates! What I want to know is, exactly what
are the benefits of:
> eval [format {
> fileevent %s readable {
> set async_eof [gets %s async_data]
> if {$async_eof != -1} {
> %s
> } {
> after %d %s
> }
> }
> } $fd $fd $code $polltime $proc]
>
over the much more straightforward:
fileevent $fd readable {
set async_eof [gets $fd async_data]
if {$async_eof != -1} {
eval $code
} {
after $polltime $proc
}
}
Why is this a situation in which format is useful?
for one, you have to keep those variables around for as long as you use the
socket. and since they have to be global, you could only use it on one socket
at a time. i'd prefer arrays, the fixed substitutions we're on about, or
procedures. *my* choice for this would be:
fileevent $fd readable "fuddle $fd $code $polltime $proc"
format isn't an ideal solution to quoting hell; it's very easy to lose track
of which % goes with which $. i'd like to see someone give us something akin
to lisp's backquote for this kind of situation:
eval [subst {
fileevent %fd readable {
set async_eof [gets %fd $async_data]
if {$async_eof != -1} {
%code
} {
after %polltime %proc
}
}
}]
but we need analogues to both , and ,@; we may want "nasty" characters (like
SPC) quoted, or we may not. (format doesn't handle the quoting that may be
needed - notice the trouble you'd have with
set x "hi\} \{there"
eval [format {puts {%s}} $x]
that is handled by
puts $x
(artificial but makes the point)
how many times have we wanted to create a -command with some of the variables
substituted on creation and the rest on execution?
I'm not sure how many times this has been said, but it is a lot:
A Tcl command is a _list_. Double quotes do _string_ quoting (and
format does string furtling too)
If you want to produce a Tcl command with substitutions, use the
_list_ command. For anything longer than a single command, use a
procedure as it will make your life much easier, both when writing
code and when maintaining it.
If you _must_ use more than one command in a binding, build the
commands separately and use [join $commandList :] which will do
what you want.
My favourite may of doing the example up above is:
fileevent $fd readable [list fuddle $fd $code $polltime $proc]
which will work whatever rubbish your variables contain.
Donal.
--
Donal K. Fellows http://r8h.cs.man.ac.uk:8000/ (SAY NO TO COMMERCIAL SPAMS!)
(work) fell...@cs.man.ac.uk Dept. Comp. Sci, Univ. Manchester, U.K.
| do...@ugglan.demon.co.uk 6,Randall Place, Heaton, Bradford, U.K. (home)
+-> ++44-161-275-6137 Send correspondence to my office ++44-1274-401017 <-+
>My favourite may of doing the example up above is:
>
> fileevent $fd readable [list fuddle $fd $code $polltime $proc]
d'oh! yes, that's the way *that* should be done. but list is a less
readable solution than format when nested evaluations or partial evaluations
are involved.
here's a line from one of my own programs (excuse me if it's too wide):
shortcut [button $tl.okay -command "set $var \[list okay \$fileselect(s$tl) \$fileselect(f$tl)\]"]
remembering that list quotes $ and [, a robust expression of my intent would
be something like this:
... -command "set [list $var] \[list okay \[[list set fileselect(s$tl)]\] \[set [list fileselect(f$tl)]\]\]"
at least with format, i could write:
... -command [format {set $var [list okay $fileselect(s%s) $fileselect(f%s)]} $tl $tl]
which is even more readable than my version but still doesn't cover nasty
$tl. i'd have to use
format {set $var [list okay [set %s] [set %s]]} [list fileselect(f$tl)] [list fileselect(s$tl)]
for that. now, is that more readable? does making a procedure *just* for
that simple snippet feel right? doesn't there seem a need for a cleverer
kind of subst? something where i could say, say,
... -command [clever {set $var [list okay [set fileselect(f%$tl)] [set fileselect(s%$tl)]]}]
and the substs that would usually be done are done only if preceded by %.
and what looked like a single word remained one. there'd also have to be
something that wouldn't quote strings, so you could do this sort of thing:
set cmd {puts "$x is too big"}
set val 100
... -command [clever {if {$x > %$val} { @$cmd }}]
aren't those much more readable? i'd love to have the patience to write one :)
restricted to variables, i suppose it wouldn't be too hard...
>i'd love to have the patience to write one :)
>restricted to variables, i suppose it wouldn't be too hard...
actually, i've since thrown up a prototype, and it's *easier* to do the
bracket-matching (with info complete) than it is to pick out array variables.
any suggestions?
Joe Kelsey <jo...@dgtl.com> writes:
> Marc Graham wrote:
> > Hint 1: format isn't used very often in tcl. For example, instead of
> > puts [format "xdata += %s" $async_data]
> > one could just say
> > puts "xdata += $async_data"
>
> There is absolutely nothing wrong with using format. Simply stating
> that you don't see it very often in Tcl programs means that you yourself
> haven't bothered to find out what circumstances it might be useful in.
No, don't read too much into what Marc wrote. What he
wrote is true, and useful to newbie Tcl programmers,
who don't realise the "power" of variable substitutions.
In the example above, Marc is completely correct in what
he is saying, and he is also *not* saying that format
is *never* useful.
Next time, think before typing. (:-o)
Hal
<URL:http://137.186.18.22/%7Ehclsmith/plugin.html>
(my POP just changed ISPs, and they haven't got that machine in DNS yet)