Training, Open Source computer languages

This is page http://www.wellho.net/forum/The-Tcl- ... guage/Pulling-a-list-out-of-a-proceedure.html

Our email: info@wellho.net • Phone: 01144 1225 708225

 
For 2023 (and 2024 ...) - we are now fully retired from IT training.
We have made many, many friends over 25 years of teaching about Python, Tcl, Perl, PHP, Lua, Java, C and C++ - and MySQL, Linux and Solaris/SunOS too. Our training notes are now very much out of date, but due to upward compatability most of our examples remain operational and even relevant ad you are welcome to make us if them "as seen" and at your own risk.

Lisa and I (Graham) now live in what was our training centre in Melksham - happy to meet with former delegates here - but do check ahead before coming round. We are far from inactive - rather, enjoying the times that we are retired but still healthy enough in mind and body to be active!

I am also active in many other area and still look after a lot of web sites - you can find an index ((here))
Pulling a list out of a proceedure

Posted by selfism (selfism), 1 February 2005
If I built a list inside a proceedure like this:

/usr/bin/expect

proc load_hosts {} {
set server_list_file  [open "/home/mydir/serverlist.txt" "r"]
upvar #0 server_list
while {[ gets $server_list_file host ]} != -1 {
lappend server_list $host
}

load_hosts

foreach element server_list {
puts $element
}

How would I read that list from outside of the proceedure. Upvar and global seem to be two potential ways but I can't seem to make them work. If I understand correctly:

GLOBAL is used to access variables anywhere in the prog (i.e that are not in my local proceedure)

UPVAR is used to alias proceedure parameters so they can be accessed outside of a proceedure (i.e. in the rest of the program)  it seems like this is the one I should use but it won't let me use it

Any help would be greatly appreciated.

Posted by admin (Graham Ellis), 1 February 2005
You're pretty close but there are a few typos in your code ... perhaps that's what has been causing you so much heartache.
a) You need a #! in front of the /usr/bin/expect
b) You have a missing } for the end of the proc
c) You need a $ in front on the list name in the foreach

Here are working examples, using upvar and global in the way you thought might work, then using upvar to take in a parameter, an example that returns a list, and an example that uses uplevel.   I have tested all of these ...

Code:
#!/usr/bin/expect

proc load_hosts {} {
set server_list_file [open "serverlist.txt" "r"]
global server_list
while {[ gets $server_list_file host ] != -1} {
lappend server_list $host
}
}

load_hosts

foreach element $server_list {
puts $element
}


Code:
#!/usr/bin/expect

proc load_hosts {} {
set server_list_file [open "serverlist.txt" "r"]
upvar server_list server_list
while {[ gets $server_list_file host ] != -1} {
lappend server_list $host
}
}

load_hosts

foreach element $server_list {
puts $element
}


Recommended ... Code:
#!/usr/bin/expect

proc load_hosts {vname} {
set server_list_file [open "serverlist.txt" "r"]
upvar $vname server_list
while {[ gets $server_list_file host ] != -1} {
lappend server_list $host
}
}

load_hosts tohere

foreach element $tohere {
puts $element
}


Recommended ... Code:
#!/usr/bin/expect

proc load_hosts {} {
set server_list_file [open "serverlist.txt" "r"]
global server_list
while {[ gets $server_list_file host ] != -1} {
lappend server_list $host
}
return $server_list
}

set gotten [load_hosts]

foreach element $gotten {
puts $element
}


Code:
#!/usr/bin/expect

proc load_hosts {} {
set server_list_file [open "serverlist.txt" "r"]
while {[ gets $server_list_file host ] != -1} {
uplevel lappend server_list $host
}
}

load_hosts

foreach element $server_list {
puts $element
}


I would suggest you go with the third or fourth of these, as they allows you to uncouple the variable name used within the proc from the variable named in the main code.   The other three solutions (global, uplevel and upvar using an unchanging variable name) have their uses, but the merging of variable scopes within the proc and outside is asking for trouble as your application grows unless used with extreme care.

