Contents Index Summary Citation Doornik

Ox Language Tutorial

Contents:
Introduction
A first Ox program
Running the first Ox program
Linking multiple files
Variables, types and scope
Functions and function arguments
The for and while loops
The if statement
Operations and matrix programming
Arrays
Object-oriented programming
Style and Hungarian notation
Optimizing for speed
Tables:
Hungarian notation prefixes
Hungarian notation, case sensitivity


Introduction

This chapter will give a brief overview of the important elements of the Ox language. A more formal description of the Ox syntax is in the syntax chapter.

The next section will introduce the first Ox program, showing the similarity between the syntax of Ox and that of the C language. (the standard reference for C is Kernighan and Ritchie, 1988) We shall see that a program always includes header files to define the standard library functions, and that it must have a main function, which is where program control starts. We shall also see that the body of the function is enclosed in curly braces.


A first Ox program

Ox is an object-oriented matrix language with a syntax similar to the C and C++ languages. This similarity is most clear in syntax items such as loops, functions, arrays and classes. A major difference is that Ox variables have no explicit type, and that special support for matrices is available. A comprehensive matrix function library is provided with Ox.

The advantages of object-oriented programming are that it potentially improves the clarity and maintainability of the code, as well as reducing coding effort through inheritance. Several useful classes are provided with Ox.

An Ox program consists of one or more source code files. As a first example consider the following small program (the code is in \ox\samples\myfirst.ox):

#include <oxstd.h> // include the Ox standard library header main() // function main is the starting point { decl m1, m2; // declare two variables, m1 and m2 m1 = unit(3); // assign to m1 a 3 x 3 identity matrix m2 = <0,0,0;1,1,1>;//m2 is a 2 x 3 matrix, the first row // consists of zeros, the second of ones print("two matrices", m1, m2); // print the matrices }

Running this first program will produce the following result:

two matrices 1.0000 0.00000 0.00000 0.00000 1.0000 0.00000 0.00000 0.00000 1.0000 0.00000 0.00000 0.00000 1.0000 1.0000 1.0000

The default extension of an Ox source code file is .ox, so the file name of this program could e.g. be myfirst.ox. The next section explains how to run the Ox program on your system. First we consider some aspects of the program.

