Programming

(directly go to documentation on : /*, */, //, Prog, [, ], Bodied, Infix, Postfix, Prefix, IsBodied, IsInfix, IsPostfix, IsPrefix, OpPrecedence, OpLeftPrecedence, OpRightPrecedence, RightAssociative, LeftPrecedence, RightPrecedence, RuleBase, RuleBaseListed, Rule, HoldArg, Retract, UnFence, HoldArgNr, RuleBaseArgList, MacroSet, MacroClear, MacroLocal, MacroRuleBase, MacroRuleBaseListed, MacroRule, Backquoting, DefMacroRuleBase, DefMacroRuleBaseListed, ExtraInfo'Set, ExtraInfo'Get, GarbageCollect, FindFunction, Secure. )

# 2. Programming

This chapter describes functions useful for writing Yacas scripts.

/* | Start of comment
*/ | end of comment
// | Beginning of one-line comment
Prog | block of statements
[ | beginning of block of statements
] | end of block of statements
Bodied | define function syntax (bodied function)
Infix | define function syntax (infix operator)
Postfix | define function syntax (postfix operator)
Prefix | define function syntax (prefix operator)
IsBodied | check for function syntax
IsInfix | check for function syntax
IsPostfix | check for function syntax
IsPrefix | check for function syntax
OpPrecedence | get operator precedence
OpLeftPrecedence | get operator precedence
OpRightPrecedence | get operator precedence
RightAssociative | declare associativity
LeftPrecedence | set operator precedence
RightPrecedence | set operator precedence
RuleBase | define function with a fixed number of arguments
RuleBaseListed | define function with variable number of arguments
Rule | define a rewrite rule
HoldArg | mark argument as not evaluated
Retract | erase rules for a function
UnFence | change local variable scope for a function
HoldArgNr | specify argument as not evaluated
RuleBaseArgList | obtain list of arguments
MacroSet | define rules in functions
MacroClear | define rules in functions
MacroLocal | define rules in functions
MacroRuleBase | define rules in functions
MacroRuleBaseListed | define rules in functions
MacroRule | define rules in functions
Backquoting | macro expansion (LISP-style backquoting)
DefMacroRuleBase | define a function as a macro
DefMacroRuleBaseListed | define macro with variable number of arguments
ExtraInfo'Set, ExtraInfo'Get | annotate objects with additional information
GarbageCollect | do garbage collection on unused memory
FindFunction | find the library file where a function is defined
Secure | guard the host OS

###

/* -- Start of comment

###

*/ -- end of comment

###

// -- Beginning of one-line comment

##### Internal function

##### Calling format:

    /* comment */
    // comment

##### Description:

Introduce a comment block in a source file, similar to C++ comments. `//` makes everything until the end of the line a comment, while `/*` and `*/` may delimit a multi-line comment.

##### Examples:

    a+b; // get result
    a + /* add them */ b;

###

Prog -- block of statements

###

[ -- beginning of block of statements

###

] -- end of block of statements

##### Internal function

##### Calling format:

    Prog(statement1, statement2, ...)
    [ statement1; statement2; ... ]

##### Parameters:

`statement1`, `statement2` -- expressions

##### Description:

The `Prog` and the `[ ... ]` construct have the same effect: they evaluate all arguments in order and return the result of the last evaluated expression.

`Prog(a,b);` is the same as typing `[a;b;];` and is very useful for writing out function bodies. The `[ ... ]` construct is a syntactically nicer version of the `Prog` call; it is converted into `Prog(...)` during the parsing stage.

###

Bodied -- define function syntax (bodied function)

###

Infix -- define function syntax (infix operator)

###

Postfix -- define function syntax (postfix operator)

###

Prefix -- define function syntax (prefix operator)

##### Internal function

##### Calling format:

    Bodied("op", precedence)
    Infix("op")
    Infix("op", precedence)
    Postfix("op")
    Postfix("op", precedence)
    Prefix("op")
    Prefix("op", precedence)

##### Parameters:

`"op"` -- string, the name of a function

`precedence` -- nonnegative integer (evaluated)

##### Description:

Declares a special syntax for the function to be parsed as a bodied, infix, postfix, or prefix operator.

"Bodied" functions have all arguments except the first one inside parentheses and the last argument outside, for example:

    For(pre, condition, post) statement;

Here the function `For` has 4 arguments and the last argument is placed outside the parentheses. The `precedence` of a "bodied" function refers to how tightly the last argument is bound to the parentheses. This makes a difference when the last argument contains other operators. For example, when taking the derivative

    D(x) Sin(x)+Cos(x)

both `Sin` and `Cos` are under the derivative because the bodied function `D` binds less tightly than the infix operator "`+`".

"Infix" functions must have two arguments and are syntactically placed between their arguments. Names of infix functions can be arbitrary, although for reasons of readability they are usually made of non-alphabetic characters.

"Prefix" functions must have one argument and are syntactically placed before their argument. "Postfix" functions must have one argument and are syntactically placed after their argument.

Function name can be any string but meaningful usage and readability would require it to be either made up entirely of letters or entirely of non-letter characters (such as "+", ":" etc.). Precedence is optional (will be set to 0 by default).

##### Examples:

    In> YY x := x+1;
    CommandLine(1) : Error parsing expression

    In> Prefix("YY", 2)
    Out> True;
    In> YY x := x+1;
    Out> True;
    In> YY YY 2*3
    Out> 12;
    In> Infix("##", 5)
    Out> True;
    In> a ## b ## c
    Out> a##b##c;

Note that, due to a current parser limitation, a function atom that is declared prefix cannot be used by itself as an argument.

    In> YY
    CommandLine(1) : Error parsing expression

##### See also:

IsBodied , OpPrecedence .

###

IsBodied -- check for function syntax

###

IsInfix -- check for function syntax

###

IsPostfix -- check for function syntax

###

IsPrefix -- check for function syntax

##### Internal function

##### Calling format:

    IsBodied("op")
    IsInfix("op")
    IsPostfix("op")
    IsPrefix("op")

##### Parameters:

`"op"` -- string, the name of a function

##### Description:

Check whether the function with given name `"op"` has been declared as a "bodied", infix, postfix, or prefix operator, and return `True` or `False`.

##### Examples:

    In> IsInfix("+");
    Out> True;
    In> IsBodied("While");
    Out> True;
    In> IsBodied("Sin");
    Out> False;
    In> IsPostfix("!");
    Out> True;

##### See also:

Bodied , OpPrecedence .

###

OpPrecedence -- get operator precedence

###

OpLeftPrecedence -- get operator precedence

###

OpRightPrecedence -- get operator precedence

##### Internal function

##### Calling format:

    OpPrecedence("op")
    OpLeftPrecedence("op")
    OpRightPrecedence("op")

##### Parameters:

`"op"` -- string, the name of a function

##### Description:

Returns the precedence of the function named "op" which should have been declared as a bodied function or an infix, postfix, or prefix operator. Generates an error message if the string str does not represent a type of function that can have precedence.

For infix operators, right precedence can differ from left precedence. Bodied functions and prefix operators cannot have left precedence, while postfix operators cannot have right precedence; for these operators, there is only one value of precedence.

##### Examples:

    In> OpPrecedence("+")
    Out> 6;
    In> OpLeftPrecedence("!")
    Out> 0;

###

RightAssociative -- declare associativity

##### Internal function

##### Calling format:

    RightAssociative("op")

##### Parameters:

`"op"` -- string, the name of a function

##### Description:

This makes the operator right-associative. For example:

    RightAssociative("*")

would make multiplication right-associative. Take care not to abuse this function, because the reverse, making an infix operator left-associative, is not implemented. (All infix operators are by default left-associative until they are declared to be right-associative.)

##### See also:

OpPrecedence .

###

LeftPrecedence -- set operator precedence

###

RightPrecedence -- set operator precedence

##### Internal function

##### Calling format:

    LeftPrecedence("op",precedence)
    RightPrecedence("op",precedence)

##### Parameters:

`"op"` -- string, the name of a function

`precedence` -- nonnegative integer

##### Description:

`"op"` should be an infix operator. This function call tells the infix expression printer to bracket the left or right hand side of the expression if its precedence is larger than precedence.

This functionality was required in order to display expressions like `a-(b-c)` correctly. Thus, `a+b+c` is the same as `a+(b+c)`, but `a-(b-c)` is not the same as `a-b-c`.

Note that the left and right precedence of an infix operator does not affect the way Yacas interprets expressions typed by the user. You cannot make Yacas parse `a-b-c` as `a-(b-c)` unless you declare the operator "`-`" to be right-associative.

##### See also:

OpPrecedence , OpLeftPrecedence , OpRightPrecedence , RightAssociative .

###

RuleBase -- define function with a fixed number of arguments

##### Internal function

##### Calling format:

    RuleBase(name,params)

##### Parameters:

`name` -- string, name of function

`params` -- list of arguments to function

##### Description:

Define a new rules table entry for a function "name", with `params` as the parameter list. Name can be either a string or simple atom.

In the context of the transformation rule declaration facilities this is a useful function in that it allows the stating of argument names that can he used with HoldArg.

Functions can be overloaded: the same function can be defined with different number of arguments.

##### See also:

MacroRuleBase , RuleBaseListed , MacroRuleBaseListed , HoldArg , Retract .

###

RuleBaseListed -- define function with variable number of arguments

##### Internal function

##### Calling format:

    RuleBaseListed("name", params)

##### Parameters:

`"name"` -- string, name of function

`params` -- list of arguments to function

##### Description:

The command `RuleBaseListed` defines a new function. It essentially works the same way as `RuleBase`, except that it declares a new function with a variable number of arguments. The list of parameters `params` determines the smallest number of arguments that the new function will accept. If the number of arguments passed to the new function is larger than the number of parameters in `params`, then the last argument actually passed to the new function will be a list containing all the remaining arguments.

A function defined using `RuleBaseListed` will appear to have the arity equal to the number of parameters in the `param` list, and it can accept any number of arguments greater or equal than that. As a consequence, it will be impossible to define a new function with the same name and with a greater arity.

The function body will know that the function is passed more arguments than the length of the `param` list, because the last argument will then be a list. The rest then works like a `RuleBase`-defined function with a fixed number of arguments. Transformation rules can be defined for the new function as usual.

##### Examples:

The definitions

    RuleBaseListed("f",{a,b,c})
    10 # f(_a,_b,{_c,_d}) <--
      Echo({"four args",a,b,c,d});
    20 # f(_a,_b,c_IsList) <--
      Echo({"more than four args",a,b,c});
    30 # f(_a,_b,_c) <-- Echo({"three args",a,b,c});

give the following interaction:

    In> f(A)
    Out> f(A);
    In> f(A,B)
    Out> f(A,B);
    In> f(A,B,C)
    three args A B C
    Out> True;
    In> f(A,B,C,D)
    four args A B C D
    Out> True;
    In> f(A,B,C,D,E)
    more than four args A B {C,D,E}
    Out> True;
    In> f(A,B,C,D,E,E)
    more than four args A B {C,D,E,E}
    Out> True;

The function `f` now appears to occupy all arities greater than 3:

    In> RuleBase("f", {x,y,z,t});
    CommandLine(1) : Rule base with this arity
      already defined

##### See also:

RuleBase , Retract , Echo .

###

Rule -- define a rewrite rule

##### Internal function

##### Calling format:

    Rule("operator", arity,
      precedence, predicate) body

##### Parameters:

`"operator"` -- string, name of function

`arity`, `precedence` -- integers

`predicate` -- function returning boolean

`body` -- expression, body of rule

##### Description:

Define a rule for the function "operator" with "arity", "precedence", "predicate" and "body". The "precedence" goes from low to high: rules with low precedence will be applied first.

The arity for a rules database equals the number of arguments. Different rules data bases can be built for functions with the same name but with a different number of arguments.

Rules with a low precedence value will be tried before rules with a high value, so a rule with precedence 0 will be tried before a rule with precedence 1.

###

HoldArg -- mark argument as not evaluated

##### Internal function

##### Calling format:

    HoldArg("operator",parameter)

##### Parameters:

`"operator"` -- string, name of a function

`parameter` -- atom, symbolic name of parameter

##### Description:

Specify that parameter should not be evaluated before used. This will be declared for all arities of "operator", at the moment this function is called, so it is best called after all `RuleBase` calls for this operator. "operator" can be a string or atom specifying the function name.

The `parameter` must be an atom from the list of symbolic arguments used when calling `RuleBase`.

##### See also:

RuleBase , HoldArgNr , RuleBaseArgList .

###

Retract -- erase rules for a function

##### Internal function

##### Calling format:

    Retract("function",arity)

##### Parameters:

`"function"` -- string, name of function

`arity` -- positive integer

##### Description:

Remove a rulebase for the function named `"function"` with the specific `arity`, if it exists at all. This will make Yacas forget all rules defined for a given function. Rules for functions with the same name but different arities are not affected.

Assignment `:=` of a function does this to the function being (re)defined.

##### See also:

RuleBaseArgList , RuleBase , := .

###

UnFence -- change local variable scope for a function

##### Internal function

##### Calling format:

    UnFence("operator",arity)

##### Parameters:

`"operator"` -- string, name of function

`arity` -- positive integers

##### Description:

When applied to a user function, the bodies defined for the rules for "operator" with given arity can see the local variables from the calling function. This is useful for defining macro-like procedures (looping and such).

The standard library functions `For` and `ForEach` use `UnFence`.

###

HoldArgNr -- specify argument as not evaluated

##### Standard library

##### Calling format:

    HoldArgNr("function", arity, argNum)

##### Parameters:

`"function"` -- string, function name

`arity`, `argNum` -- positive integers

##### Description:

Declares the argument numbered `argNum` of the function named `"function"` with specified `arity` to be unevaluated ("held"). Useful if you don't know symbolic names of parameters, for instance, when the function was not declared using an explicit `RuleBase` call. Otherwise you could use `HoldArg`.

##### See also:

HoldArg , RuleBase .

###

RuleBaseArgList -- obtain list of arguments

##### Internal function

##### Calling format:

    RuleBaseArgList("operator", arity)

##### Parameters:

`"operator"` -- string, name of function

`arity` -- integer

##### Description:

Returns a list of atoms, symbolic parameters specified in the `RuleBase` call for the function named `"operator"` with the specific `arity`.

##### See also:

RuleBase , HoldArgNr , HoldArg .

###

MacroSet -- define rules in functions

###

MacroClear -- define rules in functions

###

MacroLocal -- define rules in functions

###

MacroRuleBase -- define rules in functions

###

MacroRuleBaseListed -- define rules in functions

###

MacroRule -- define rules in functions

##### Internal function

##### Description:

These functions have the same effect as their non-macro counterparts, except that their arguments are evaluated before the required action is performed. This is useful in macro-like procedures or in functions that need to define new rules based on parameters.

Make sure that the arguments of `Macro`... commands evaluate to expressions that would normally be used in the non-macro versions!

##### See also:

Set , Clear , Local , RuleBase , Rule , Backquoting .

###

Backquoting -- macro expansion (LISP-style backquoting)

##### Internal function

##### Calling format:

    `(expression)

##### Parameters:

`expression` -- expression containing "`@var`" combinations to substitute the value of variable "`var`"

##### Description:

Backquoting is a macro substitution mechanism. A backquoted `expression` is evaluated in two stages: first, variables prefixed by `@` are evaluated inside an expression, and second, the new expression is evaluated.

To invoke this functionality, a backquote ``` needs to be placed in front of an expression. Parentheses around the expression are needed because the backquote binds tighter than other operators.

The expression should contain some variables (assigned atoms) with the special prefix operator `@`. Variables prefixed by `@` will be evaluated even if they are inside function arguments that are normally not evaluated (e.g. functions declared with `HoldArg`). If the `@var` pair is in place of a function name, e.g. "`@f(x)`", then at the first stage of evaluation the function name itself is replaced, not the return value of the function (see example); so at the second stage of evaluation, a new function may be called.

One way to view backquoting is to view it as a parametric expression generator. `@var` pairs get substituted with the value of the variable `var` even in contexts where nothing would be evaluated. This effect can be also achieved using `UnList` and `Hold` but the resulting code is much more difficult to read and maintain.

This operation is relatively slow since a new expression is built before it is evaluated, but nonetheless backquoting is a powerful mechanism that sometimes allows to greatly simplify code.

##### Examples:

This example defines a function that automatically evaluates to a number as soon as the argument is a number (a lot of functions do this only when inside a `N(...)` section).

    In> Decl(f1,f2) := \
    In>   `(@f1(x_IsNumber) <-- N(@f2(x)));
    Out> True;
    In> Decl(nSin,Sin)
    Out> True;
    In> Sin(1)
    Out> Sin(1);
    In> nSin(1)
    Out> 0.8414709848;

This example assigns the expression `func(value)` to variable `var`. Normally the first argument of `Set` would be unevaluated.

    In> SetF(var,func,value) := \
    In>     `(Set(@var,@func(@value)));
    Out> True;
    In> SetF(a,Sin,x)
    Out> True;
    In> a
    Out> Sin(x);

##### See also:

MacroSet , MacroLocal , MacroRuleBase , Hold , HoldArg , DefMacroRuleBase .

###

DefMacroRuleBase -- define a function as a macro

##### Standard library

##### Calling format:

    DefMacroRuleBase(name,params)

##### Parameters:

`name` -- string, name of a function

`params` -- list of arguments

##### Description:

`DefMacroRuleBase` is similar to `RuleBase`, with the difference that it declares a macro, instead of a function. After this call, rules can be defined for the function "`name`", but their interpretation will be different.

With the usual functions, the evaluation model is that of the applicative-order model of substitution, meaning that first the arguments are evaluated, and then the function is applied to the result of evaluating these arguments. The function is entered, and the code inside the function can not access local variables outside of its own local variables.

With macros, the evaluation model is that of the normal-order model of substitution, meaning that all occurrences of variables in an expression are first substituted into the body of the macro, and only then is the resulting expression evaluated in its calling environment. This is important, because then in principle a macro body can access the local variables from the calling environment, whereas functions can not do that.

As an example, suppose there is a function `square`, which squares its argument, and a function `add`, which adds its arguments. Suppose the definitions of these functions are:

    add(x,y) <-- x+y;

and

    square(x) <-- x*x;

In applicative-order mode (the usual way functions are evaluated), in the following expression

    add(square(2),square(3))

first the arguments to `add` get evaluated. So, first `square(2)` is evaluated. To evaluate this, first `2` is evaluated, but this evaluates to itself. Then the `square` function is applied to it, `2*2`, which returns 4. The same is done for `square(3)`, resulting in `9`. Only then, after evaluating these two arguments, `add` is applied to them, which is equivalent to

    add(4,9)

resulting in calling `4+9`, which in turn results in `13`.

In contrast, when `add` is a macro, the arguments to `add` are first expanded. So

    add(square(2),square(3))

first expands to

    square(2) + square(3)

and then this expression is evaluated, as if the user had written it directly. In other words, `square(2)` is not evaluated before the macro has been fully expanded.

Macros are useful for customizing syntax, and compilers can potentially greatly optimize macros, as they can be inlined in the calling environment, and optimized accordingly.

There are disadvantages, however. In interpreted mode, macros are slower, as the requirement for substitution means that a new expression to be evaluated has to be created on the fly. Also, when one of the parameters to the macro occur more than once in the body of the macro, it is evaluated multiple times.

When defining transformation rules for macros, the variables to be substituted need to be preceded by the `@` operator, similar to the back-quoting mechanism. Apart from that, the two are similar, and all transformation rules can also be applied to macros.

Macros can co-exist with functions with the same name but different arity. For instance, one can have a function `foo(a,b)` with two arguments, and a macro `foo(a,b,c)` with three arguments.

##### Example:

The following example defines a function `myfor`, and shows one use, referencing a variable `a` from the calling environment.

    In> DefMacroRuleBase("myfor",{init,pred,inc,body})
    Out> True;
    In> myfor(_init,_pred,_inc,_body)<--[@init;While(@pred)[@body;@inc;];True;];
    Out> True;
    In> a:=10
    Out> 10;
    In> myfor(i:=1,i<10,i++,Echo(a*i))
    10
    20
    30
    40
    50
    60
    70
    80
    90
    Out> True;
    In> i
    Out> 10;

##### See also:

RuleBase , Backquoting , DefMacroRuleBaseListed .

###

DefMacroRuleBaseListed -- define macro with variable number of arguments

##### Internal function

##### Calling format:

    DefMacroRuleBaseListed("name", params)

##### Parameters:

`"name"` -- string, name of function

`params` -- list of arguments to function

##### Description:

This does the same as `DefMacroRuleBase` (define a macro), but with a variable number of arguments, similar to `RuleBaseListed`.

##### See also:

RuleBase , RuleBaseListed , Backquoting , DefMacroRuleBase .

###

ExtraInfo'Set, ExtraInfo'Get -- annotate objects with additional information

##### Internal function

##### Calling format:

    ExtraInfo'Set(expr,tag)
    ExtraInfo'Get(expr)

##### Parameters:

`expr` -- any expression

`tag` -- tag information (any other expression)

##### Description:

Sometimes it is useful to be able to add extra tag information to "annotate" objects or to label them as having certain "properties". The functions `ExtraInfo'Set` and `ExtraInfo'Get` enable this.

The function `ExtraInfo'Set` returns the tagged expression, leaving the original expression alone. This means there is a common pitfall: be sure to assign the returned value to a variable, or the tagged expression is lost when the temporary object is destroyed.

The original expression is left unmodified, and the tagged expression returned, in order to keep the atomic objects small. To tag an object, a new type of object is created from the old object, with one added property (the tag). The tag can be any expression whatsoever.

The function `ExtraInfo'Get(x)` retrieves this tag expression from an object `x`. If an object has no tag, it looks the same as if it had a tag with value `False`.

No part of the Yacas core uses tags in a way that is visible to the outside world, so for specific purposes a programmer can devise a format to use for tag information. Association lists (hashes) are a natural fit for this, although it is not required and a tag can be any object (except the atom `False` because it is indistinguishable from having no tag information). Using association lists is highly advised since it is most likely to be the format used by other parts of the library, and one needs to avoid clashes with other library code. Typically, an object will either have no tag or a tag which is an associative list (perhaps empty). A script that uses tagged objects will check whether an object has a tag and if so, will add or modify certain entries of the association list, preserving any other tag information.

Note that `FlatCopy` currently does not copy the tag information (see examples).

##### Examples:

    In> a:=2*b
    Out> 2*b;
    In> a:=ExtraInfo'Set(a,{{"type","integer"}})
    Out> 2*b;
    In> a
    Out> 2*b;
    In> ExtraInfo'Get(a)
    Out> {{"type","integer"}};
    In> ExtraInfo'Get(a)["type"]
    Out> "integer";
    In> c:=a
    Out> 2*b;
    In> ExtraInfo'Get(c)
    Out> {{"type","integer"}};
    In> c
    Out> 2*b;
    In> d:=FlatCopy(a);
    Out> 2*b;
    In> ExtraInfo'Get(d)
    Out> False;

##### See also:

Assoc , := .

###

GarbageCollect -- do garbage collection on unused memory

##### Internal function

##### Calling format:

    GarbageCollect()

##### Description:

`GarbageCollect` garbage-collects unused memory. The Yacas system uses a reference counting system for most objects, so this call is usually not necessary.

Reference counting refers to bookkeeping where in each object a counter is held, keeping track of the number of parts in the system using that object. When this count drops to zero, the object is automatically removed. Reference counting is not the fastest way of doing garbage collection, but it can be implemented in a very clean way with very little code.

Among the most important objects that are not reference counted are the strings. `GarbageCollect` collects these and disposes of them when they are not used any more.

`GarbageCollect` is useful when doing a lot of text processing, to clean up the text buffers. It is not highly needed, but it keeps memory use low.

###

FindFunction -- find the library file where a function is defined

##### Internal function

##### Calling format:

    FindFunction(function)

##### Parameters:

`function` -- string, the name of a function

##### Description:

This function is useful for quickly finding the file where a standard library function is defined. It is likely to only be useful for developers. The function `FindFunction` scans the `.def` files that were loaded at start-up. This means that functions that are not listed in `.def` files will not be found with `FindFunction`.

##### Examples:

    In> FindFunction("Sum")
    Out> "sums.rep/code.ys";
    In> FindFunction("Integrate")
    Out> "integrate.rep/code.ys";

##### See also:

Vi .

###

Secure -- guard the host OS

##### Internal function

##### Calling format:

    Secure(body)

##### Parameters:

`body` -- expression

##### Description:

`Secure` evaluates `body` in a "safe" environment, where files cannot be opened and system calls are not allowed. This can help protect the system when e.g. a script is sent over the Internet to be evaluated on a remote computer, which is potentially unsafe.

##### See also:

SystemCall .