It is general-purpose programming language with extensive numerical computation capabilities; it's like a Python with many innovative features and a built-in Matlab; functions are all nameless values; functions can have parameters, variables have domains, there's conditional class in addition to ordinary class, etc.
You can do it in three ways:
run("prog.x");The extension of the program file is not important.
For the most part it is, but line breaks are not completely equivalent to whitespaces.
The basic syntax is like that of C. For example, assignment operator is =, equality test operator is ==, white space is insignificant, etc.
Of course it has some of its own rule coming with the many new features. The principle of the syntax design is to make the language as clear and readable as possible. In particular, we try to make every syntax rule "explainable". For example, x # 3 is the element No. 3 of a list x, A[\] is the main diagonal of a matrix, p>> is the data that pointer p points to, etc. In Shang language, there are no seemingly meaningless symbols like __, ___, @@, and are there are no non-word keywords.
The worksheet mode is like a full screen editor. You can move the cursor around and edit the previously entered commands and rerun them. The console mode works like a unix terminal. Previously entered commands and the output cannot be edited (you can browse the command history using up and down arrows and bring copies of previously commands to the current command buffer).
UNICODE support is under development. Right now the command window can only display ASCII text. A string is treated as a null terminated array of bytes. Although the users may be able to store UTF-8 encoded unicode text in a string and do the processing by themself, the interpreter assumes that each single byte is a character.
If a statement is terminated with a new line or a comma, then the outcome of the computation is displayed. If a statement is terminated by a semicolon, then the outcome is suppressed. For example
>> x = 5, y = 8; z = 9 5 9Normally a statement should end with a semicolon to prevent unwanted printing.
The opening statement of a compound statement (such as an if or for block) cannot be terminated by a semicolon. For example, both
s = 0; N = 100; for k = 1 : N, s += k; end;and
s = 0; N = 100; for k = 1 : N s += k; end;are OK, while
for k = 1 : N; s += k; end;will have trouble getting interpreted.
The closing statements of a do-while or a do-until compund statement have to be terminated with a semicolon. For example
s = 0; k = 0; do s += k; while k // code end while yy;
The interpreter knows that the first while is not corresponding to do since it is terminated with a newline.
To create a local variable, use the syntax name = value. For example, x = 3. To create a global variable, use the syntax global.name = value. For example, global.y = 3. The value of a variable can be updated using the assignment operator.
If the variable is created with the global keyword, like in global.x = 100, it is a global variable and can be accessed anywhere. Otherwise, it is a local variable and can only be used inside the current function.
There is no lexical scope.
In other words, if function g is defined inside f, g does not have access to the local variables of f. A function can only access global variables and its own local variables, and does not inherit the local variables of the parent function. However, function parameters can be used to pass information to a function.
Type is not important. A variable is not tied with a type, but it has a domain, which is a set. Domain is a more powerful concept than type, which gives the language the strenghs and advantages of those differently typed languages.
B will obtain the value of A, but B and A will be independent. For example,
A = "his shadow"; B = A; B[1] = "H";then A still remains "his shadow".
Not always. The value assigned to a variable must be inside the domain of the variable. If the domain is not specified when the variable is first created, it obtains the default domain, which is the set of everything, and thus any value is allowed.
In Shang, there's no such a distinction, since it's the value not the reference of an object that's stored in a variable. If you want to make a variable unmodifiable or restrict the possible values it can assume, you can specify a domain for it.
Use domain:
A = 3 in {3};
The operator for testing equality is ==. Use
if a == b ... end
A matrix with at least one non-zero element, a list whose length is at least 2, or a non-empty character string evaluates to true. A matrix with only zero elements, the empty matrix [], the empty list (), and the empty string "" evaluate to false.
Note that (), [], and null are the same thing.
No. A function is a nameless value just like a number. It can be assigned to a variable. The name of the variable can act like the name of the function. However, functions can have parameters, and if desired, a parameter can be used for descriptave purpose. For example
f = function [name = "sample function"] x -> y ... endThe function does not have a name. f is the name of the variable, and name is a parameter. f.name returns sample function.
To use a function inside another function, it needs to be defined as a global variable. For example,
global.f = x -> x^2+1; g = x -> 1 - f(x); // use f inside g
If the function has only one return value and it can be expressed by a simple formula, then function can be created with one simple statement like
f = x -> sqrt(x^2+1) or g = (x, y) -> sqrt(x^2+y^2+1);If the function definition involves multiple statements, the keyword function and end must be used. Here's the basic syntax:
f = function x -> y // code endThe input value x and the output value y can be used like local variables, and the return value y should be assigned a value inside the function,
Shang is strictly calling by value. However, calling by reference can be emulated by using pointer.
If a function is defined by
f = function (x = 3, y = 5, z = 6) -> w ... endthen you can call it by naming the arguments, e.g., f{z=8, y=2}. Here x will get the default value. Note that her curly braces must be used.
Function arguments are the values passed to the function when it is called. For example in function call f(128), 128 is the value of the argument.
Function parameters are the attributes of the function which can be altered to modify the behavior of the function. It is not passed to the function when it's called. For example, if a is a parameter of function f, then f.a returns the value of a, and f.a = -1 changes the parameter value to -1.
A parameter is an attribute of a function that can be any type of value. The parameter value is statically associated with the function and needs not be passed when calling the function. For example
f = function [alpha = 1, beta = 2] (x, y) -> z z = alpha * x + beta * y - 1; end
defines a function with two parameters alpha and beta, which have default values 1 and 2. So a call to the function f(x,y) would return alpha * x + beta * y - 1.
Outside the function, a parameter value is accessed as if it were a class member attribute. For example f.alpha would return the value of alpha parameter of function f.
Inside the function body, the value of a parameter can be accessed using the keyword this. For example
f = function [alpha = 1, beta = 2] (x, y) -> z z = (this.alpha) * x + (this.beta) * y - 1; endIf the function doesn't have a local variable named alpha, then the keyword can be omitted, and alpha would refer to this.alpha. For example, the above function is equivalent to
f = function [alpha = 1, beta = 2] (x, y) -> z z = (this.alpha) * x + (this.beta) * y - 1; end
There are several access types of parameters. The most common ones are public and private parameters. For example, the following function has a public parameter alpha and private parameter beta
f = function [public alpha = 1, private beta = 2] (x, y) -> z z = (this.alpha) * x + (this.beta) * y - 1; endNote that if the access type is not specified, then it is treated as a public. For example, in the following
f = function [public alpha = 1, beta = 2, private gamma = 3] (x, y) -> z z = (this.alpha) * x + (this.beta) * y - this.gamma; endbeta is treated as a public parameter.
A public parameter can be modified outside the function but not inside the function. For example, if alpha is a public parameter, then
f.alpha = -3;would change the value of alpha to -3.
A private parameter can be modified inside the function but not outside the function. For example, if gamma is a private parameter of f, then f.gamma = -3 would not be allowed. Inside the function body
this.gamma = -3;would change the value of gamma to -3. Note that in the following code
f = function [public alpha = 1, beta = 2, private gamma = 3] (x, y) -> z gamma = -3; z = (this.alpha) * x + (this.beta) * y - this.gamma; endthe statement gamma = -3 doesn't modify the private parameter (if you want to do that, you need to use this.gamma = -3. Instead, it creates a local variable gammma.
A parameter can have a domain. For example
f = function [public alpha = 0.5 in (0 to 1), beta = 2, private gamma = 3] (x, y) -> z gamma = -3; z = (this.alpha) * x + (this.beta) * y - this.gamma; endwhere the domain of public parameter alpha is interval [0, 1]. Any value assigned to it must be in this domain. Statement like f.alpha = 3 will not be allowed.
When a function is defined, a default value can be assigned to a formal argument. For example
f = function (x = 3, y = 5) -> z ... end
The function f can then be called in the following ways
f() // equivalent to f(3,5) f(x) // equivalent to f(x, 5) f(*, y) // equivalent to f(3, y)
When a function f is called, some arguments are passed a dot instead of an acutal value, the result is a new function. For example, for a function f(x,y) of two variables the call
f(., 1)returns a new function g such that g(x) = f(x, 1)
The RHS of arrow in the function header can be a pair of parentheses enclosing several variable identifiers, such as
global.f = function (x1, x2) -> (y1, y2, y3) ... ... endInside the function, y1, y2, and y3 are treated as local variables, and their values are returned when the function call is finished. f(x1,x2) will return a list. Or one can use a simultaneous assignment
(y1,y2,y3) = f(x1,x2);
These "function functions" don't need to worry about this at all and can expect functions of a determined signature. The functions being passed as arguments can take care of themselves. There are two ways to do this: partial substitution or function parameter. Say you have a function defined by
f = function (t, y, alpha, beta) -> yp ... endThen the partial substitution
f(., ., 0.5, 0.7)is a function that expects only two arguments t and y. (alpha and beta are set to 0.5 and 0.7)
Or you may define your f in the following way
f = function [alpha = 0.5, beta = 0.7] (t, y) -> yp ... endf is now a function that expects two arguments t and y. It has two external parameters alpha and beta than may be modified like this
f.alpha = 0.8; f.beta = 2;Or you can spawn a brand new function using f
g = f[0.8, 2]
A pointer is a value which stores the necessary information for accessing another value. There are two types of pointers: the one that points to a local variable, and the one that points to anonymous data. For example
s = "Sammi"; p = >>s; // p points to local variable s q >>= "Cheng"; // q points to value "Cheng"
p is a pointer that points to a local variable s, while q is a pointer that points to a piece of anonymous data.
To obtain the value pointed to by pointer p, use
p>>If a pointer is to be returned by a function, it must point to anonymous data, not a local variable.
Passing by reference can be emulated using pointers. For example
f = function (xp, k, v) -> () xp>>[k] = v; end x = [1, 3, 5]; f(>>x, 2, 7);Now x becomes [1, 7, 5]
Swap two values
swap = function (xp, yp) -> () temp = xp>>; xp>> = yp>>; yp>> = temp; end a = "aaa"; b = "bbb"; swap(>>a, >>b);Now a becomes "bbb" and b becomes "aaa"
A plainly entered number like 23 or -7.23 is a floating point number of double precision.
A number with postfix z or Z, such as 35Z or -227z is an integer (size depending on the machine)
A number with postfix L is a long integer, which can have as many digits as the system memory allows, such as 253392892763855283922227759L.
A number with postfix M is a multiple precision floating point number. Such as 3.14159265358979323846264338327950M. Its precesion is determined by global variable global.mpf_ndigits.
A number can be entered literally in decimal, binary, or hexadecimal format. For binary and hex format, the prefixes 0b, 0B, 0x, or 0X should be used.
>> x = 1011.11 /* decimal format */ 1011.11>> y = 0B1011.11 /* binary format */ 11.75
>> z = 0X1011.11 /* hex format */ 4113.0664060
>> w = -0Xabcd.ab /* hex format */ -214375.2578
A string literal is entered with double quotes, such as x = "His shadow". Individual character or substring can be extracted as if the string were a vector. For example, x[3], x[1:5], etc. The result of the indexing expression is still a string. To compare two string use the operator ==, or use strcmp function.
A regular expression is a type of value and thus can be assigned to a variable. For example,
r = ~/zz*/;To match a regular expression r against a string s, just use it like a function
r(s);Or use the match attribute of r, r.match(s).
If there is no match, empty matrix [] will be returned. Otherwise, the location (starting and ending indices) where the match occurs is returned. If the return vector has more than two values, those index pairs after the first pair are the indices of captured sub patterns.
Use x in set, where set can be the built-in functions \_D, \_Z, \_B, \_M, \_L, \_S, \_C, \_string,
Use convert(x, set) if you want to convert value s into type set. Here set can be: \_D, \_Z, \_B, \_M, \_L, \_S, \_C, \_string,
Use convert(s, _D), convert(s, _Z), or sscanf
Use convert(x, _string) or sprintf
sscanf extracts data from a string and return a list. For example,
s = "22, 88, 3369"; sscanf(s, "%d, %d, %d");
fscanf does similar job from a file.
Use the square brackets [] or built-in functions such as zeros, ones, rand.
If a is a vector, the last element is a[$].
See the following examples
A[5] // element #5 A[1 : 5] // element #1 to #5 A[2 : 2 : 8] // element #2, 4, 6, 8 A[$] // last element of A A[5 : $] // element #5 to the last one A[1, 3] // elment at row 1 column 3 A[1 : 5, 3] // element #1 to #5 on column 3 A[1 : 5, 3 : 7] // all elements on rows 1 to 5 and columns 3 to 7 A[1 : 5, :] // all elements on rows 1 to 5 A[:, 3 : 7] // all elements on columns 3 to 7 A[1 : 5; 2 : 6] // elements A[1,2], A[2,3], ..., A[5,6] A[\] // main diagonal of square matrix A A[\, 1] // first superdiagonal of square matrix A A[\, 1 : 5] // first to 5th super diagonals A[\, -1] // first subdiagonal A[\, -3 : -1] // subdiagonals 3 to 1
A(x) is equivalent to A * x, the matrix multiplication of matrix A and vector x. Of course, the sizes of A and x must match for multiplication.
A matrix is an mxn array of numbers.
A vector is simply an 1xn or mx1 matrix.
A list is an array of arbitrary values.
Use the parenthese. For example, x = (3, 5, "she", "sang", [5, 2, 9]). To reference the elements use the operator #. For example x#2, x # (2:5).
Both are lists of length 2, but (3, 5, ~) is extendable (the length can grow).
Any value, if not already a list of two or more values, is treated as a list of one element. For example, 3 and (3) are the same. If x=3, then x#1 is 3. In other words, in Shang, x is the same as (x), the list that contains x alone. And hence x, (x), ((x)), (((x))), ... are all the same.
If new elements are to be added to the list later, it should be created as list of variable length, such as
x = (3,~); x # 2 = 5;If you're not planing to add more items to the list, then the element can be regarded as a list already. Otherwise, you can use v = (x, ~) to create an extendable list with a single element x.
the k-th element of list x is x#k
If x is a vector, like x = [2, 3, 5], then x[1] = 2 and x#1 = [2, 3, 5], since x is also list of a single element.
To create an empty stack, call the stack function
s = stack();
To push an item $x$ into the stack,
s.push(x);
To pop the top item out of the stack
x = s.pop();
To create a queue
q = queue();To add an item x to the queue,
q.enqueue(x);To remove the next available item from the queue
x = q.dequeue();To check the length of the queue
s.length;
A hash table is a collection of data values with each value associated with a key. The value can be retrieved by providing the key. For example
H = {"a" => 96, "b" => 97, 5z => pi};The values stored in the table are 96, 97, and pi, and the keys are "a", "b", and 5z respectively.
A value can be retrieved using the @ operator. For example
>> H @ "b" 97New entries can be added to the table, or old entries can be updated by using assignment. For example
>> H @ "b" = 197;
A hash table can be used as if it were a function. For example
>> H = {"a" => 96, "b" => 97, 5z => pi}; >> H("a") 96 >> H("b") 97 >> H(5z) 3.14159265
the element whose key is k is x@k.
\question What can be used as hash table keys? \answer
Although in principle anything can be used as a key, it's best not to use floating point numbers as keys because two such numbers may appear equal but actually are not.
The best choices for keys are character strings and integers such as orange and 97z.
A set is a collection of distinct values. x in s is true if x is a member of set s. Sets can be realized in a number of different ways.
The domain of x is the set of valid values a variable x is allowed to have. When you reset the value of x, the new value has to be inside its domain.
local variables, global variables, function arguments, function parameters, and class attributes.
If you want x to have a domain S, you must also specify a default value v for x, and v must be inside S. For example
global.x = v in S;f = function [x = 1 in (0 to 1)] x in _R -> y ... end
c = class public x = 1 in _R; ... end
The domain must be specified when the variable is declared (the first time the variable name is assigned a value). If you want to create a global varialbe alpha and want its value to be between 0 and 1, then
global.alpha = 0.5 in (0 to 1);
Once the domain of a global is specified, the domain can not be changed. If it is declared without a domain, then domain cannot be added later.
You can view a class as a set (of all its members). What's special about class is that it can generate its members (by using constructor). So the set represented by the class is being expanded as new members are created. The definition of a class specifies the attributes of its members. When a class member is created, it is endowed with these attributes.
A class can be used as a set, and is considered a special set (which has a constructor and can acquire new members.
Inside an attribute function f (in the definition of the class), use parent.x. Outside the class, if m is a class member, use m.x. For example
global.c = class private a; public b; public seta = function x -> () parent.a = x; end endm = c.new(); c.b = 10; c.seta(100);
The constructor of a class is a collective attribute named \texttt{new}. It must be a function with no output argument. The result of calling the constructor is the creation of a class member. Any local variable of the constructor whose name matches that of a member attribute of the class, will be assigned to the corresponding attribute of the newly created class member. Any attribute of the member whose name is not a local variable of the constructor will be given the default value.
A very simple and effective way to write a constructor is to write a one-liner function. For example
student = class public name = "xxx xxx"; public id = "000000"; public age = 12; public room = "7L"; ... new = (name, id, age) -> (); endIf student.new("Squido", "123456", 13) is evaluated, member of the student is returned with the specified name, id, age. Any attributes not dealt with in the constructor, like room, will reveive the default values.
If no constructor is specified in the class definition, then you can still call the system provided default constructor .new(), which assign the default value to each attribute.
Use a domain. For example, if attribute age is defined by
public age = 30 in (0 to 150);and x is a member of the class, x.age = new_value is allowed only if new_value is between 0 and 150.
public and private attributes may have domains.
A common attribute is shared by all members of the class and cannot be reassigned. It must be a function.
An auto attribute is a common attribute. It is a function that takes no arguments. If attribute f is declared as auto, m.f can be used in place of m.f().
Class validator can be used to prevent the class from entering an illegal state. See the following example.
global.ellipse = class title = "ellipse"; public a = 1 in (0 to inf); public b = 5 in (0 to inf); auto longaxis = () -> max(parent.a, parent.b); auto shortaxis = () -> min(parent.a, parent.b); auto area = () -> pi * parent.a * parent.b; new = (a, b) -> (); endcircle = class super = global.ellipse; title = "circle"; auto radius = () -> parent.a; auto longaxis = () -> parent.radius; auto shortaxis = () -> parent.radius; auto perimeter = () -> 2 * pi * parent.radius; common set_radius = r -> ((parent.a, parent.b) = (r, r)); common validate = () -> (parent.a == parent.b); new = (a, b) -> (); end
u = circle.new(3, 3) u.a = 5; // won't work
Use (x.a, x.b) = (new_value, new_value);
Statements like
(a,b,c) = (1,2,3)or
(x.a, x.b, x.c) = (1, 2, 3)
Note that (a,b,c) = (1,2,3) is equivalent to a=1; b=2; c=3; When (x.a, x.b, x.c) = (1, 2, 3) is being executed, the validator checks the status of x after all the three assignments are performed. If the status is invalid, the changes will be reversed.
A finite set, a function, an interval, a built-in set, or a class.
A conditional class is a collection of loosely connected objects. Unlike a traditional class, it doesn't ``create'' new members using the constructor, but issues membership to members of other classes that meet certain conditions. Such memberships may be cancelled once the conditions are no longer satisfied or the member wishes to withdraw.
For example, ``person'', ``man'', and ''woman'' can be regarded as regular classes, while ``child'', ``customer'', ``pedestrian'', and ``card holder'' are appropriate examples of conditional classes.
A number with suffix M is an Mpf constant.
>> x = 1.3352M 1.33519999999999999999999999999999E0Operations involving Mpf usually produces Mpf results.
>> sqrt(x) 1.15550854605234313688808503023426E0
An Mpf matrix is created if all components in a bracket expression are Mpf's
>> X = [3.1M; 2.2M; -1.55M]The functions zeros, ones, and rand returns Mpf matrices if the last argument is _M. For example3.09999999999999999999999999999999E0 2.19999999999999999999999999999999E0 -1.54999999999999999999999999999999E0
>> x = rand(5, 1, _M) 9.3021775040240436056832347452058E-1 8.5311813443668253119942506400455E-1 7.7238437656903267296393271625706E-1 5.8162992980263383743533686828037E-1 8.8838553079998591821584992499428E-1
The following commands solve a system of linear equations using Mpf
>> A = rand(10, 10, _M); >> b = rand(10, _M); >> x = A \ b; >> norm(A * x - b) 4.7817994155912976409669674814276E-38The default precision of Mpf is 128 binary digits, or about 37 decimal digits. The precision can be changed by assigning a new value to global variable mpf_ndigits. The new value must be a multiple of 32.
>> global.mpf_ndigits = 256; >> r = sqrt(2M); >> r^2 - 2M -3.45446742203777785e-77
Note that Mpf computations are much slower and more resource consuming than double precision computations.
A number with suffix L is a long integer. The magnitude of a long integer is limited only by the system's resources. For example
>> n = 123456789012345678901234567890L 123456789012345678901234567890 >> factorial(50L) 30414093201713378043612608166064768844377641568960512000000000000Be very careful when using long integers, one integer that is too long may use up all the system memory.
Use \inlinecode{zeros\inlinecode\} and specify the last argument as _sparse. Example
x = sparse(100, _sparse); // sparse column vector x = sparse(100, 100, _sparse); // sparse matrixAfter the matrix is created, you can fill in the values as if it were a dense matrix.
A[\, 1 : n-1] = 0
To create a polynomial p(x) = 1-2x+5x^2-0.2x^3, do
p = poly[[1, -2, 5, -0.2]];
or make a copy of poly and then modify the coeff attribute
p = poly; p.coeff = [1, -2, 5, -0.2];
If p is a polynomial, p(x) gives the value. Here x can be a scalar, vector, or matrix.
To find the polynomial interpolation through a set of points, do
p = interp(x, y)The result is a polynomial of Newton form and can be called directly.
To create a spline, use splinefit function. For example
>> x = [-1, 0, 1]; >> y = [1.5, 0, 2.5]; >> s = splinefit(x, y); >> s(x) 1.5 0 2.5As the result shows, the spline s can be called directly. s.coeff and s.xnodes give the coefficient matrix and the nodes of the spline. Note that the following examples give natural spline, periodic spline, not-a-knot spline, and clampled spline respectively
s = splinefit(x, y); s = splinefit(x, y, "cyclic"); s = splinefit(x, y, "not-a-knot"); s = splinefit(x, y, ypl, ypr); // ypl and ypr are the specified derivative values //at the left and right end points
One dimensional integral:
int(f, a, b)
Multi-dimensional integral: f has to be a function of one variable, e.g.,
f = x -> exp(-(x[1]^2 + x[2]^2);int(f, [0, 0], [1, 2])
You need to have an ODE function, which takes t and y as input arguments and returns dy/dt as output values, e.g.,
lorenz = function [sigma = 10, b=8/3, r=28] (t, y) -> yp d1 = sigma * (y[2] - y[1]); d2 = r*y[1] - y[2] - y[1] * y[3]; d3 = y[1] * y[2] - b * y[3]; yp = [d1; d2; d3]; end;// solve the ivp on t=[0, 1] (x, y) = dsolve(lorenz, [0, 1], [8; 9; 10]);
x is the vector of time points, and y is the matrix whose each row is the numerical solution at the corresponding t value.
You may also specify the relative tolerance, absolute tolerance, the type of ode (stidd/non-stiff), and whether a Hermite spline of the numerical solution is to be returned.
A distribution is a function which when called return random numbers. The built-in distributions include: rand,randz, normal, gauss, gammarv, betarv, binomal, chi, exprv,poisson, frv, student, hypergeom, and multinomial.
Each function is a random number generator. For example, normal(3) returns 3 random numbers of normal distribution. The normal function has two public parameters mu and sigma. To create a new normal distribution, you can reset the parameter values
rv = normal; rv.mu = 100; rv.sigma = 15;The normal function also has common parameters mean,stddev,variance,pdf, cdf, prob, quantile. If you want to calculate the probability that a random variable of distribution rv is between 70 and 130, do
>> rv.prob(70, 130) 0.9544997361
An automaton is a function that can pause and restart. When it pauses the flow control is returned to the interpreter, but the values of the local variables and the status and position of the instruction sequence are retained, and when the automaton restarts, the local variable values and the previous execution status will be restored.
An automaton can have a number of ports, which are two-way channels used for data exchange. Data can be manually added to or removed from the ports. The data added to a port form a queque. The ports of the same or different automatons can be connected so that the input channel of one port becomes the output channel of the other.
Copyright © 2011 Xiaorang Li, All rights reserved.
Revised: Tue Jan 10 17:04:13 2012