We'll also do the TA evaluations today. CAPE will come for the
instructor evaluation next Wednesday, June 5.
in Pascal you have to write the following:type complex = record re, im: real end;
An aggregate is a literal for composite types. In Ada you can writevar zero: complex;
...
zero.re := 0.0;
zero.im := 0.0;
The expression (re => 0.0, im => 0.0) is a literal of type complex, called an aggregate. Modern languages provide an aggregate notation for each composite type.zero: constant complex := (re => 0.0, im => 0.0)
Conceptually there are several steps of evaluation for function-call expressions:
There is one rule of evaluation order for function-calls
that is so fundamental, you may never have noticed it consciously: argument
subexpressions must be evaluated before calling the function.
We saw before that the regular notation for operators where they appear between their arguments is called "infix". An operator that appears before its argument(s) is called "prefix". One that appears after is called "postfix".times(plus(2,3),plus(4,5))
Programming languages typically don't allow postfix operators because they make life difficult for the parser, but they occur in mathematics, e.g. for the factorial function, as in 29! using the factorial function.
The notation for abstract syntax used above standardizes
all operators to be prefix. It is also possible to make all operators
postfix. One variant of all-postfix notation is called "reverse Polish
notation" or RPN for short.
Writing this expression in standard prefix format forces some ambiguity on evaluation order to be resolved. There are two standard ambiguity-reduction rules:
Like standard prefix format, a parse tree representation is unambiguous. Operator precedence and ambiguity are lexical details that are important in concrete syntax but not in abstract syntax.
Sometimes some orders give an error and others don't: consider the expression
where maxint is the largest integer that can be represented.maxint + 1 - maxint
The usual rule is left-to-right, so in plus(plus(2,3),times(4,5)) the inner addition is done before the multiplication.
Most modern programming languages carefully do not specify the order in which arguments are evaluated, because they want to allow the compiler to do optimizations:
The rule that subexpressions must be evaluated before calling the function has one crucial exception: the "if-then-else" function. The first argument (the Boolean expression) should be evaluated first. Then, exactly one of the two other argument expressions should be evaluated.
Evaluating both the then and else expressions would be inefficient. Even worse, it could lead to an incorrect program, i.e. a non-terminating program. Consider the factorial function
If the third argument "n*fact(n-1)" is always evaluated, even when "n=0" is true, then calling this function will always give an infinite loop.function fact (n: integer): integer;
begin
return ifthenelse(n=0, 1, n*fact(n-1))
end;
In most traditional languages you can't write the first expression above.
If the function itself is an expression, then we need to know when it is evaluated: before or after the arguments?
Evaluating the function itself first is suggested by viewing (if flag then sin else cos)(u+v+w) as syntactic sugar for call(if flag then sin else cos,u+v+w)
One PL that has an explicit call operator
is Lisp.
More precisely, a function is an abstraction of an expression. All the user of the function needs to know is what value will be returned. Exactly how this value is computed is a detail that is private to the function.
Consider for example two alternative cosine functions:
Each function "encapsulates" a very different expression, but for the same argument, both always yield the same result.function cosine (x: real): real;
begin
return sine(x + pi/2.0);
end;function cosine (x: real): real;
begin
return integrate(sine,0,x,epsilon);
end;
Just like for functions, procedures with different bodies and different
algorithms can have exactly the same effect, i.e. they are the same
from the user's abstract point of view.