Go to the previous, next section.

Examples of Useful Macros

When you start SM the directory specified as macro in your `.sm' file is searched for a file `default', and then the macro startup from that file is executed. At the time of writing of this manual, startup was defined as:
startup         ## macro invoked upon startup
                FOREACH 1 { default_font device edit file_type history_char \
                       macro macro2 overload printer prompt prompt2 SHELL 2 {
                   DEFINE $1 :
                FOREACH 1 { TeX_strings case_fold_search fan_compress \
                    line_up_exponents noclobber overload \
                    remember_history_line traceback uppercase 2 {
                   DEFINE $1 :
                   IF($?$1) {
                      IF('$$1' == '0') {
                         DEFINE $1 DELETE
                IF($?prompt) { PROMPT $prompt\n DEFINE prompt DELETE
                2 ELSE { PROMPT : 2
                IF($?device) { DEVICE $device
                2 ELSE { DEFINE device nodevice 2
                IF($?default_font && $?TeX_strings == 0) {
                   echo You can only define a default font if you use TeX
                IF($?history_char) {   # use $history_char as history character
                   IF('$history_char' != '0' && '$history_char' != '1') {
                      EDIT history_char $history_char
                   EDIT ^ ^
                   DEFINE history_char DELETE
                # load the default macros
                DEFINE mfiles < stats utils > # $mfiles is used by `sav'
                FOREACH f ( mongo $mfiles ) { MACRO READ "$!macro"$f 2
                FOREACH var ( x_col y_col data_file ) { DEFINE $var . 2
                # load uppercase if defined in .sm file
                IF($?uppercase) {
                   MACRO READ "$!macro"uppercase
                # and overload keywords such as erase, if so desired
                set_overload $?overload
                # and some keymaps
                IF($?edit) { READ EDIT "$!edit" 2
                # and an optional macro file, with macro startup2
                IF($?macro2) {
                   MACRO READ "$!macro2"default
                   IF(is_set(startup2,1)) { startup2 2  # startup2 is defined
                # provide a \n after the IF
As this macro is executed every time that you run SM, let us consider it in some detail. After setting the prompt, it looks for entries for a number of variables in your `.sm' file. Some (such as printer) are simply DEFINEd, while some (such as TeX_strings) are only DEFINEd if they have a non-zero value. Because some of the values might not be numeric, the comparison is forced to be done on strings by enclosing the quantities in single quotes. An entry prompt is interpreted as a primary prompt, mostly for compatibility with the use of $prompt2 to set the secondary prompt. If device is defined it is used to set the default plotting device, and both it and printer are used by a couple of macros (hcopy and hmacro) that produce hardcopy. The variables TeX_strings and default_fonts are used in producing labels (see section SM's Fonts). Because TeX uses ^ for superscripts, we allow you to put a history_char line in `.sm' to specify a character to use rather than ^ for history (I use `). If you use 0, or omit the value (so it is set to 1), no history character is defined to replace ^. The variable file_type is used by the IMAGE command to determine the file format that you use (e.g. C, or unformatted fortran).

Startup doesn't have to check that macro was successfully defined as it must have been found for startup to have been read in the first place. Macro specifies where to look for macro libraries, and startup next sets the variable mfiles containing the names of some of the system macros to be loaded, and reads them. The macro load defined below also maintains the mfiles list, as does unload. It is used by the sav macro, which is discussed below the main listing of macros that follows. We also set some variables used by the id macro.

As part of our effort to be nice to users, if you have uppercase 1 in your `.sm' file, we also load the uppercase macros. Next startup overloads some keywords if overload is in your `.sm' file, reads a file of keybindings (if edit is given in `.sm'), and finally tries to read a second optional macro directory macro2, and executes a macro startup2 if it's defined (that's what the macro is_set is checking). This is quite important, as it provides a way to customise SM to your personal taste without convincing the local SM guru that your taste should be foisted on everyone. If you want a prompt that is different, or a definition of q that just quits without asking questions, you can get them by using macro2. You can see that it is possible to tailor SM pretty much as you wish without changing a line of code, just by playing with the startup macro.

SM provides various compatibility macros, and some to package often-used functions. The macro files `stats' and `utils', which are read when SM is started, provide various useful macros, a few of which are presented here. To see a current list, either look at the files directly, set VERBOSE to zero and list all the macros, look at the listing in this manual (see section The System Macro Libraries), or use lsm to list macro files (this only works if you are running Unix; try lsm demos). We give here a number of macros taken from the files `default', `mongo', `stats', and `utils'. Among those not listed are those like lin defined to be lines that are pure abbreviations, those like xlogarithm defined as SET x=lg(x) which provide functionality in a perhaps familiar form, and many more like those that are given here which provide enhancements (e.g. the macro barhist). A discussion of a few of the more interesting or obscure follows. Keywords are written in uppercase, because you might have been playing tricks with overloading the lowercase equivalents. Many of these macros, in fact all from `default' and `mongo', start with ## so as not to show up in listings made when VERBOSE is 0, and so as not to be SAVEd. In the interest of brevity we have omitted most of these initial comments.

cumulate 2   # Find the cumulative distribution of $1 in $2
             DEFINE sum 0 SET $2=0*$1 SET HELP $2 Cumulation of $1
             DO i=0,DIMEN($1)-1 {
                DEFINE sum ( $sum + $1[$i] )
                SET $2[$i] = $sum
             define sum delete
da   1       DATA "$1"
del1 1       DELETE HISTORY \n
dev  1       del1 DEFINE device $1 DEVICE $1
dra 2        # Draw, accepting expressions
             define 1 ($1) define 2 ($2) draw $1 $2
edit_hist    # Edit the history list
             del1 MACRO all 0 100000       # define "all" from buffer
             WRITE STANDARD Editing History Buffer\n
             MACRO EDIT all                # do the editing
             DELETE 0 100000       # empty history buffer
             WRITE HISTORY all             # replace history by "all"
era          del1 ERASE
gauss 1      # Evaluate a Gaussian : N($mean,$sig)
             SET $0 = 1/(SQRT(2*PI)*$sig)*EXP(-(($1-$mean)/$sig)**2/2)
get 2        # Syntax: get i j.  Read a column from a file.
             # Name of vector is jth word of line i.
             DEFINE nn READ $1 $2 echo reading $nn\n
             READ $nn $2
             SET HELP $nn Column $2 from $data_file
             DEFINE nn DELETE
hardcopy     DEVICE nodevice   # close old device
hcopy 13     ## hcopy [printer] [l1] [l2] Make hardcopy of playback buffer
             # optionally specify printer ($1) and desired lines ($2-$3)
             # if the printer ($1) is omitted (i.e. $1 is missing or a
             # number), it will be taken from the value of the environment
             # variable PRINTER, if defined.
             IF($?printer == 0) {
                DEFINE printer ? { what kind of printer? 2
             IF($?1) {
                IF(WHATIS($1) == 0) { # a number
                   if($?2) { DEFINE 3 $2 2
                   DEFINE 2 $1
                   DEFINE 1 DELETE
             IF($?1) {
                DEVICE $printer $1
             2 ELSE {
                IF($?PRINTER == 0) { DEFINE PRINTER : 2 # which one?
                IF($?PRINTER) {
                   DEVICE $printer $PRINTER
                2 ELSE {
                   DEVICE $printer
             IF($?2 == 0) {
                DEFINE 2 0 DEFINE 3 10000
             2 ELSE {
                IF($?3 == 0) { DEFINE 3 $2 2
             playback $2 $3 \n DEVICE $device
hmacro  12   ## hmacro [macro] [printer] Make hardcopy of a macro
             # If only 1 argument is present, it is taken to be the printer
             # unless an environment PRINTER variable is defined, when
             # that's used as a printer, and the argument is taken to be
             # a macro. If no macro is specified, make a temp one
             IF($?printer == 0) {
                DEFINE printer ? { what kind of printer? 2
             IF($?2 == 0) {          # only one arg
                IF($?PRINTER == 0) { DEFINE PRINTER : 2
                IF($?PRINTER) {
                   DEFINE 2 $PRINTER
             IF($?1) {
                if($?2) {            # 2 args
                   DEFINE _mac $1
                   DEFINE _temp 0    # no temp macro
                2 ELSE {             # 1 arg, take as printer
                   DEFINE 2 $1       # printer
                   DEFINE _temp 1    # need temp macro
             2 ELSE {                # no $1
                IF($?2 == 0) { DEFINE 2 " " 2
                DEFINE _temp 1       # need temp macro
             IF($_temp) {
                DEFINE _mac _mac
                echo "Create temporary macro, exit with ^X"
                MACRO EDIT $_mac
                IF(is_set($_mac,1) == 0) {
                   DEFINE _mac DELETE DEFINE _temp DELETE 
                   DEFINE _test DELETE
             DEVICE $printer $2
             $_mac \n DEVICE $device
             IF($_temp) { MACRO $_mac DELETE 2
             DEFINE _mac DELETE DEFINE _temp DELETE bell
load         # load macros in default directory
             DEFINE macro :                   # get default directory
             MACRO READ "$!macro"$1  # read macro file
             IF($?mfiles == 0) {
                DEFINE mfiles $1
             2 ELSE {
                DEFINE 3 0
                FOREACH 2 ( $mfiles ) {
                  IF('$2' == '$1') { DEFINE 3 1 2
                IF($3 == 0) { DEFINE mfiles < $mfiles $1 > 2
load2 1      # load macros in (second) default directory
             DEFINE macro2 : # get directory
             IF($?macro2) {
                MACRO READ "$!macro2"$1  # read macro file
             2 ELSE {
                echo Directory macro2 is not defined
logerr 3     # logerr x y error, where y is logged, and error isn't
             SET _y = 10**$2
             SET d_y = LG(_y + $3) - $2 ERRORBAR $1 $2 d_y 2
             SET d_y = $2 - LG(_y - $3) ERRORBAR $1 $2 d_y 4
             DELETE _y DELETE d_y
lsq 4        # Do a least squares fit to a set of vectors
             # Syntax: lsq x y x2 y2   Fit line y2=$a*x2+$b to x y
             SET _n = DIMEN($1)  # number of points
             SET _sx = SUM($1)  # Sigma x
             SET _sy = SUM($2)  # Sigma y
             SET _sxy = SUM($1*$2)  # Sigma xy
             SET _sxx = SUM($1*$1)  # Sigma xx
             DEFINE a ( (_n*_sxy - _sx*_sy)/(_n*_sxx - _sx*_sx) )
             DEFINE b ( (_sy - $a*_sx)/_n )
             SET $4=$a*$3+$b
             FOREACH v ( _n _sx _sy _sxy _sxx ) { DELETE $v 2
playback     ## define "all" from buffer, and run it
             # with args, only playback those lines
             IF($?1 == 0) {
                DEFINE 1 0 DEFINE 2 10000
             2 ELSE {
                IF($?2 == 0) { DEFINE 2 $1 2
             del1 MACRO all $1 $2 all
read_old 1   del1 # read a Mongo file onto the history buffer
             READ OLD temp $1
             WRITE HISTORY temp MACRO temp { DELETE 2
rel 2        # Relocate, accepting expressions
             define 1 ($1) define 2 ($2) relocate $1 $2
reverse 1    # reverse the order of a vector
             SET _i = DIMEN($1),1,-1 SORT < _i $1 > DELETE _i
sav 1        # Save to a file $1, don't save from files `$mfiles'
             _save $1
_save 1      # Save to a file $1, don't save from files `$mfiles'
             FOREACH 2 ( $mfiles ) { MACRO DELETE "$!macro"$2 2
             DEFINE 2 0 define 2 ? { save vectors? 2
             SAVE "$!1" 1 $2 1
             FOREACH 2 ( $mfiles ) { MACRO READ "$!macro"$2 2

Cumulate is given as a way not to write macros if you can help it (in this case, I couldn't). A better example is reverse which reverses the order of the elements in a vector without resorting to a DO loop.

The macro da could have been defined to be DATA, but there are various special characters that appear in filenames; try data /usr/spool/junk or data disk$data:[ETHELRED]junk.dat. The macro da provides a set of double quotes to escape these unwanted interpretations. Incidently, da "/usr/spool/junk" won't work.

DELETE HISTORY deletes the last command on the history buffer, so del1 alone on a line will delete itself, which can be used to prevent a command from appearing on the history list, for example changing devices with dev. Dev also defines a variable device which is used by the hcopy and hmacro macros to make hardcopies, while returning you to your initial device. The startup macro listed above also sets device, if it is specified in your `.sm' file. You should be careful not to include more than one del1 macro in any macro that you write yourself, as each del1 will remove a command from history and you could find commands mysteriously disappearing.

Gauss evaluates a Gaussian, e.g. SET x=-3,3,0.05 SET g=gauss(x) lim x g box con x g, an example of using a macro like a function definition. (For this example to work, you have to define variables mean and sig first).

There is an example of reading variables from files and using them in macro get. This reads a word from a line in a file with the DEFINE nn READ i j command, which sets $nn to be the jth word on line i of the current data file. This variable is then used to READ a vector, which is given the appropriate name. So if a file looks like:

This is an example file
alpha   beta    gamma   delta
1       10      0.1     1e1
2       20      0.2     1e2
3       30      0.3     1e3
4       40      0.4     1e4
5       50      0.5     1e5
then the commands
GET 2 1   GET 2 2   GET 2 3   GET 2 4
will read `1 2 3 4 5' into vector alpha, `10 20 30 40 50' into beta and so forth. Note that
DEFINE READ file_id 1 LABEL $file_id
will write out `This is an example file' to the current position of the plot pointer (see, e.g. RELOCATE). Incidently, READ ROW omega 5 would set the vector omega to have values `3 30 0.3 1e3'.

The macros hcopy and hmacro make hardcopies of, respectively, the playback buffer and a macro. Both assume that the variables device and printer are set. device is set from your `.sm' file and by the dev macro; printer is assumed set in `.sm'. (See `startup' file above). If all is well, the macros switch to device printer (with an argument to specify which sub-printer is desired. We have so many laser printers here...), execute the desired commands, and return to the initial device. When the printer device is closed, hardcopy will result. Note the use of \n to ensure that no nasty things happen; if there were no \n and the buffer ended with LABEL Hi, the plot could appear with a label Hi device tek4010. The versions of hcopy and hmacro given here accept a variable number of arguments (`13' means up to 3 arguments). The first (if present) is taken to be the desired laser printer @footnote #{Actually, if the environment (VMS: logical) `.sm' variable PRINTER is defined the macros pretend that it was the first argument, so you can simply type hcopy.} , the next argument is the number of the first line that you want played back, and the third is the last line number. (If you omit both line numbers you'll get the whole buffer; if you omit the second you'll just get the one line). The macro sees what it has been given by using $? to see which variables are defined, and acts accordingly. Hmacro is somewhat similar, except that if you omit an argument it is taken to be the macro name, and a temporary one is created for you. The playback macro deals with its arguments in a similar way, and is discussed further in the examples at the end of this section.

load enables you to read a set of macros from a directory specified as macro in your environment file. Load2 is similar, but it looks in directory macro2. The macro unload (not listed here) will undefine the loaded macros. Note that a list of all the loaded macros is kept in $mlist, which is used by the sav macro to avoid SAVEing lots of system macros. Sav is written in terms of a macro _save so that it won't itself be forgotten (by MACRO DELETE) while in the middle of saving macros.

If you want to put errorbars on logarithmic plots, logerr is the macro you've been looking for. It calculates the correct length for the errorbars, and plots them de-logging and re-logging as appropriate.

The macros rel and dra illustrate a method of using expressions, rather than numbers, in the commands RELOCATE and DRAW. There are Good Reasons why DRAW won't accept an expression directly (see section The Command Interpreter). These macros exploit the fact that the arguments to a macro are whitespace delimited, so a string such as 1+2/$x comprises one argument. Redefining the arguments means that the macros don't have to define, and then delete, a couple of variables to hold the expressions.

Now that you have had your appetite whetted, we strongly recommend that you take the time to look through the other macros that are available (see section The System Macro Libraries). Otherwise how would you know that there are macros to draw arrows on plots, do KS and Wilcoxon tests on vectors, and a host of other good things?

Go to the previous, next section.