The format is a bit of a mess, but I just finished the second exam (which seemed to go fine; apparently studying does pay off) and am not really in the mood to work on cleaning it up.
CS4250: Programming Languages
HW42: Eric Buckley – 18148800
8.4) Requiring the use of unique closing reserved words
(“fi” or “end-if” for “if”, etc.) for compound statements removes ambiguity
around binding of subordinate clauses. For example, in C, the code:
if ( x == 0 )
if ( y == 0 )
{
y
= 1;
x
= 1;
}
else
y
= 3;
relies on the programmer knowing that the else will bind to
the closest open if. However, in Ruby, the statement would be written as:
if ( x = 0 ) then
if ( y = 0 ) then
y = 1
x = 1
else
y = 3
end
end
The ambiguity is resolved by the presence of the end keyword.
On the other hand, binding an else to the nearest open if is
such a common paradigm that it can be argued that forcing the use of a closing
keyword is superfluous at best and may even impede readability. Furthermore,
the gains of using the closing keyword are only realized when a compound
statement is used. Unless a language adopts the syntax (as does Ruby) that ALL
blocks are compound statements, there will still be ambiguity when a single
statement is used in the then clause. Forcing the use of compound block
structures when a simple statement is used increases line count and decreases
readability of otherwise straightforward structures.
8.9) Java’s restriction of using only Boolean statements in
control structures was a response to the fact that many C and C++ program
errors resulted from an inadvertent use of an arithmetic expression. In
particular, “comparisons” of the form:
if ( x = 0 )
which will always evaluate to false as well as setting x to
zero, when what was intended was
if ( x == 0 )
were so commonplace that experienced C programmers formed
the habit of writing such expressions as
if ( 0 == x )
simply so the compiler would catch the error if the
additional = sign was omitted.
That
said, there are legitimate reasons for the use of arithmetic expressions in
control statements, the most common being the ubiquitous string copy that
appears in nearly every C program ever written:
while (*dest++ = *src++);
This code is not only concise and well understood by any
seasoned C programmer; most optimizing compilers will reduce it to a single
assembly instruction (assuming the underlying hardware supports a string copy).
8.4Prog) The C program:
j = -3;
for (i = 0; i < 3; i++) {
switch (j + 2) {
case 3:
case 2: j--;
break;
case 0: j += 2;
break;
default: j = 0;
}
if ( j > 0 ) break;
j = 3 – i;
}
can be rewritten without goto’s or break as follows:
bool term = false;
j = -3;
for (i = 0; (i < 3) && !term; i++) {
if ( 3 == (j+2) || 2
== (j+2) )
j--;
else if ( 0 == (j+2) )
j += 2;
else
j = 0;
if ( j > 0 )
term = true;
else
j = 3 – i;
}
and even with that, if said programmer works for me, they’d
better have their résumé up to date, because I’ll fire them.
9.5) Consider the program in C syntax:
void swap (int a, int b){
int temp;
temp = a;
a = b;
b = temp;
}
void main() {
int value = 2, list[5]
= {1, 3, 5, 7, 9};
swap(value, list[0]);
swap(list[0], list[1];
swap(value,
list[value]);
a)
The values of the variables after each call when
pass by value is employed are:
value
|
list
|
note
|
2
|
{1, 3, 5, 7, 9}
|
values are switched in swap, but not
returned to main
|
2
|
{1, 3, 5, 7, 9}
|
|
2
|
{1, 3, 5, 7, 9}
|
|
b)
When pass by reference is employed
value
|
list
|
note
|
1
|
{2, 3, 5, 7, 9}
|
swap switches and returns value and
list[0]
|
1
|
{3, 2, 5, 7, 9}
|
swap switches and returns list[0] and
list[1]
|
2
|
{3, 1, 5, 7, 9}
|
despite the aliasing on the call, once
the addresses for value and list[value] (list[1] in this case) are passed,
the subprogram does not modify the addresses, just the contents of those addresses.
Therefore, swap will exchange the values of value and list[1].
|
c)
When pass by value-result is employed, the
result depends on the time of address binding
value
|
list
|
note
|
1
|
{2, 3, 5, 7, 9}
|
swap switches and returns value and
list[0]
|
1
|
{3, 2, 5, 7, 9}
|
swap switches and returns list[0] and
list[1]
|
2
|
{3, 2, 2, 7, 9}
|
if address binding is done at the time
of entry, the results are the same as call by reference. If however, address
binding is done at the time of the exit AND addresses are computed left to
right, then the return value for the second parameter (2) will be placed in
list[value] which will evaluate to list[2], giving the result indicated to
the left.
|
9.7) Consider the program in C syntax:
void fun (int first, int second) {
first += first;
second += second;
}
void main() {
int list[2] = {1, 3};
fun(list[0], list[1]);
}
a)
The resulting values of the list array when call
by value is used is {1, 3} since the modified values are not returned to main.
b)
When call by reference is used, the content of
each address is added to itself, yielding {2, 6}.
c)
When call by value-result is used, each value is
added to itself and then returned to main, again yielding {2, 6}.
9.11) The out modifier in C# allows a value set in the
called program to be returned to the calling program. This allows a method to
return more than one intrinsic-typed value (objects and arrays are always
passed by reference in C++, Java, and C#).
While Java and C++ can implement multiple return values, either by
boxing (putting an intrinsic-typed element inside an object) or, in the case of
C++ simply passing the reference, the out keyword offers a more reliable
alternative. Out parameters MUST be set by the called program (similar to
return values). Also, the referencing is somewhat safer in that the compiler
will not allow aliasing of an address that may also be accessed through another
reference, such as an element of an array or an object member. Finally, while
the program will compile, a warning will be generated if an out parameter is
used in an expression prior to having its value set.
10.9) Introducing a second static chain link which points to
the grandparent activation instance decreases non-local access by at most 50%.
In general, a depth difference of d would result in chains of length
since the grandparent chain can be
followed back through any even depth differences, and then the parent chain
used once for an odd difference. Thus
While this savings is not insignificant
when d is large, it is offset by the fact that two chain pointers need to be
maintained at program linkage, which increases the time for the call whether or
not any non-local variables are ever used in the subprogram. Furthermore, an
optimizing compiler should be able to predict how many non-local references are
needed and determine if the chain should be computed once at entry or every
time a variable is referenced. Thus, the savings is at best minimal and does
not justify the additional complexity. (And really, if speed is a concern, the
programmer shouldn’t be using such constructs to begin with).
10.11) If a static-chain process is used
to implement blocks, the activation record must contain two of the five
elements typically stored in the activation record:
·
The dynamic link, which points to the
activation record of the enclosing block or subprogram. The block itself is
considered to be one level deeper than its enclosing block/subprogram for
purposes of resolving references to variables defined outside the block.
·
Space for variables defined inside the
block
The other two elements are not necessary:
·
The static link is not necessary because
it will always be the same as the dynamic link. (Blocks are always “called”
from the block/subprogram that defines them.)
·
The return address is not needed because
the block will not return control to the point where it was entered. Instead,
control will flow out of the block either to the next statement following the
block end or to the target of a goto.
·
Storage for parameters is also not needed
because blocks do not take parameters.
No comments:
Post a Comment