One important aspect of the C, C++ and Ox languages emerges from the program: array elements start at zero, so [0][0] is row 0, column 0, and [1][2] is row 1, column 2. This convention is also adopted the Ox language (but could be changed, see under pragmas.

The advantage of Ox over C here is that we can directly work with matrices, and do not have to worry about memory allocation and deallocation.


Running the first Ox program

Although you might not run Ox under MS-DOS, please read the next section anyway, as much of the information in there is relevant for other Ox versions.

MS-DOS compiler

The Ox compiler under MS-DOS is called oxl; starting it without arguments lists the command line options. Provided Ox has been properly installed, you can type: oxl myfirst to run the myfirst.ox program (the .ox extension is automatically appended). The output appears on screen. It can be redirected to the file myfirst.out as follows: oxl myfirst > myfirst.out Dynamic link libraries (DLL) are not supported under MS-DOS.

Linux, Sun, HP compilers

The Ox compiler under Linux is called oxlinux. The Ox compiler on the Sun (SunOS 4.1) is called oxsun, the version for the Hewlett-Packard workstation oxhp. Both have the same command line syntax as the MS-DOS compiler. Unlike the MS-DOS compiler, they cannot display graphs on screen (but graphs can be saved to a disk file and viewed with GhostView). Dynamic link libraries (DLL) are not yet supported on the Sun. It is possible to statically relink Ox with additional source code.

Windows command-line compiler

The Ox command-line compiler for Windows NT/Windows 95 is called oxlw. It has the same command line syntax as the MS-DOS and Linux compilers. Like the Linux compiler, it cannot display graphs on screen (but can save graphs to disk). Dynamic link libraries (DLL) are supported under Windows.

Windows compiler (OxRun)

OxRun is a small Windows front end to Ox. It offers the same services as the command-line compilers. The main dialog items are:
Filename:
the Ox program to compile
Link:
the object files to link in e.g. file1+file2+file3
Include/link path:
this field corresponds to the -I command line switch in Oxl, e.g.: c:/ox/include
Define:
arguments for the -D command line switch
OxRun remembers previously run programs, and has a Browse button. Most importantly, it activates GiveWin, and text and graphics output from the Ox program will appear in GiveWin. To abort a program run using OxRun, use end task on the task manager (closing GiveWin or a GiveWin window will stop the output, but not the program).

OxRun and GiveWin are not part of the basic release of Ox.

Running programs with graphics

Several types of graphs are readily produced in Ox, such as graphs over time of several variables, cross-plots, histograms, correlograms, etc. Although all graph saving will work on any system supported by Ox, the result on screen will not always be identical.

A graph can be saved in various formats:

encapsulated PostScript (.eps),
PostScript (.ps), and
GiveWin graphics file (.gwg).
When using GiveWin, graphs can also be saved in Windows Metafile format (.wmf), and copied to the clipboard for pasting into wordprocessors.

MS-DOS graphics

ShowDrawWindow switches the system to graphics mode and shows the graph on screen. Use CloseDrawWindow to switch back to text mode. If you forget this, use the MS-DOS mode command to switch back, e.g.: mode co80

Linux graphics

Oxlinux, etc. cannot display graphics, but can save graphics.

Windows graphics from the command-line

Oxlw cannot display graphics, but can save graphics.

Windows graphics (OxRun and GiveWin)

Text and graphics output from the Ox program will appear in GiveWin. There, text and graphs can be edited further, or copied to the clipboard for pasting into other programs.


Linking multiple files

The source code of larger projects will often be spread over several source files. Usually the .ox file containing the main function is only a few tens of lines. We have already seen that information about other source files is passed on through included header files. However, to run the entire program, the code of those files needs to be linked together as well. Ox offers various ways of doing this. As an example, consider a mini-project consisting of two files: a source code file and a header file. The third file will contain the main function. All files in the examples are in \ox\samples.

File 1: myfunc.ox

#include <oxstd.h> // calls counter, initialize to 0 static decl iCalls = 0; MyFunction(const ma) { ++iCalls; // increment calls counter print("MyFunction has been called ", iCalls, " times and prints:", ma); }

File 1: myfunc.h

MyFunction(const ma); The header file myfunc.h declares the MyFunction function, so that it can be used in other Ox files. Note that the declaration ends in a semicolon. The source code file contains the definition of the function, which is the actual code of the function. The header of the definition does not end in a semicolon, but is followed by the opening brace of the body of the function. The iCalls variable is declared outside any function, making it an external variable. Here we also use the static type specifier, which restricts the scope of the variable to the myfunc.ox file: iCalls is invisible anywhere else (and other files may contain their own iCalls variable). Without the static specifier, the iCalls variable can be seen from any other source file, provided it is declared in the header file. Local variables, such as m1 and m2 in myfirst.ox are called automatic. Their life starts when they are declared, and finishes at the closing brace (matching the brace level of declaration).

Including the code into the main file

The first way of combining the mini project with the main function is to #include the actual code. In that case the myfunc.h header file is not needed:

File 3a: mymaina.ox

#include <oxstd.h> #include "myfunc.ox" main() { MyFunction("one"); } The result will be just one code file, and mymaina.ox can be run, e.g. as oxl mymaina.

Separate compilation and linkage

Ox source code files can be compiled into Ox object files. These files have the .oxo extension, and are binary. The format is identical across operating systems, but since they are binary, transfer from one platform to another has to be done in binary mode.

File 3b: mymainb.ox

#include <oxstd.h> #include "myfunc.h" main() { MyFunction("one"); } The second way of running the project is to first compile myfunc.ox into myfunc.oxo. The next step is to compile mymainb.ox and link the two together. First compile myfunc.ox into an Ox object file using the -c switch: oxl -c myfunc This creates myfunc.oxo (the .oxo extension is automatically appended). (Such files are binary, and cross-platform compatible.) Remember that a new myfunc.oxo needs to be created every time myfunc.ox changes. Next run mymainb.ox, linking in myfunc.oxo: oxl mymainb -lmyfunc The -l switch specifies the files to link in; with an additional myfunc2.oxo for example: oxl mymainb -lmyfunc+myfunc2

Separate compilation, including link file

Finally, object files to be linked in can be included, comparable to including source code files. For this, the link pragma is used:

File 3c: mymainc.ox

#include <oxstd.h> #include "myfunc.h" #pragma link("myfunc.oxo") main() { MyFunction("one"); } When mymainc.ox is run, the Ox object file myfunc.oxo is linked in at the specified place, and this file can be run, e.g. as oxlw mymainc.ox.


Variables, types and scope

Variables are declared using the decl keyword. Unlike C, variables are implicitly typed. This means that variables do not have a type when they are declared, but get a type when values are assigned to them. So a variable can change type during its lifetime. The most important implicit types are int for an integer value, double for a real number, string for a text string and matrix for a matrix (two-dimensional array) of real numbers.

The next Ox program illustrates implicit declaration and scope:

#include <oxstd.h> main() { decl i, d, m, s; i = 1; // assign integer to i --> i is of type int d = 1.0; // assign real number to d --> d is double s = "some text"; // assign string to s --> s is string m = zeros(3,3); // assign to m a 3 x 3 matrix of zeros // --> m is of type matrix print("i=", i, " d=", d, " s=", s, "\nm=", m); }

This prints (\n is the newline character):

i=1 d=1 s=some text m= 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000

The scope of a variable refers to the parts of the program which can see the variable. This could be different from its lifetime: a variable can be `alive' but not `seen'. If a variable is declared outside any function, its scope is the remainder of the source file. It is possible to export such variables to other source files, as we shall see shortly.

Variables declared inside a function have scope until the closing brace of the level at which it is declared. The following example illustrates:

#include <oxstd.h> decl mX; // external variable main() { decl i = 0; // local variable { decl i = 1, j = 0; // new i mX = ones(3,3); print("i=", i, " j=", j); // prints: i=1 j=0 } // brace end: local i and j cease to exist print("\ni=", i); // revert to old i, prints: i=0 }

The variable mX (here we use Hungarian notation, see Hungarian notation prefixes), can be seen everywhere in the main function. To make sure that it can never be seen in other source files, prefix it with the word static. It is good programming practice to use static in such cases, because it is very useful to know that it is not used in any other files (we may than rename it, e.g., without any unexpected side effects). An example was given in myfunc.ox on page \pageref{myfunc.ox}.

It is also possible to share variables between various source files, although there can be only one declaration (physical allocation) of the shared variable. The following modifications would do that for the myfunc.ox program: (1) delete the static keyword from the declaration, (2) add to myfunc.h the line:

extern decl iCalls;

Then any code which includes myfunc.h can reference or change the iCalls variable.


Functions and function arguments

We have already used various functions from the standard library (such as print, ones and zeros), and written several new ones (MyFunction, various main functions). Indeed, an Ox program is primarily a collection of functions. It is important to know that all function arguments are passed by value. This means that the function gets a copy which it can change without changing the original. For example:

#include <oxstd.h> func(mA) { mA = zeros(1,2); print("ma in func()", mA); } main() { decl ma; ma = ones(1,2); print("ma before func()", ma); func(ma); print("ma after func()", ma); }

which prints:

ma before func() 1.0000 1.0000 ma in func() 0.00000 0.00000 ma after func() 1.0000 1.0000

If the function argument is not changed by the function, it is good programming style to prefix it with the const keyword, as in:

func(const mA) { print("ma in func()", mA); }

Then the compiler can generate much more efficient code, especially for matrices and strings.

Of course it is possible to return changed values from the function. If there is only one return value, this is most simply done by using the return statement:

#include <oxstd.h> func(const r, const c) { return rann(r, c); // return r x c matrix of random } // numbers from standard normal main() { print("return value from func():", func(1,2) ); }

Another way is to pass a pointer to the variable, rather than the variable itself, as for example in:

#include <oxstd.h> func(const pmA) { pmA[0] = zeros(1,2); print("ma in func()", pmA[0]); } main() { decl ma; ma = ones(1,2); print("ma before func()", ma); func(&ma); print("ma after func()", ma); }

which prints:

ma before func() 1.0000 1.0000 ma in func() 0.00000 0.00000 ma after func() 0.00000 0.00000

Now the change to ma is permanent. The argument to the function was the address of ma, and func received that address as a pointer. In C one could reference what is pointed to as *pmA or pmA[0]. In Ox only the latter is possible; we modified what that pointer referred to by assigning a value to pmA[0]. When func has finished, ma has been changed permanently. Note that we gave the argument a const qualification. This was possible because we did not change pmA itself, but what pmA pointed to.


The for and while loops

Since Ox is a matrix language, there is much less need for loop statements than in C or \Cpp. Indeed, because Ox is compiled and then interpreted, there is a speed penalty for using loop statements when they are not necessary.

The for, while and do while loops have the same syntax as in C. The for loop consists of three parts, an initialization part, a termination check, and an incrementation part. The while loops only have a termination check.

#include <oxstd.h> main() { decl i, d; for (i = 0; i < 5; ++i) { d = i * 0.01; print(d, "\n"); } }

which prints:

0 0.01 0.02 0.03 0.04

This could also be written, less elegantly, using while as follows:

#include <oxstd.h> main() { decl i, d; i = 0; while (i < 5) { d = i * 0.01; print(d, "\n"); ++i; } }

It is not uncommon to have more than one loop counter in the for statement, as the following code snippet illustrates:

decl i, j; for (i = 0, j = 10; i < 5 && j > 0; ++i, --j) print(i * j, "\n");

The && is logical-and, whereas || is logical-or. The ++i statement is called (prefix) incrementation, and means `add one to i'. Similarly, --j subtracts one from j. There is a difference between prefix and postfix incrementation (decrementation). For example, the second line in

i = 3; j = ++i;

means: add one to i, and assign the result to j, which will get the value 4. But

i = 3; j = i++;

means: leave the value of i on the stack for assignment, then afterwards increment i. So j will get the value 3. In the incrementation part of the for loop it does not matter whether you use the prefix or postfix form.


The if statement

The if statement allows for conditional program flow. In the following example we draw a uniform random number. Such a random number is always between zero and one. The ranu returns a matrix, unless we ask it to generate just one number. Then it returns a double, as is the case here.

decl d = ranu(1,1); if (d < 0.5) print("less than 0.5\n"); else if (d < 0.75) print("less than 0.75\n"); else print("greater than 0.75\n");

Again, braces are used to group multiple statements together. They should also be used when nesting if statements, to avoid confusion about which else belongs to which if.

decl d1 = ranu(1,1), d2 = ranu(1,1); if (d1 < 0.5) { print("d1 is less than 0.5\n"); } else { if (d2 < 0.75) print("d1 >= 0.5 and d2 < 0.75\n"); else print("d1 >= 0.5 and d2 <= 0.75\n"); }

The if part is executed if the expression evaluates to a non-zero value (true). The else part otherwise, i.e. when the expression evaluates to zero (false: either an integer 0, or a double 0.0). Some care is required when using matrices in if statements. A matrix expression is a true statement if all elements are true (non-zero). Even if only one element is zero, the matrix expression is false, so

#include <oxstd.h> main() { if (ones(2,2)) print("yes"); else print("no"); if (unit(2)) print("yes"); else print("no"); if (zeros(2,2)) print("yes"); else print("no"); }

prints: yesnono.

There are two forms of relational operators. There is < <= > >= == != meaning `less', `less than or equal', `greater', `greater than or equal', `is equal' and `is not equal'. These always produce the integer value 1 (true) or 0 (false). If any of the arguments is a matrix, the result is only true if it is true for each element:

#include <oxstd.h> main() { if (ones(2,2) == 1) print("yes"); // true for each else print("no"); // element if (unit(2) == 1) print("yes");//not true for each else print("no"); // element if (zeros(2,2) == 1) print("yes");//not true for each else print("no"); // element }

prints: yesnono.

The second form are the dot-relational operators .< .<= .> .>= .== .!= meaning `dot less', `dot less than or equal', `dot greater', `dot greater than or equal', `is dot equal' and `is not dot equal'. If any of the arguments is a matrix, the result is a matrix of zeros and ones, with each element indicating the relevant result.

The any library function returns 1 (true) if any element of the matrix is non-zero, so that yesyesno will be printed by:

#include <oxstd.h> main() { if (any(ones(2,2))) print("yes"); else print("no"); if (any(unit(2))) print("yes"); else print("no"); if (any(zeros(2,2))) print("yes"); else print("no"); }

To conclude: you can test whether all elements of a matrix m are equal to one (say) by writing: if (m == 1). To test whether any element is equal to one: if (any(m .== 1)). The expression if (m != 1), on the other hand, is only true if none of the elements is equal to one. So, use if (!(m == 1)) to test whether it is true that not all elements are equal to one.


Operations and matrix programming

To a large extent, the same operators are available in Ox as in C or C++. Some of the additional operators are power (^), horizontal concatenation (~), vertical concatenation (|) and the Kronecker product (**). One important distinction is that the operators are also available for matrices, so that, for example, two matrices can be added up directly. For some operators, such as multiplication, there is a distinction between the dot operators (e.g. .* is element by element multiplication and * is matrix multiplication if both arguments are matrices). Not available in Ox are the bitwise operators, instead you need to use the library functions binand and binor.

Because Ox is implicitly typed, the result type of the expression will depend on the types of the variables in the expression. When a mixture of types is involved, the result is promoted upwards in the order integer, double, matrix. So in an expression consisting if an integer and a double, the integer will be promoted to a double. An expression of only integers yields an integer. However, there are two important exceptions to this rule:

To illustrate, we write the Fahrenheit to Celsius example of Kernighan and Ritchie (1988) in Ox:

#include <oxstd.h> const decl LOWER = 0; const decl UPPER = 100; const decl STEP = 20; main() { decl fahr; for (fahr = LOWER; fahr <= UPPER; fahr += STEP) print("%3d", fahr, " ", "%6.1f", (5.0/9.0) * (fahr-32), "\n"); }

which prints:

0 -17.8 20 -6.7 40 4.4 60 15.6 80 26.7 100 37.8

In C we have to write 5.0/9.0, because 5/9 evaluates to zero. In Ox both expressions would be evaluated in floating point arithmetic.

In general we get more more efficient code by vectorizing each program as much as possible:

#include <oxstd.h> const decl LOWER = 0; const decl UPPER = 100; const decl STEP = 20; main() { decl fahr; fahr = range(LOWER, UPPER, STEP)'; print("%6.1f", fahr ~ (5.0/9.0) * (fahr-32) ); }

The program prints a table similar to the earlier output:

0.0 -17.8 20.0 -6.7 40.0 4.4 60.0 15.6 80.0 26.7 100.0 37.8


Arrays

The Ox syntax allows for arrays, so you may use, for example, an array of strings (often useful), an array of matrices, or even an array of an array of matrices (etc.). The following program gives an example.

#include <oxstd.h> const decl MX_R = 2; const decl MX_C = 2; main() { decl i, asc, asr, m; asr = new array[MX_R]; asc = new array[MX_C]; for (i = 0; i < MX_R; ++i) asr[i] = sprint("col ", i); for (i = 0; i < MX_R; ++i) asc[i] = sprint("row ", i); m = ranu(MX_R, MX_C); print("%r", asr, "%c", asc, m); }

which prints

row 0 row 1 col 0 0.020192 0.68617 col 1 0.15174 0.74598


Object-oriented programming

In some of the literature of recent years it has been claimed that object-oriented programming would solve all programming problems. My claims here are more modest. I see it as a useful addition, but believe thoughtless application could actually result in less readable programs. You may completely ignore the object-oriented features. However, you will then not be able to use the preprogrammed classes for data management and simulation. It is especially in the latter task that I found a considerable reduction in the required programming effort after writing the base class.

One of the drawbacks of C++, as compared with C, is that it is a much larger and considerably more complicated language, with all the extra overhead used for the object-oriented features. The standardization of C++ is also as yet an unfinished process. Ox only implements a subset of the C++ features. I tend to see that as a benefit rather than a drawback.

The class is the main vehicle for object-oriented programming. A class is nothing more than a group of variables (the data) and functions (the actions) packaged together. This makes it a supercharged struct (or record in Pascal terminology). Inheritance allows for a new class to add data and functions to the base class, or even redefine functionality of the base class.

In Ox, all data members of the class are private (only visible to class members), and all function members are public. Like C++, Ox has the virtual keyword to define functions which can be replaced by the derived class. Classes are used by dynamically creating objects of that class. No static objects exist in Ox. When an object is created, the constructor function is called, when the object is deleted, the destructor function is called.

The encapsulation of data in classes removes the need for global variables. This is an important advantage: a proliferation of global variables makes programs very difficult to maintain. That is also why it is important to make external variables which are specific to one file static.

More information on object-oriented programming is given in the classes section.


Style and Hungarian notation

The readability and maintainability of a program is considerably enhanced when using a consistent style and notation, together with proper indentation and documentation. Style is a personal matter; this section describes the one I have adopted.

In my code, I always indent by four spaces at the next level of control (i.e. after each opening brace), jumping back on the closing brace.


Table tut.1: Hungarian notation prefixes

prefix type example i integer iX c count of cX f boolean (integer flag) fX (b is also used) d double dX m matrix mX v vector (1 by n matrix) vX s string sX a array as array of strings asX am array of matrix amX p pointer (function argument) pX m_ class member variable m_mX g_ external variable with global scope g_mX

I have found Hungarian notation especially useful (see e.g. Petzold, 1992, Ch. 1). Hungarian notation involves the decoration of variable names. There are two elements to Hungarian notation: prefixing of variable names to indicate type (Table tut.1), and using case to indicate scope (Table tut.2, remember that Ox is case sensitive).


Table tut.2: Hungarian notation, case sensitivity

function all lowercase function (exported) first letter uppercase static external variable type in lowercase, next letter uppercase exported external variable as above, but prefixed with <tt>g_</tt> function argument type in lowercase, next letter uppercase local variables all lowercase constants all uppercase

As an example consider:

#include <oxstd.h> const decl MX_R = 2; /* a constant */ decl g_mX; /* exported matrix */ static decl iCount; /* static external variable */ static func1(const pdX) /* argument is pointer to double */ { } /* exported function */ Func2(const mX, const asX, const cT, const cX) { decl i, m; }

Func2 expects a cT by cX matrix, and corresponding array of cX variable names. The c prefix is used for the number of elements in a matrix or string. Note however, that it is not necessary in Ox to pass dimensions separately. You can ask mX and asX what dimensions they have:

Func2(const mX, const asX) { decl i, m, ct, cx; cx = columns(mX); ct = rows(mX); if (cx != sizeof(asX)) print("error: dimensions don't match"); }


Optimizing for speed

Ox is very fast: current benchmarks suggest that it is faster than several other commonly used matrix language interpreters. A program can never be fast enough though, and here are some tips to achieve higher speed:


References

Kernighan, B.W. and Ritchie, D.M. (1988). The C Programming Language 2nd Ed. Englewood Cliffs, NJ: Prentice Hall.

Petzold, C. (1992). Programming Windows 3.1. Redmond: Microsoft Press.


Ox version 1.11. This file last changed 5-Aug-1996.