Go to the first, previous, next, last section, table of contents.


String Variables

Some SM users seem to be confused by variables and vectors; if you are one of these, the section on quoting (see section What Quotes What When) might help.

SM maintains a set of variables which are defined with one of the statements

DEFINE name value

or

DEFINE name { value_list }

or

DEFINE name ( expression )

where name must consist of digits, letters and `_' (but must not start with a digit), and may be a keyword.

Value may be a word or a number. Value_list has no such restrictions and may contain many words. Note that due to the presence of the {}, variables are not expanded (i.e. replaced by their value) in value_list, whereas they are in value. In fact, the list can be delimited by <> rather than {}; see DEFINE for details.

The expression in DEFINE variable ( expr ) should be a scalar; if it is not, the first element of the vector will be used and you will be warned, if VERBOSE (see section Verbose) is one or greater. Sometimes you just want to evaluate an expression and treat the answer as a string; in this case use the special vector form $(expr) which is replaced by the value of the expression -- for example echo e is $(exp(1)). Expressions are further discussed under `Vectors and Arithmetic'.

There are a number of special variables whose value is always the current value of some internal SM variable such as the current position or the point type. The variable "date" is also special and expands to give the current time and date, -- try typing echo $date. You can freeze these variables at their current value by saying define name | (see below).

Each time SM reads $name it replaces it by its value, considered as a character string. For example,

DEFINE hi hello
WRITE STANDARD $hi

will print hello. This expansion is done before even the lowest level of lex analysis, so if a command is attempting to read a value it is possible to give it the name of a SM variable. An example would be the XLABEL command, which writes a string as the x-axis label of a graph,

DEFINE name Aelfred
XLABEL My name is $name

will invoke the XLABEL command, and write My name is Aelfred below the x-axis. (Incidentally, DEFINE Aelfred Aethelstan YLABEL $$name will write Aethelstan as the y-axis label, which can be handy in macros. The use of the double $$ indicates to SM to do a double translation, as it first expands to $Aelfred which then expands to Aethelstan).

A variable can be deleted by DEFINE name DELETE so for example the macro

MACRO undef 1 { DEFINE $1 DELETE }

invoked as

undef name

will undefine the variable name (see the section on macros if you are confused).

There are also three special values, :, |, and ?. The command define name : means `get the value of name from the environment file'. If this fails, and if the variable is all uppercase, SM will then try to use the value of an environment (VMS: logical) variable of the same name. Using define name ? means `read the value of name from the keyboard'. You can specify a prompt to be used, see DEFINE for details. The form with | has changed a little with version 2.1.1. The variables that you can use with | have not changed, but their usage has slightly. They are all defined for you when SM starts and each is always correct, tracking the current value of the corresponding internal variable. For example, try echo $angle angle 45 echo $angle. If you now say define angle |, $angle will cease to track the internal value and will remain fixed (the same effect can be achieved with define angle 45). When you say define angle delete it will once more track the internal value. Your old code will continue to work, but in many cases it is possible to remove the explicit definition with |. This special sort of variable will not be SAVEd, and will not show up if you list the currently defined variables. A list of the | variables is given in the section on DEFINE.

So using the example `.sm' environment file listed in the previous section of the manual, DEFINE name : will define name to be Robert, DEFINE angle | will give the last value set by the ANGLE command, and DEFINE datafile ? will ask you for the value of `datafile', which can be useful in macros. For example,

DEFINE noise ? { Ring bell? } IF('$noise' != 'n') { bell }

will execute the macro bell if you type anything but n in reply to the question `Ring bell?'.

When writing macros, it is also sometimes useful to know if a variable has been defined. The variable $?name has the value 1 if name is defined, otherwise it is 0. For instance, there is a line

define term : if($?term) { termtype $term } 

in the startup file, to set a termtype if present in the environment file.

There are also commands to read the values of variables from data files defined with the DATA command.

DEFINE name READ i
or
DEFINE name READ i j

will set name to be the i'th line of the file (or the j'th word of the i'th line). An example is given in the section on `useful macros'. You can read variables from the headers of binary files (specified with the IMAGE command) using DEFINE name IMAGE, although this is only supported for a limited class of file_type's (see section Two-Dimensional Graphics).

All currently defined variables may be listed with

LIST DEFINE [ begin end ]

where the optional begin and end define the range of variables (alphabetically) to be listed. You might prefer to use the macro lsv which won't appear on your history list.

Variables are usually not expanded within double quotes or { }. If for some reason you need to force expansion within double quotes, it can be done with $!name. The macro `load' discussed under useful macros gives an example of this mechanism. If you need to expand a variable, with no questions asked (and even within {}), use $!!name.

Sometimes you may want to terminate a variable name where SM doesn't want to, and this can be done with a trick involving double quotes. Say you are writing a macro to find all the stars redder than B-V = 1.0 in a set of data vectors, and you want to rename them with a trailing "_red", so star goes to star_red. So you write a foreach loop,

FOREACH x ( U B V R I J K ) { SET $x_red = $x IF(B-V >1)}

Well, that won't work because SM thinks that you are referring to a previously defined variable named x_red, so it will complain that x_red is not defined. But if you write it as $x""_red the "" separate the x from the _red until $x is expanded, and then disappear, and all is well. When a variable is read, SM skips over all whitespace before the definition, and this can cause problems if you hit ^C in the middle, as the rest of the command will be thrown away. If you ever hit a ^C, and can't get a prompt, try typing any non-whitespace character.

Variables are string-variables, and are not primarily designed for doing arithmetic (that's what vectors are for). This is a common source of confusion so let's consider some examples (at the risk of anticipating some later sections of the manual).

DEFINE a 12

defines a variable a which consists of the two characters `1' and `2', and which can be used anywhere -- for example xlabel $a. What about vectors? Consider

SET x=10

which defines a single-element vector whose value is ten, ready to be used in expressions such as

SET y=$a + x*12

Note that the $a is still just the two characters `1' and `0', but in this context that is interpreted as the number ten. So what does

DEFINE y $a+x*12

do? Well, actually it results in a syntax error (the `+' ends a word), so try

DEFINE y <$a+x*12>

This defines the variable y as the string `10+x*12', it doesn't evaluate the expression. You can evaluate the expression if you want with

DEFINE y ( $a+x*12 )

which defines y as the string `130'. Incidently, you can sometimes get away without an explicit variable with the syntax $($a+x*12) which also expands to the string `130'.

The fact that variables are simply strings can be used to build complex commands; consider for example the macro

readem          # read multiple lines columns with names in row 1
                READ ROW names 1.s
                DEFINE rc <$(names[(0)]) 1>
                DO i=2,DIMEN(names) {
                        DEFINE rc <$rc $(names[($i - 1)]) $i>
                }
                LINES 2 0
                READ < $rc >

which reads the names of a set of columns from line 1, builds a command to read the data in the variable rc, and then reads all the data in one command. You could of course loop through names reading each column in turn, but this should be a good deal faster.

By default variables are global, so if you have a variable $i, and a macro that you call also uses a variable $i, the macro will change the value that you so carefully defined. To avoid this, it is possible to force variables (and also vectors) to be local, using a command like DEFINE i LOCAL; in this case the variable $i will softly and suddenly disappear when you leave the current macro (and therefore you cannot make variables local at the topmost level, i.e. at the command prompt). In fact, such variables aren't strictly local, they have what's called nested scope, as they are visible from any macros that you may call -- they simply do not propagate backwards up the call stack. You are free to make a variable local in any (or all) macros, there's no restriction on how deep such local declarations may be nested.


Go to the first, previous, next, last section, table of contents.