mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-23 09:41:36 +00:00
[language/tcl-en]Extensive edit of the Tcl document (#2731)
* Rework some examples. * Small change to description of iterator commands. * Adjust whitespace. * Adjust whitespace again. * Various changes to wording and grammar * Rather extensive editing of the entire document. * trivial change of one variable name
This commit is contained in:
parent
d30d6f69ca
commit
c809a163b4
@ -1,21 +1,21 @@
|
|||||||
---
|
---
|
||||||
language: Tcl
|
language: Tcl
|
||||||
contributors:
|
contributors:
|
||||||
- ["Poor Yorick", "http://pooryorick.com/"]
|
- ["Poor Yorick", "https://pooryorick.com/"]
|
||||||
filename: learntcl.tcl
|
filename: learntcl.tcl
|
||||||
---
|
---
|
||||||
|
|
||||||
Tcl was created by [John Ousterhout](http://wiki.tcl.tk/John Ousterout) as a
|
Tcl was created by [John Ousterhout](https://wiki.tcl.tk/John%20Ousterout) as a
|
||||||
reusable scripting language for chip design tools he was creating. In 1997 he
|
reusable scripting language for circuit design tools that he authored. In 1997 he
|
||||||
was awarded the [ACM Software System
|
was awarded the [ACM Software System
|
||||||
Award](http://en.wikipedia.org/wiki/ACM_Software_System_Award) for Tcl. Tcl
|
Award](https://en.wikipedia.org/wiki/ACM_Software_System_Award) for Tcl. Tcl
|
||||||
can be used both as an embeddable scripting language and as a general
|
can be used both as an embeddable scripting language and as a general
|
||||||
programming language. It can also be used as a portable C library, even in
|
programming language. It can also be used as a portable C library, even in
|
||||||
cases where no scripting capability is needed, as it provides data structures
|
cases where no scripting capability is needed, as it provides data structures
|
||||||
such as dynamic strings, lists, and hash tables. The C library also provides
|
such as dynamic strings, lists, and hash tables. The C library also provides
|
||||||
portable functionality for loading dynamic libraries, string formatting and
|
portable functionality for loading dynamic libraries, string formatting and
|
||||||
code conversion, filesystem operations, network operations, and more.
|
code conversion, filesystem operations, network operations, and more. Various
|
||||||
Various features of Tcl stand out:
|
features of Tcl stand out:
|
||||||
|
|
||||||
* Convenient cross-platform networking API
|
* Convenient cross-platform networking API
|
||||||
|
|
||||||
@ -30,30 +30,29 @@ Various features of Tcl stand out:
|
|||||||
* A threading model recognized as robust and easy to use
|
* A threading model recognized as robust and easy to use
|
||||||
|
|
||||||
|
|
||||||
If Lisp is a list processor, then Tcl is a string processor. All values are
|
Tcl has much in common with Lisp, but instead of lists, Tcl uses strings as the
|
||||||
strings. A list is a string format. A procedure definition is a string
|
currency of the language. All values are strings. A list is a string with a
|
||||||
format. To achieve performance, Tcl internally caches structured
|
defined format, and the body of a procedure (a script) is also a string rather
|
||||||
representations of these values. The list commands, for example, operate on
|
than a block. To achieve performance, Tcl internally caches structured
|
||||||
|
representations of these values. list routines, for example, operate on
|
||||||
the internal cached representation, and Tcl takes care of updating the string
|
the internal cached representation, and Tcl takes care of updating the string
|
||||||
representation if it is ever actually needed in the script. The copy-on-write
|
representation if it is ever actually needed in the script. The copy-on-write
|
||||||
design of Tcl allows script authors can pass around large data values without
|
design of Tcl allows script authors to pass around large data values without
|
||||||
actually incurring additional memory overhead. Procedures are automatically
|
actually incurring additional memory overhead. Procedures are automatically
|
||||||
byte-compiled unless they use the more dynamic commands such as "uplevel",
|
byte-compiled unless they use the more dynamic routines such as "uplevel",
|
||||||
"upvar", and "trace".
|
"upvar", and "trace".
|
||||||
|
|
||||||
Tcl is a pleasure to program in. It will appeal to hacker types who find Lisp,
|
Tcl is a pleasure to program in. It will appeal to hacker types who find Lisp,
|
||||||
Forth, or Smalltalk interesting, as well as to engineers and scientists who
|
Forth, or Smalltalk interesting, as well as to engineers and scientists who
|
||||||
just want to get down to business with a tool that bends to their will. Its
|
just want to get down to business with a tool that bends to their will. Its
|
||||||
discipline of exposing all programmatic functionality as commands, including
|
discipline of exposing all programmatic functionality as routines, including
|
||||||
things like loops and mathematical operations that are usually baked into the
|
things like looping and mathematical operations that are usually baked into the
|
||||||
syntax of other languages, allows it to fade into the background of whatever
|
syntax of other languages, allows it to fade into the background of whatever
|
||||||
domain-specific functionality a project needs. It's syntax, which is even
|
domain-specific functionality a project needs. Its syntax, which is even
|
||||||
lighter that that of Lisp, just gets out of the way.
|
lighter that that of Lisp, just gets out of the way.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```tcl
|
```tcl
|
||||||
#! /bin/env tclsh
|
#! /bin/env tclsh
|
||||||
|
|
||||||
@ -61,70 +60,75 @@ lighter that that of Lisp, just gets out of the way.
|
|||||||
## 1. Guidelines
|
## 1. Guidelines
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# Tcl is not Bash or C! This needs to be said because standard shell quoting
|
# Tcl is not Sh or C! This needs to be said because standard shell quoting
|
||||||
# habits almost work in Tcl and it is common for people to pick up Tcl and try
|
# habits almost work in Tcl and it is common for people to pick up Tcl and try
|
||||||
# to get by with syntax they know from another language. It works at first,
|
# to get by with syntax they know from another language. It works at first,
|
||||||
# but soon leads to frustration with more complex scripts.
|
# but soon leads to frustration when scripts become more complex.
|
||||||
|
|
||||||
# Braces are just a quoting mechanism, not a code block constructor or a list
|
# Braces are a quoting mechanism, not syntax for the construction of code
|
||||||
# constructor. Tcl doesn't have either of those things. Braces are used,
|
# blocks or lists. Tcl doesn't have either of those things. Braces are used to
|
||||||
# though, to escape special characters in procedure bodies and in strings that
|
# escape special characters, which makes them well-suited for quoting procedure
|
||||||
# are formatted as lists.
|
# bodies and strings that should be interpreted as lists.
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## 2. Syntax
|
## 2. Syntax
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# Every line is a command. The first word is the name of the command, and
|
# A script is made up of commands delimited by newlines or semiclons. Each
|
||||||
# subsequent words are arguments to the command. Words are delimited by
|
# command is a call to a routine. The first word is the name of a routine to
|
||||||
# whitespace. Since every word is a string, in the simple case no special
|
# call, and subsequent words are arguments to the routine. Words are delimited
|
||||||
# markup such as quotes, braces, or backslash, is necessary. Even when quotes
|
# by whitespace. Since each argument is a word in the command it is already a
|
||||||
# are used, they are not a string constructor, but just another escaping
|
# string, and may be unquoted:
|
||||||
# character.
|
set part1 Sal
|
||||||
|
set part2 ut; set part3 ations
|
||||||
set greeting1 Sal
|
|
||||||
set greeting2 ut
|
|
||||||
set greeting3 ations
|
|
||||||
|
|
||||||
|
|
||||||
#semicolon also delimits commands
|
# a dollar sign introduces variable substitution:
|
||||||
set greeting1 Sal; set greeting2 ut; set greeting3 ations
|
set greeting $part1$part2$part3
|
||||||
|
|
||||||
|
|
||||||
# Dollar sign introduces variable substitution
|
# When "set" is given only the name of a variable, it returns the
|
||||||
set greeting $greeting1$greeting2$greeting3
|
# value of that variable:
|
||||||
|
set part3 ;# Returns the value of the variable.
|
||||||
|
|
||||||
|
|
||||||
# Bracket introduces command substitution. The result of the command is
|
# Left and right brackets embed a script to be evaluated for a result to
|
||||||
# substituted in place of the bracketed script. When the "set" command is
|
# substitute into the word:
|
||||||
# given only the name of a variable, it returns the value of that variable.
|
set greeting $part1$part2[set part3]
|
||||||
set greeting $greeting1$greeting2[set greeting3]
|
|
||||||
|
|
||||||
|
|
||||||
# Command substitution should really be called script substitution, because an
|
# An embedded script may be composed of multiple commands, the last of which provides
|
||||||
# entire script, not just a command, can be placed between the brackets. The
|
# the result for the substtution:
|
||||||
# "incr" command increments the value of a variable and returns its value.
|
|
||||||
|
|
||||||
set i 0
|
|
||||||
set greeting $greeting[
|
set greeting $greeting[
|
||||||
incr i
|
incr i
|
||||||
incr i
|
incr i
|
||||||
incr i
|
incr i
|
||||||
]
|
]
|
||||||
# i is now 3
|
puts $greeting ;# The output is "Salutations3"
|
||||||
|
|
||||||
# backslash suppresses the special meaning of characters
|
# Every word in a command is a string, including the name of the routine, so
|
||||||
|
# substitutions can be used on it as well. Given this variable
|
||||||
|
# assignment,
|
||||||
|
set action pu
|
||||||
|
|
||||||
|
# , the following three commands are equivalent:
|
||||||
|
puts $greeting
|
||||||
|
${action}ts $greeting
|
||||||
|
[set action]ts $greeting
|
||||||
|
|
||||||
|
|
||||||
|
# backslash suppresses the special meaning of characters:
|
||||||
set amount \$16.42
|
set amount \$16.42
|
||||||
|
|
||||||
|
|
||||||
# backslash adds special meaning to certain characters
|
# backslash adds special meaning to certain characters:
|
||||||
puts lots\nof\n\n\n\n\n\nnewlines
|
puts lots\nof\n\n\n\n\n\nnewlines
|
||||||
|
|
||||||
|
|
||||||
# A word enclosed in braces is not subject to any special interpretation or
|
# A word enclosed in braces is not subject to any special interpretation or
|
||||||
# substitutions, except that a backslash before a brace is not counted when
|
# substitutions, except that a backslash before a brace is not counted when
|
||||||
# looking for the closing brace
|
# looking for the closing brace:
|
||||||
set somevar {
|
set somevar {
|
||||||
This is a literal $ sign, and this \} escaped
|
This is a literal $ sign, and this \} escaped
|
||||||
brace remains uninterpreted
|
brace remains uninterpreted
|
||||||
@ -132,40 +136,44 @@ set somevar {
|
|||||||
|
|
||||||
|
|
||||||
# In a word enclosed in double quotes, whitespace characters lose their special
|
# In a word enclosed in double quotes, whitespace characters lose their special
|
||||||
# meaning
|
# meaning:
|
||||||
set name Neo
|
set name Neo
|
||||||
set greeting "Hello, $name"
|
set greeting "Hello, $name"
|
||||||
|
|
||||||
|
|
||||||
#variable names can be any string
|
# A variable name can be any string:
|
||||||
set {first name} New
|
set {first name} New
|
||||||
|
|
||||||
|
|
||||||
# The brace form of variable substitution handles more complex variable names
|
# The braced form of variable substitution handles more complex variable names:
|
||||||
set greeting "Hello, ${first name}"
|
set greeting "Hello, ${first name}"
|
||||||
|
|
||||||
|
|
||||||
# The "set" command can always be used instead of variable substitution
|
# "set" can always be used instead of variable substitution, and can handle all
|
||||||
|
# variable names:
|
||||||
set greeting "Hello, [set {first name}]"
|
set greeting "Hello, [set {first name}]"
|
||||||
|
|
||||||
|
|
||||||
# To promote the words within a word to individual words of the current
|
# To unpack a list into the command, use the expansion operator, "{*}". These
|
||||||
# command, use the expansion operator, "{*}".
|
# two commands are equivalent:
|
||||||
```
|
|
||||||
```tcl
|
|
||||||
set {*}{name Neo}
|
|
||||||
|
|
||||||
# is equivalent to
|
|
||||||
set name Neo
|
set name Neo
|
||||||
|
set {*}{name Neo}
|
||||||
|
|
||||||
|
|
||||||
# An array is a special variable that is a container for other variables.
|
# An array is a special variable that is a container for other variables.
|
||||||
set person(name) Neo
|
set person(name) Neo
|
||||||
set person(gender) male
|
set person(destiny) {The One}
|
||||||
set greeting "Hello, $person(name)"
|
set greeting "Hello, $person(name)"
|
||||||
|
|
||||||
|
|
||||||
# A namespace holds commands and variables
|
# "variable" can be used to declare or set variables. In contrast with "set",
|
||||||
|
# which uses both the global namespace and the current namespace to resolve a
|
||||||
|
# variable name, "variable" uses only the current namespace:
|
||||||
|
variable name New
|
||||||
|
|
||||||
|
|
||||||
|
# "namespace eval" creates a new namespace if it doesn't exist. A namespace
|
||||||
|
# can contain both routines and variables:
|
||||||
namespace eval people {
|
namespace eval people {
|
||||||
namespace eval person1 {
|
namespace eval person1 {
|
||||||
variable name Neo
|
variable name Neo
|
||||||
@ -173,50 +181,102 @@ namespace eval people {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# The full name of a variable includes its enclosing namespace(s), delimited by
|
# Use two or more colons to delimit namespace components in variable names:
|
||||||
# two colons:
|
namespace eval people {
|
||||||
set greeting "Hello $people::person1::name"
|
set greeting "Hello $person1::name"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Two or more colons also delimit namespace components in routine names:
|
||||||
|
proc people::person1::speak {} {
|
||||||
|
puts {I am The One.}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fully-qualified names begin with two colons:
|
||||||
|
set greeting "Hello $::people::person1::name"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## 3. A Few Notes
|
## 3. No More Syntax
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# All other functionality is implemented via commands. From this point on,
|
# All other functionality is implemented via routines. From this point on,
|
||||||
# there is no new syntax. Everything else there is to learn about Tcl is about
|
# there is no new syntax. Everything else there is to learn about
|
||||||
# the behaviour of individual commands, and what meaning they assign to their
|
# Tcl is about the behaviour of individual routines and what meaning they
|
||||||
# arguments.
|
# assign to their arguments.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## 4. Variables and Namespaces
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# Each variable and routine is associated with some namespace.
|
||||||
|
|
||||||
# To end up with an interpreter that can do nothing, delete the global
|
# To end up with an interpreter that can do nothing, delete the global
|
||||||
# namespace. It's not very useful to do such a thing, but it illustrates the
|
# namespace. It's not very useful to do such a thing, but it illustrates the
|
||||||
# nature of Tcl.
|
# nature of Tcl. The name of the global namespace is actually the empty
|
||||||
namespace delete ::
|
# string, but the only way to represent it is as a fully-qualified name. To
|
||||||
|
# try it out call this routine:
|
||||||
|
proc delete_global_namespace {} {
|
||||||
|
namespace delete ::
|
||||||
|
}
|
||||||
|
|
||||||
|
# Because "set" always keeps its eye on both the global namespace and the
|
||||||
# Because of name resolution behaviour, it's safer to use the "variable"
|
# current namespace, it's safer to use "variable" to declare a variable or
|
||||||
# command to declare or to assign a value to a namespace. If a variable called
|
# assign a value to a variable. If a variable called "name" already exists in
|
||||||
# "name" already exists in the global namespace, using "set" here will assign
|
# the global namespace, using "set" here will assign a value to the global
|
||||||
# a value to the global variable instead of creating a new variable in the
|
# variable instead of to a variable in the current namespace, whereas
|
||||||
# local namespace.
|
# "variable" operates only on the current namespace.
|
||||||
namespace eval people {
|
namespace eval people {
|
||||||
namespace eval person1 {
|
namespace eval person1 {
|
||||||
variable name Neo
|
variable name Neo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Once a variable is declared in a namespace, [set] sees it instead of seeing
|
||||||
|
# an identically-named variable in the global namespace:
|
||||||
|
namespace eval people {
|
||||||
|
namespace eval person1 {
|
||||||
|
variable name
|
||||||
|
set name Neo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# But if "set" has to create a new variable, it always does it relative to the
|
||||||
|
# current namespace:
|
||||||
|
unset name
|
||||||
|
namespace eval people {
|
||||||
|
namespace eval person1 {
|
||||||
|
set name neo
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
set people::person1::name
|
||||||
|
|
||||||
|
|
||||||
|
# An absolute name always begins with the name of the global namespace (the
|
||||||
|
# empty string), followed by two colons:
|
||||||
|
set ::people::person1::name Neo
|
||||||
|
|
||||||
|
|
||||||
|
# Within a procedure, the "variable" links a variable in the current namespace
|
||||||
|
# into the local scope:
|
||||||
|
namespace eval people::person1 {
|
||||||
|
proc fly {} {
|
||||||
|
variable name
|
||||||
|
puts "$name is flying!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# The full name of a variable can always be used, if desired.
|
|
||||||
set people::person1::name Neo
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## 4. Commands
|
## 4. Built-in Routines
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# Math can be done with the "expr" command.
|
# Math can be done with the "expr":
|
||||||
set a 3
|
set a 3
|
||||||
set b 4
|
set b 4
|
||||||
set c [expr {$a + $b}]
|
set c [expr {$a + $b}]
|
||||||
@ -226,54 +286,63 @@ set c [expr {$a + $b}]
|
|||||||
# "http://wiki.tcl.tk/Brace%20your%20#%20expr-essions" for details.
|
# "http://wiki.tcl.tk/Brace%20your%20#%20expr-essions" for details.
|
||||||
|
|
||||||
|
|
||||||
# The "expr" command understands variable and command substitution
|
# "expr" understands variable and script substitution:
|
||||||
set c [expr {$a + [set b]}]
|
set c [expr {$a + [set b]}]
|
||||||
|
|
||||||
|
|
||||||
# The "expr" command provides a set of mathematical functions
|
# "expr" provides a set of mathematical functions:
|
||||||
set c [expr {pow($a,$b)}]
|
set c [expr {pow($a,$b)}]
|
||||||
|
|
||||||
|
|
||||||
# Mathematical operators are available as commands in the ::tcl::mathop
|
# Mathematical operators are available as routines in the ::tcl::mathop
|
||||||
# namespace
|
# namespace:
|
||||||
::tcl::mathop::+ 5 3
|
::tcl::mathop::+ 5 3
|
||||||
|
|
||||||
# Commands can be imported from other namespaces
|
# Routines can be imported from other namespaces:
|
||||||
namespace import ::tcl::mathop::+
|
namespace import ::tcl::mathop::+
|
||||||
set result [+ 5 3]
|
set result [+ 5 3]
|
||||||
|
|
||||||
|
|
||||||
# New commands can be created via the "proc" command.
|
# Non-numeric values must be quoted, and operators like "eq" can be used to
|
||||||
|
# constrain the operation to string comparison:
|
||||||
|
set name Neo
|
||||||
|
expr {{Bob} eq $name}
|
||||||
|
|
||||||
|
# The general operators fall back to string string comparison if numeric
|
||||||
|
# operation isn't feasible:
|
||||||
|
expr {{Bob} == $name}
|
||||||
|
|
||||||
|
|
||||||
|
# "proc" creates new routines:
|
||||||
proc greet name {
|
proc greet name {
|
||||||
return "Hello, $name!"
|
return "Hello, $name!"
|
||||||
}
|
}
|
||||||
|
|
||||||
#multiple parameters can be specified
|
#multiple parameters can be specified:
|
||||||
proc greet {greeting name} {
|
proc greet {greeting name} {
|
||||||
return "$greeting, $name!"
|
return "$greeting, $name!"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# As noted earlier, braces do not construct a code block. Every value, even
|
# As noted earlier, braces do not construct a code block. Every value, even
|
||||||
# the third argument of the "proc" command, is a string. The previous command
|
# the third argument to "proc", is a string. The previous command
|
||||||
# rewritten to not use braces at all:
|
# can be rewritten using no braces:
|
||||||
proc greet greeting\ name return\ \"\$greeting,\ \$name!\"
|
proc greet greeting\ name return\ \"\$greeting,\ \$name!\"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# When the last parameter is the literal value, "args", it collects all extra
|
# When the last parameter is the literal value "args", all extra arguments
|
||||||
# arguments when the command is invoked
|
# passed to the routine are collected into a list and assigned to "args":
|
||||||
proc fold {cmd args} {
|
proc fold {cmd first args} {
|
||||||
set res 1
|
|
||||||
foreach arg $args {
|
foreach arg $args {
|
||||||
set res [$cmd $res $arg]
|
set first [$cmd $first $arg]
|
||||||
}
|
}
|
||||||
return $res
|
return $first
|
||||||
}
|
}
|
||||||
fold ::tcl::mathop::* 5 3 3 ;# -> 45
|
fold ::tcl::mathop::* 5 3 3 ;# -> 45
|
||||||
|
|
||||||
|
|
||||||
# Conditional execution is implemented as a command
|
# Conditional execution is implemented as a routine:
|
||||||
if {3 > 4} {
|
if {3 > 4} {
|
||||||
puts {This will never happen}
|
puts {This will never happen}
|
||||||
} elseif {4 > 4} {
|
} elseif {4 > 4} {
|
||||||
@ -283,31 +352,40 @@ if {3 > 4} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Loops are implemented as commands. The first, second, and third
|
# Loops are implemented as routines. The first and third arguments to
|
||||||
# arguments of the "for" command are treated as mathematical expressions
|
# "for" are treated as scripts, while the second argument is treated as
|
||||||
|
# an expression:
|
||||||
|
set res 0
|
||||||
for {set i 0} {$i < 10} {incr i} {
|
for {set i 0} {$i < 10} {incr i} {
|
||||||
set res [expr {$res + $i}]
|
set res [expr {$res + $i}]
|
||||||
}
|
}
|
||||||
|
unset res
|
||||||
|
|
||||||
|
|
||||||
# The first argument of the "while" command is also treated as a mathematical
|
# The first argument to "while" is also treated as an expression:
|
||||||
# expression
|
|
||||||
set i 0
|
set i 0
|
||||||
while {$i < 10} {
|
while {$i < 10} {
|
||||||
incr i 2
|
incr i 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# A list is a specially-formatted string. In the simple case, whitespace is
|
# A list is a string, and items in the list are delimited by whitespace:
|
||||||
# sufficient to delimit values
|
|
||||||
set amounts 10\ 33\ 18
|
set amounts 10\ 33\ 18
|
||||||
set amount [lindex $amounts 1]
|
set amount [lindex $amounts 1]
|
||||||
|
|
||||||
|
# Whitespace in a list item must be quoted:
|
||||||
|
set inventory {"item 1" item\ 2 {item 3}}
|
||||||
|
|
||||||
|
|
||||||
|
# It's generally a better idea to use list routines when modifing lists:
|
||||||
|
lappend inventory {item 1} {item 2} {item 3}
|
||||||
|
|
||||||
|
|
||||||
# Braces and backslash can be used to format more complex values in a list. A
|
# Braces and backslash can be used to format more complex values in a list. A
|
||||||
# list looks exactly like a script, except that the newline character and the
|
# list looks exactly like a script, except that the newline character and the
|
||||||
# semicolon character lose their special meanings. This feature makes Tcl
|
# semicolon character lose their special meanings, and there is no script or
|
||||||
# homoiconic. There are three items in the following list.
|
# variable substitution. This feature makes Tcl homoiconic. There are three
|
||||||
|
# items in the following list:
|
||||||
set values {
|
set values {
|
||||||
|
|
||||||
one\ two
|
one\ two
|
||||||
@ -319,19 +397,19 @@ set values {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Since a list is a string, string operations could be performed on it, at the
|
# Since, like all values, a list is a string, string operations could be
|
||||||
# risk of corrupting the formatting of the list.
|
# performed on it, at the risk of corrupting the formatting of the list:
|
||||||
set values {one two three four}
|
set values {one two three four}
|
||||||
set values [string map {two \{} $values] ;# $values is no-longer a \
|
set values [string map {two \{} $values] ;# $values is no-longer a \
|
||||||
properly-formatted listwell-formed list
|
properly-formatted list
|
||||||
|
|
||||||
|
|
||||||
# The sure-fire way to get a properly-formmated list is to use "list" commands
|
# The sure-fire way to get a properly-formatted list is to use "list" routines:
|
||||||
set values [list one \{ three four]
|
set values [list one \{ three four]
|
||||||
lappend values { } ;# add a single space as an item in the list
|
lappend values { } ;# add a single space as an item in the list
|
||||||
|
|
||||||
|
|
||||||
# Use "eval" to evaluate a value as a script
|
# Use "eval" to evaluate a value as a script:
|
||||||
eval {
|
eval {
|
||||||
set name Neo
|
set name Neo
|
||||||
set greeting "Hello, $name"
|
set greeting "Hello, $name"
|
||||||
@ -339,84 +417,94 @@ eval {
|
|||||||
|
|
||||||
|
|
||||||
# A list can always be passed to "eval" as a script composed of a single
|
# A list can always be passed to "eval" as a script composed of a single
|
||||||
# command.
|
# command:
|
||||||
eval {set name Neo}
|
eval {set name Neo}
|
||||||
eval [list set greeting "Hello, $name"]
|
eval [list set greeting "Hello, $name"]
|
||||||
|
|
||||||
|
|
||||||
# Therefore, when using "eval", use [list] to build up a desired command
|
# Therefore, when using "eval", , use "list" to build
|
||||||
|
# up the desired command:
|
||||||
set command {set name}
|
set command {set name}
|
||||||
lappend command {Archibald Sorbisol}
|
lappend command {Archibald Sorbisol}
|
||||||
eval $command
|
eval $command
|
||||||
|
|
||||||
|
|
||||||
# A common mistake is not to use list functions when building up a command
|
# A common mistake is not to use list functions when building up a command:
|
||||||
set command {set name}
|
set command {set name}
|
||||||
append command { Archibald Sorbisol}
|
append command { Archibald Sorbisol}
|
||||||
eval $command ;# There is an error here, because there are too many arguments \
|
try {
|
||||||
to "set" in {set name Archibald Sorbisol}
|
eval $command ;# The error here is that there are too many arguments \
|
||||||
|
to "set" in {set name Archibald Sorbisol}
|
||||||
|
} on error {result eoptions} {
|
||||||
|
puts [list {received an error} $result]
|
||||||
|
}
|
||||||
|
|
||||||
|
# This mistake can easily occur with "subst":
|
||||||
|
|
||||||
# This mistake can easily occur with the "subst" command.
|
|
||||||
set replacement {Archibald Sorbisol}
|
set replacement {Archibald Sorbisol}
|
||||||
set command {set name $replacement}
|
set command {set name $replacement}
|
||||||
set command [subst $command]
|
set command [subst $command]
|
||||||
eval $command ;# The same error as before: too many arguments to "set" in \
|
try {
|
||||||
{set name Archibald Sorbisol}
|
eval $command ;# The same error as before: too many arguments to "set" in \
|
||||||
|
{set name Archibald Sorbisol}
|
||||||
|
} trap {TCL WRONGARGS} {result options} {
|
||||||
|
puts [list {received another error} $result]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# The proper way is to format the substituted value using use the "list"
|
# "list" correctly formats a value for substitution:
|
||||||
# command.
|
|
||||||
set replacement [list {Archibald Sorbisol}]
|
set replacement [list {Archibald Sorbisol}]
|
||||||
set command {set name $replacement}
|
set command {set name $replacement}
|
||||||
set command [subst $command]
|
set command [subst $command]
|
||||||
eval $command
|
eval $command
|
||||||
|
|
||||||
|
|
||||||
# It is extremely common to see the "list" command being used to properly
|
# "list" is commonly used to format values for substitution into scripts: There
|
||||||
# format values that are substituted into Tcl script templates. There are
|
# are several examples of this, below.
|
||||||
# several examples of this, below.
|
|
||||||
|
|
||||||
|
|
||||||
# The "apply" command evaluates a string as a command.
|
# "apply" evaluates a two-item list as a routine:
|
||||||
set cmd {{greeting name} {
|
set cmd {{greeting name} {
|
||||||
return "$greeting, $name!"
|
return "$greeting, $name!"
|
||||||
}}
|
}}
|
||||||
apply $cmd Whaddup Neo
|
apply $cmd Whaddup Neo
|
||||||
|
|
||||||
|
# A third item can be used to specify the namespace to apply the routine in:
|
||||||
|
set cmd [list {greeting name} {
|
||||||
|
return "$greeting, $name!"
|
||||||
|
} [namespace current]]
|
||||||
|
apply $cmd Whaddup Neo
|
||||||
|
|
||||||
# The "uplevel" command evaluates a script in some enclosing scope.
|
|
||||||
|
# "uplevel" evaluates a script at some higher level in the call stack:
|
||||||
proc greet {} {
|
proc greet {} {
|
||||||
uplevel {puts "$greeting, $name"}
|
uplevel {puts "$greeting, $name"}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc set_double {varname value} {
|
proc set_double {varname value} {
|
||||||
if {[string is double $value]} {
|
if {[string is double $value]} {
|
||||||
uplevel [list variable $varname $value]
|
uplevel [list variable $varname $value]
|
||||||
} else {
|
} else {
|
||||||
error [list {not a double} $value]
|
error [list {not a double} $value]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# The "upvar" command links a variable in the current scope to a variable in
|
# "upvar" links a variable at the current level in the call stack to a variable
|
||||||
# some enclosing scope
|
# at some higher level:
|
||||||
proc set_double {varname value} {
|
proc set_double {varname value} {
|
||||||
if {[string is double $value]} {
|
if {[string is double $value]} {
|
||||||
upvar 1 $varname var
|
upvar 1 $varname var
|
||||||
set var $value
|
set var $value
|
||||||
} else {
|
} else {
|
||||||
error [list {not a double} $value]
|
error [list {not a double} $value]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Get rid of the built-in "while" command.
|
# Get rid of the built-in "while" routine, and use "proc" to define a new one:
|
||||||
rename ::while {}
|
rename ::while {}
|
||||||
|
# handling is left as an exercise:
|
||||||
|
|
||||||
# Define a new while command with the "proc" command. More sophisticated error
|
|
||||||
# handling is left as an exercise.
|
|
||||||
proc while {condition script} {
|
proc while {condition script} {
|
||||||
if {[uplevel 1 [list expr $condition]]} {
|
if {[uplevel 1 [list expr $condition]]} {
|
||||||
uplevel 1 $script
|
uplevel 1 $script
|
||||||
@ -425,27 +513,68 @@ proc while {condition script} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# The "coroutine" command creates a separate call stack, along with a command
|
# "coroutine" creates a new call stack, a new routine to enter that call stack,
|
||||||
# to enter that call stack. The "yield" command suspends execution in that
|
# and then calls that routine. "yield" suspends evaluation in that stack and
|
||||||
# stack.
|
# returns control to the calling stack:
|
||||||
proc countdown {} {
|
proc countdown count {
|
||||||
#send something back to the initial "coroutine" command
|
# send something back to the creater of the coroutine, effectively pausing
|
||||||
yield
|
# this call stack for the time being.
|
||||||
|
yield [info coroutine]
|
||||||
|
|
||||||
set count 3
|
while {$count > 1} {
|
||||||
while {$count > 1} {
|
yield [incr count -1]
|
||||||
yield [incr count -1]
|
}
|
||||||
}
|
return 0
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
coroutine countdown1 countdown
|
coroutine countdown1 countdown 3
|
||||||
coroutine countdown2 countdown
|
coroutine countdown2 countdown 5
|
||||||
puts [countdown 1] ;# -> 2
|
puts [countdown1] ;# -> 2
|
||||||
puts [countdown 2] ;# -> 2
|
puts [countdown2] ;# -> 4
|
||||||
puts [countdown 1] ;# -> 1
|
puts [countdown1] ;# -> 1
|
||||||
puts [countdown 1] ;# -> 0
|
puts [countdown1] ;# -> 0
|
||||||
puts [coundown 1] ;# -> invalid command name "countdown1"
|
catch {
|
||||||
puts [countdown 2] ;# -> 1
|
puts [coundown1] ;# -> invalid command name "countdown1"
|
||||||
|
} cres copts
|
||||||
|
puts $cres
|
||||||
|
puts [countdown2] ;# -> 3
|
||||||
|
|
||||||
|
|
||||||
|
# Coroutine stacks can yield control to each other:
|
||||||
|
|
||||||
|
proc pass {whom args} {
|
||||||
|
return [yieldto $whom {*}$args]
|
||||||
|
}
|
||||||
|
|
||||||
|
coroutine a apply {{} {
|
||||||
|
yield
|
||||||
|
set result [pass b {please pass the salt}]
|
||||||
|
puts [list got the $result]
|
||||||
|
set result [pass b {please pass the pepper}]
|
||||||
|
puts [list got the $result]
|
||||||
|
}}
|
||||||
|
|
||||||
|
coroutine b apply {{} {
|
||||||
|
set request [yield]
|
||||||
|
while 1 {
|
||||||
|
set response [pass c $request]
|
||||||
|
puts [list [info coroutine] is now yielding]
|
||||||
|
set request [pass a $response]
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
coroutine c apply {{} {
|
||||||
|
set request [yield]
|
||||||
|
while 1 {
|
||||||
|
if {[string match *salt* $request]} {
|
||||||
|
set request [pass b salt]
|
||||||
|
} else {
|
||||||
|
set request [pass b huh?]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
# get things moving
|
||||||
|
a
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
Loading…
Reference in New Issue
Block a user