Posted by selfism (selfism), 1 February 2005
I am on my lunch break now and don't have time to run "me" through the paces but as soon as I get home I will attack it.

THANK YOU Graham.

I feel guilty with all of this free  expert advice. In Chicago you better have your money out before you ask for anything! Do you have a paid subscription or something so I can give back to you guys.

Thanks again I truly appreciate the effort invested into this site.

p.s. it was late last night and I made alot of minor mistakes when I retyped the code from my laptop to mydesktop and this post... but i normery pogarm gooood

Posted by admin (Graham Ellis), 1 February 2005
Glad to be of help ...

We don't have any form of subscription service ...

The board was originally to help customers who've been on our Tcl and Tk courses and have post-class questions. However, it's turned rather wider than that.  Good questions here  (and their replies) form part of our archive, which in due course gets indexed by the search engines, which drives more people along here and to our main web site .... the term networking comes to mind.

Works for us ... gets our name out there ... and helps bring along people who want to learn Tcl in our more formal classes. Many training companies complain about slow times; we don't - we get busier year by year - thanks to helpful people such as yourself seeding our web site with questions in a different way to we would have asked them.

Oh - and  global v upvar v uplevel v returned parameter is a favourite question.  I think I last answered it on a Tcl course about 7 days ago, and no doubt it'lll come up again next time.

Graham

Posted by selfism (selfism), 3 February 2005
Graham, I tested each and they all worked flawlessly

If I may, I would like to confirm my understanding of your code examples:

Example 1)
One can use "global" to make a variable defined inside a proc {} available to the rest of the program. Even if it wasn't previously defined and wasn't provided as an arg to the proc. In this case you  globalized the "server_list" variable... errr list, ok you know what I mean.

Example 2)
One can use "upvar" to alias a varname to another varname2. The varname2 alias is then available to the rest of the program. Again this applies even if the var wasn't previously defined and | or wasn't provided as an arg to the proc. In this case you "upvared" server_list to server_list hence making it available to the rest of the program.

Example 3)
One can supply an argument to a proc and that argument can just act as a container for the procs output? rather than always being a piece of information the proc manipulates? In this example we called it "tohere" but we didn't care about the value itself only the container it represented. In the proc definition it was called "vname" which is just another place holder only for the proc. We then aliased / "upvared" it to server_list which makes it available to the rest of the program. I am not really sure why the value makes its way back out to "tohear" since we upvared it to "server_list" though...

Example 4)
Once a var local to proc is upvared it can be returned to the calling program and that value set to another variable. In this case you "globalized" "server_list", then returned it. Finaly you set the result of the proceedure to dump into the "gotten".

Example 5)
I didn't know you could upleve an entire command, cool.

Posted by admin (Graham Ellis), 3 February 2005
You are pretty close on those.  I'm going to add a couple of comments that might appear pedantic so that you'll not get "caught" by subtle issues later:

First example:

Quote:
One can use "global" to make a variable defined inside a proc {} available to the rest of the program. Even if it wasn't previously defined and wasn't provided as an arg to the proc. In this case you  globalized the "server_list" variable... errr list, ok you know what I mean.


Yes.  ALL variables in your main program are considered to be in the global scope, so any global declaration in a proc lets you share a variable between the proc and the main code.   If you specifiy a variable to be global in two different procs, then in both cases it is also shaed with the main code and (as a usefule side effect) between the two procs too.
 
Second example:

Quote:
One can use "upvar" to alias a varname to another varname2. The varname2 alias is then available to the rest of the program. Again this applies even if the var wasn't previously defined and | or wasn't provided as an arg to the proc. In this case you "upvared" server_list to server_list hence making it available to the rest of the program.


Upvar shares a variable in your proc with a variable in the calling code rather than "in the rest of the program".  It's the same thing where you just have a main program calling a single proc, but as you get to having procs calling procs, this difference in specification does have an effect.  You would use global to share something everywhere and upvar to share it with just the calling code.

Upvar can take an extra parameter to specify a number of levels up the call stack to go.  I don't like this form!
 
Third example:

Quote:
One can supply an argument to a proc and that argument can just act as a container for the procs output? rather than always being a piece of information the proc manipulates? In this example we called it "tohere" but we didn't care about the value itself only the container it represented. In the proc definition it was called "vname" which is just another place holder only for the proc. We then aliased / "upvared" it to server_list which makes it available to the rest of the program. I am not really sure why the value makes its way back out to "tohear" since we upvared it to "server_list" though...  


This is the most obscure example, but also the best way if you want to pass more than one thing from a proc or if you want to pass back an array.   Yes, I know you're only passing back a list in your case (so I would use the next example as the best) but this third example is ace!

You are not passing in the variable contents - but the variable name. This is a process known as call by name that you'll see books on computer languages referred to.   The syntax is wierd in Tcl but it works very well.   Basically, you're saying that the variable in the main code who's name you pass in the vname variable is to be known as server_list within the proc.  In effect, that's both an internal name (server_list) and an external name (tohere) for the same variable.

Fourth example:

Quote:
Once a var local to proc is upvared it can be returned to the calling program and that value set to another variable. In this case you "globalized" "server_list", then returned it. Finaly you set the result of the proceedure to dump into the "gotten".


Oops - my global statement got left in from a previous example and is unnecessary and confusing.  The code should read

Code:
#!/usr/bin/expect

proc load_hosts {} {
set server_list_file [open "serverlist.txt" "r"]
while {[ gets $server_list_file host ] != -1} {
lappend server_list $host
}
return $server_list
}

set gotten [load_hosts]

foreach element $gotten {
puts $element
}


And the explanation: "Every proc yeilds a result when called, and you can set that result by using a return command as the final executable statement in the proc. The parameter you give after the return command is the value passed back, and you can access it by writing the calling command in square brackets just in the same way that you do with many built in commands, so that it's performed ahead of the rest of the line in which it is embedded."

Fifth example:

Quote:
 
I didn't know you could upleve an entire command, cool.


So cool that I'm pretty icy cold about using it.  A great way to write obfurscated code that no-one else can follow!

I've put a copy of the source code of these examples up at http://www.wellho.net/resources/T207.html. They're under references p1 to p5.

Posted by selfism (selfism), 3 February 2005
I must be a geek, the first thing I do (6:00am) is walk over to my computer connect to my favorite programming forum and see what the latest response is to proceedure variables.... If I could just figure out how to us global  or upvar on women I would have it made

Graham, thank you. You chased all the proceedural spectures away.

Your next TCL class isn't untill May huh...

Posted by admin (Graham Ellis), 4 February 2005
on 02/03/05 at 12:34:43, selfism wrote:
If I could just figure out how to us global  or upvar on women I would have it made

Your next TCL class isn't untill May huh...


The great thing about programming is that once you've written something to (let us say) a variable, it stays there.  But make a big, romantic gesture (say) this Feb 14th and it will need to be refreshed the following February 14th ....  

Tcl trainees tend to come in batches - it's the sort of language that whole workgroups need to use - so most of our Tcl and Tcl/Tk couses are private ones.  The next public course may be May (and I'm writing this in early February), but I'm running a private one this time next week.



This page is a thread posted to the opentalk forum at www.opentalk.org.uk and archived here for reference. To jump to the archive index please follow this link.

© WELL HOUSE CONSULTANTS LTD., 2024: Well House Manor • 48 Spa Road • Melksham, Wiltshire • United Kingdom • SN12 7NY
PH: 01144 1225 708225 • FAX: 01144 1225 793803 • EMAIL: info@wellho.net • WEB: http://www.wellho.net • SKYPE: wellho