Principles of Programming Language Unit-2 1.: Explain Briefly About Scope and Its Lifetime. Scope and Scope Rules
Principles of Programming Language Unit-2 1.: Explain Briefly About Scope and Its Lifetime. Scope and Scope Rules
Unit-2
1.Explain briefly about scope and its lifetime.
Scope and Scope Rules
• The scope of the variable is basically the area of instructions in which the
variable name is known. The variable is visible under the name within its
scope and is invisible under the name outside the scope.
• The scope rules of a language determine how a particular occurrence of
a name is associated with a variable.
• The variable is bound to its scope statically or dynamically.
• The static scope is in terms of lexical structure of a program. That means
- the scope of variable is obtained by examining the complete source
program without executing it. For example, C program makes use of
static scope.
Scope Loop parameter in ADA
The scope of loop parameter in ADA is static scope. For example - consider
following procedure in ADA
If(a[i]<a[j]) {
int temp;
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
• The lifetime of a variable is the location (i.e., place) where the variable
exists
• For example
void main()
{
sum();
}
void sum()
{
int a,b,c;
a=10;b=20;
c=a+b;
printf("\n The sum= %d",c);
}
In above code, there are three variables namely a, b and c. These variables
have the scope and lifetime for entire function sum(). Outside the function
sum(), these variables are not visible or accessible.
Concept of garbage collection
Garbage collection is a method of automatic memory management.
It works as follows:
1. When an application needs some free space to allocate the nodes and if
there is no free space available to allocate the memory for these objects then a
system routine called garbage collector is called.
2. This routine then searches the system for the nodes that are no longer
accessible from an external pointer. These nodes are then made available for
reuse by adding them to available pool. The system can then make use of these
free available space for allocating the nodes.
Garbage collection is usually done in two phases marking phase and collection
phase. In marking phase, the garbage collector scans the entire system and
marks all the nodes that can be accessible using external pointer.
During collection phase, the memory is scanned sequentially and the
unmarked nodes are made free.
Marking phase: For marking each node, there is one field called mark field.
Each node that is accessible using external pointer has the value TRUE in
marking field. For example
Collection phase
• During collection phase, all the nodes that are marked FALSE are
collected and made free. This is called sweeping. There is another term
used in regard to garbage collection called thrashing.
• Consider a scenario that, the garbage collector is called for getting some
free space and almost all the nodes are accessible by external pointers.
Now garbage collection routine executes and returns a small amount of
space. Then again after some time system demands for some free space.
Once again garbage collector gets invokes which returns very small
amount of free space.
• This happens repeatedly and garbage collection routine is executing
almost all the time. This process is called thrashing. Thrashing must be
avoided for better system performance.
Advantages of garbage collection
1. The manual memory management done by the programmer (i.e. use of
malloc and free) is time consuming and error prone. Hence automatic memory
management is done.
2. Reusability of memory can be achieved with the help of garbage collection.
Disadvantages of garbage collection
1. The execution of the program is paused or stopped during the process of
garbage collection.
2. Sometime situation like thrashing may occur due to garbage collection.
2. What is binding? How the variables are binded? What are the various
methods of binding?.
Concept of Binding
Types of Binding
Static Binding (Early Binding)
➢ Binding that occurs at compile time.
➢ The variable and its attributes, such as data type and memory location,
are bound during the compilation process.
➢ This is typically used in statically typed languages like C, C++, and Java.
➢ Example: In C, declaring int x = 10; binds x to an integer type and assigns
it a memory location during compilation.
Dynamic Binding (Late Binding)
➢ Binding that occurs at run time.
➢ The variable and its attributes, such as data type and memory location,
are determined during program execution.
➢ This is often used in dynamically typed languages like Python and
JavaScript.
➢ Example: In Python, x = 10 binds x to an integer type at run time, and the
type can change if x is later assigned a different type (e.g., x = "Hello").
Methods of Binding
1.Explicit Declaration:
➢ Variables are explicitly declared with a type, which binds them at
compile time.
➢ Used in languages with strong typing, like C, C++, and Java.
➢ Example: int a; in C explicitly binds a to an integer type.
2.Implicit Declaration:
➢ The variable type is inferred based on the initial value assigned to it.
➢ Often used in scripting languages or dynamically typed languages.
➢ Example: In Python, a = 5 implicitly binds a to an integer without needing
an explicit type declaration.
3.Type Inference:
➢ The compiler infers the variable type based on the assigned value.
➢ This is common in languages with type inference features, such as Swift,
Kotlin, and TypeScript.
➢ Example: In Swift, var x = 10 binds x to an integer type by inferring it
from the initial value.
4.Dynamic Typing:
➢ Variables can be bound to different types during runtime.
➢ Common in dynamically typed languages like Python, JavaScript, and
Ruby.
➢ Example: In JavaScript, a variable var a = 10; can later be bound to a
string, such as a = "Hello";.
3. Explain in detail the Pointers and References.
Pointers
▪ A pointer is a variable that stores a memory address, for the purpose of
acting as an alias to what is stored at that address.
▪ A pointer can be used to access a location in the area where storage is
dynamically allocated which is called as heap.
▪ Variables that are dynamically allocated from the heap are called heap
dynamic variables.
▪ Variables without names are called anonymous variables.
Uses of pointers
1) Provide the power of indirect addressing.
2) Provide a way to manage dynamic memory. A pointer can be used to access
a location in the area where storage is dynamically created usually called a
heap.
Design Issues
The primary design issues are
1) Should a language support a pointer type or reference type or both?
2) What are the scope and lifetime of a pointer variable?
3) Are pointers used for dynamic storage management, indirect addressing or
both?
4) Are pointers restricted as to type of value to which they can point?
5) What is the life time of dynamic variable?
Point Operations
Consider the variable declaration
int *ptr
ptr is the name of our variable. The * informs the compiler that we want a
pointer variable, the int says that we are using our pointer variable which will
actually store the address of an integer. Such a pointer is said to be integer
pointer. Thus ptr is now ready to store an address of the value which is of
integer type.
ptr
We can store the addresses of
some variable whose value need
to be referred.
The pointer variable is basically used to store some address of the variable
which is holding some value.
Consider,
✓ Line 1-> int *ptr
✓ Line 2- int a,b;
✓ Line 3-> a=10; /storing some value in a/
✓ Line 4-> ptr=&a/storing address of a in ptr/
✓ Line 5-> b=*ptr:/*getting value from address in ptr and storing it in b"/
Here we have used two important operators and &. The means 'contents at the
specified address' and & means the 'address at.
On Line 1 and Line 2 we have declared the required variables out of which ptr
is a pointer variable and variables a and b are our normal variables. On Line 3
we have assigned value 10 to variable a. The Line 4 tells us that address of
variable a is stored in a pointer variable ptr. And on Line 4 we have written that
in ptr variable we have stored some address and at that address whatever
value is stored, store that value in variable b.That means at ptr we have stored
address of variable a. Then at the address of a whatever is a value we have to
store that value in variable b.
The dynamic memory allocation is done using an operator new. The syntax of
dynamic memory allocation using new is
new data type;
For example:
int *p
p=new int;
We can allocate the memory for more than one element. For instance if we
want to allocate memory of size in for 5 elements we can declare.
int *p;
p=new int[5];
In this case, the system dynamically assigns space for five elements of type int
and returns a pointer to the first element of the sequence, which is assigned to
p. Therefore, now, p points to a valid block of memory with space for five
elements of type int.
int
P
The memory can be deallocated using the delete operator.
The syntax is
delete variable_name;
For example
delete p
Pointer Problems
Following are implementation problems when pointers are used -
1.Management of heap storage area: Due to creation of objects of different
sizes during execution time requires management of general heap storage area.
2. The garbage problem: Sometimes the contents of the pointers are destroyed
and object still exists which is actually not at all accessible.
3. Dangling references: The object is destroyed however the pointer still
contains the address of the used location and can be wrongly used by the
program.
var q: integer;
var p: integer;
begin
new(p);
q=p
dispose(p);
end
❖ The live pointer p has created reference for q and then p is deleted.
❖ This creates dangling reference for q
Pointers in Various Languages
Cand C++
Pointers are basically the variables that contain the location of other data
objects. It allows to construct complex data objects. In C or C++ pointer are
data objects that can be manipulated by the programmer.
For example -
int *ptr;
ptr=malloc(sizeof(int));
The type of the pointer must match the type of the intended target.
ADA
Pointers in ADA are known as access types. There are four kinds of access types
in Ada: pool access types, general access types, anonymous access types,
access to subprogram types.
For example -
PASCAL
Pascal support use of pointers. Pointers are the variables that hold the address
of another variable.
For example -
Program pointers;
type
Buffer String[255];
BufPtr=^ Buffer;
Var B: Buffer;
BP: BufPtr;
PP: Pointer;
int i= 10;
int &x=i ;// x is a reference
Reference Pointer
References must be initialized when Pointer can be initialized at any time.
created created.
Once reference is assigned with some Pointers can point to another object
object it can not be changed. at any time.
One can not have NULL references. The pointer can be assigned with the
value NULL.
2.Attributes:
In this grammar:
➢ Expr and Term are non-terminals.
➢ number is a terminal (representing integers).
Let's define an attribute value for each non-terminal, which represents the
computed value of the expression.
1.Attributes:
Expr.value: Synthesized attribute representing the value of an Expr.
Term.value: Synthesized attribute representing the value of a Term.
number.value: An inherent value (the actual integer).
2.Semantic Rules:
For the production Expr → Expr + Term, we define:
Expr.value = Expr.value + Term.value
For the production Expr → Term, we define:
Expr.value = Term.value
For the production Term → number, we define:
Term.value = number.value
3.Evaluation:
• The parse tree is traversed, and the semantic rules are applied according
to the production rules.
• For instance, if we parse 3 + 4, number.value would be set to 3 and 4,
Term.value would be synthesized as 3 and 4, and Expr.value would
ultimately be 3 + 4 = 7.
Types of Attribute Grammars
S-Attributed Grammar:
*,/,% *,/,%
Lowest Binary+,- Binary+,-
Associativity:
The associativity rules for a few common languages are given here:
When pointers point to records, In C and C++, there are two ways a pointer to a record can be
used to reference a field in that record. If a pointer variable p points to a record with a field
named age, (*p).age can be used to refer to that field. The operator ->, when used between a
2
pointer to a struct and a field of that struct, combines dereferencing and field reference. For
example, the expression p -> age is equivalent to (*p).age.Pointer Problems:
The first high- level programming language to include pointer variables was PL/I, in which pointers
could be used to refer to both heap-dynamic variables and other program variables. Dangling
Pointers: A dangling pointer, or dangling reference, is a pointer that contains the address of a heap-
dynamic variable that has been deallocated. Dangling pointers are dangerous for several reasons.
First, the location being pointed to may have been reallocated to some new heapdynamic variable.
If the new variable is not the same type as the old one, type checks of uses of the dangling pointer
are invalid. Even if the new dynamic variable is the same type, its new value will have no relationship
to the old pointer’s dereferenced value.
In C and C++, pointers can be used in the same ways as addresses are used in assembly languages. In
C and C++, the asterisk (*) denotes the dereferencing operation and the ampersand (&) denotes the
operator for producing the address of a variable. For example, consider the following code: int *ptr;
int count, init; . . . ptr = &init; count = *ptr; The assignment to the variable ptr sets it to the address
of init. The assignment to count dereferences ptr to produce the value at init, which is then assigned
to count. So, the effect of the two assignment statements is to assign the value of init to count.
Notice that the declaration of a pointer specifies its domain type. In C and C++, all arrays use zero as
the lower bound of their subscript ranges, and array names without subscripts always refer to the
address of the first element. Consider the following declarations: int list [10]; int *ptr; Now consider
the assignment.
ptr = list;
It is clear from these statements that the pointer operations include the same scaling that is used in
indexing operations. Reference Types: A reference type variable is similar to a pointer, with one
important and fundamental difference: A pointer refers to an address in memory, while a reference
refers to an object or a value in memory. Reference type variables are specified in definitions by
preceding their names with ampersands (&). For example, int result = 0; int &ref_result = result; . . .
ref_result = 100; When used as formal parameters in function definitions, C++ reference types
provide for two-way communication between the caller function and the called function. This is not
possible with nonpointer primitive parameter types, because C++ parameters are passed by value.
3
Passing a pointer as a parameter accomplishes the same two- way communication, but pointer
formal parameters require explicit dereferencing, making the code less readable and less safe.
Reference parameters are referenced in the called function exactly as are other parameters. The
calling function need not specify that a parameter whose corresponding formal parameter is a
reference type is anything unusual. The compiler passes addresses, rather than values, to reference
parameters Solutions to the Dangling-Pointer Problem: There have been several proposed solutions
to the dangling-pointer problem. Among these are tombstones (Lomet, 1975), in which every heap-
dynamic variable includes a special cell, called a tombstone, that is itself a pointer to the heap-
dynamic variable. The actual pointer variable points only at tombstones and never to heap-dynamic
variables. When a heap-dynamic variable is deallocated, the tombstone remains but is set to nil,
indicating that the heap-dynamic variable no longer exists. This approach prevents a pointer from
ever pointing to a deallocated variable. Tombstones are costly in both time and space. Because
tombstones are never deallocated, their storage is never reclaimed. Every access to a heap dynamic
variable through a tombstone requires one more level of indirection, which requires an additional
machine cycle on most computers.
An alternative to tombstones is the locks-and- keys approach. In this compiler, pointer values are
represented as ordered pairs (key, address), where the key is an integer value. When a heap-dynamic
variable is allocated, a lock value is created and placed both in the lock cell of the heap-dynamic
variable and in the key cell of the pointer that is specified in the call to new. Every access to the
dereferenced pointer compares the key value of the pointer to the lock value in the heap-dynamic
variable. If they match, the access is legal; otherwise the access is treated as a run-time error. When
a heap-dynamic variable is deallocated with dispose, its lock value is cleared to an illegal lock value.
The best solution to the dangling- pointer problem is to take deallocation of heapdynamic variables
out of the hands of programmers. If programs cannot explicitly deallocate heap-dynamic variables,
there will be no dangling pointers.
ARITHMETIC EXPRESSIONS:
Automatic evaluation of arithmetic expressions similar to those found in matematics, science, and
engineering was one of the primary goals of the first high-level programming languages. An operator
can be unary, meaning it has a single operand, binary, meaning it has two operands, or ternary,
meaning it has three operands. In most programming languages, binary operators are infix, which
means they appear between their operands. One exception is Perl, which has some operators that
are prefix, which means they precede their operands. Operator Evaluation Order: The operator
precedence and associativity rules of a language dictate the order of evaluation of its operators.
Precedence: The value of an expression depends at least in part on the order of evaluation of the
operators in the expression.
a+b*c
Suppose the variables a, b, and c have the values 3, 4, and 5, respectively. If evaluated left to right
(the addition first and then the multiplication), the result is 35. If evaluated right to left, the result is
4
23. The operator precedence rules for expression evaluation partially define the order in which the
operators of different precedence levels are evaluated. Exponentiation has the highest precedence
(when it is provided by the language), followed by multiplication and division on the same level,
followed by binary addition and subtraction on the same level. Many languages also include unary
versions of addition and subtraction. Unary addition is called the identity operator because it usually
has no associated operation and thus has no effect on its operand. The unary minus operator can
appear in an expression either at the beginning or anywhere inside the expression, as long as it is
parenthesized to prevent it from being next to another operator. For example, a + (- b) * c is legal,
but a + - b * c usually is not.
Associativity:
When an expression contains two adjacent occurrences of operators with the same level of
precedence, the question of which operator is evaluated first is answered by the associativity rules of
the language. An operator can have either left or right associativity, meaning that when there are
two adjacent operators with the same precedence, the left operator is evaluated first or the right
operator is evaluated first. In the Java expression a - b + c the left operator is evaluated first
Exponentiation in Fortran and Ruby is right associative, so in the expression A ** B ** C the right
operator is evaluated first. In Visual Basic, the exponentiation operator, ^, is left associative.
Parentheses: Programmers can alter the precedence and associativity rules by placing parentheses in
expressions. A parenthesized part of an expression has precedence over its adjacent un
parenthesized parts. For example, although multiplication has precedence over addition, in the
expression (A + B) * C The disadvantage of this scheme is that it makes writing expressions more
tedious. Ruby: Ruby is a pure object-oriented language, which means, among other things, that every
data value, including literals, is an object. For example, the expression a + b is a call to the + method
of the object referenced by a, passing the object referenced by b as a parameter. Expressions in Lisp:
In Lisp, the subprograms must be explicitly called. For example, to specify the C expression
(+ a (* b c))
Conditional Expressions:
if-then-else statements can be used to perform a conditional expression assignment. For example,
consider
if (count == 0)
average = 0;
else
assignment statement using a conditional expression, which has the following form:
Variables in expressions are evaluated by fetching their values from memory. Constants are
sometimes evaluated the same way. In other cases, a constant may be part of the machine language
instruction and not require a memory fetch. If an operand is a parenthesized expression, all of the
operators it contains must be evaluated before its value can be used as an operand. If neither of the
operands of an operator has side effects, then operand evaluation order is irrelevant.
Side Effects:
A side effect of a function, naturally called a functional side effect, occurs when the function changes
either one of its parameters or a global variable. Consider the following expression: a + fun(a) If fun
does not have the side effect of changing a, then the order of evaluation of the two operands, a and
fun(a), has no effect on the value of the expression. Suppose we have the following: a = 10; b = a +
fun(a); Then, if the value of a is fetched first (in the expression evaluation process), its value is 10 and
the value of the expression is 20. But if the second operand is evaluated first, then the value of the
first operand is 20 and the value of the expression is 30.
A program has the property of referential transparency if any two expressions in the program that
have the same value can be substituted for one another anywhere in the program, without affecting
the action of the program. result1 = (fun(a) + b) / (fun(a) - c); temp = fun(a); result2 = (temp + b) /
(temp - c); If the function fun has no side effects, result1 and result2 will be equal, because the
expressions assigned to them are equivalent. However, suppose fun has the side effect of adding 1 to
either b or c. Then result1 would not be equal to result2. So, that side effect violates the referential
transparency of the program in which the code appears. There are several advantages to referentially
transparent programs. The most important of these is that the semantics of such programs is much
easier to understand than the semantics of programs that are not referentially transparent.
Overloaded Operators:
Arithmetic operators are often used for more than one purpose. For example, + usually is used to
specify integer addition and floating-point addition. This multiple use of an operator is called
operator overloading.
consider the use of the ampersand (&) in C++. As a binary operator, it specifies a bitwise logical AND
operation. As a unary operator, however, its meaning is totally different. As a unary operator with a
variable as its operand, the expression value is the address of that variable. In this case, the
ampersand is called the address-of operator. For example, the execution of x = &y; causes the
address of y to be placed in x.
There are two problems with this multiple use of the ampersand.
First, using the same symbol for two completely unrelated operations is detrimental to readability.
Second, the simple keying error of leaving out the first operand for a bitwise AND operation can go
undetected by the compiler. The problem is only that the compiler cannot tell if the operator is
6
meant to be binary or unary. Suppose a user wants to define the * operator between a scalar integer
and an integer array to mean that each element of the array is to be multiplied by the scalar. Such an
operator could be defined by writing a function subprogram named * that performs this new
operation. The compiler will choose the correct meaning when an overloaded operator is specified,
based on the types of the operands, as with language-defined overloaded operators. For example, if
+ and * are overloaded for a matrix abstract data type and A, B, C, and D are variables of that type,
then A * B + C * D can be used instead of MatrixAdd(MatrixMult(A, B), MatrixMult(C, D))
C++ has a few operators that cannot be overloaded. Among these are the class or structure member
operator (.) and the scope resolution operator (::).
TYPE CONVERSIONS:
Type conversions are either narrowing or widening. A narrowing conversion converts a value to a
type that cannot store even approximations of all of the values of the original type. For example,
converting a double to a float in Java is a narrowing conversion, because the range of double is much
larger than that of float. A widening conversion converts a value to a type that can include at least
approximations of all of the values of the original type. For example, converting an int to a float in
Java is a widening conversion.
Narrowing conversions are not always safe—sometimes the magnitude of the converted value is
changed in the process. Widening conversions are usually safe. Type conversions can be either
explicit or implicit.
Coercion in Expressions:
One of the design decisions concerning arithmetic expressions is whether an operator can have
operands of different types. Languages that allow such expressions, which are called mixed-mode
expressions, must define conventions for implicit operand type conversions because computers do
not have binary operations that take operands of different types.
coercion was defined as an implicit type conversion that is initiated by the compiler or
runtime system. Type conversions explicitly requested by the programmer are referred to as explicit
conversions, or casts. When the two operands of an operator are not of the same type and that is
legal in the language, the compiler must choose one of them to be coerced and generate the code
for that coercion. consider the following Java code:
int a;
float b, c, d; . . .
d = b * a;
Assume that the second operand of the multiplication operator was supposed to be c, but because of
a keying error it was typed as a. Because mixed-mode expressions are legal in Java, the compiler
would not detect this as an error. It would simply insert code to coerce the value of the int operand,
a, to float. If mixed-mode expressions were not legal in Java, this keying error would have been
detected by the compiler as a type error. Explicit Type Conversion: Explicit type conversions are
called casts. To specify a cast, the desired type is placed in parentheses just before the expression to
be converted, as in
7
(int) angle One of the reasons for the parentheses around the type name in these conversions is that
the first of these languages, C, has several two-word type names, such as long int.
Errors in Expressions:
A number of errors can occur during expression evaluation. The most common error occurs when
the result of an operation cannot be represented in the memory cell where it must be stored. This is
called overflow or underflow, depending on whether the result was too large or too small. One
limitation of arithmetic is that division by zero is disallowed. Floating-point overflow, underflow, and
division by zero are examples of run-time errors, which are sometimes called exceptions.
Relational Expressions:
A relational operator is an operator that compares the values of its two operands. A
relational expression has two operands and one relational operator. The types of the operands that
can be used for relational operators are numeric types, strings, and enumeration types. The syntax of
the relational operators for equality and inequality differs among some programming languages. For
example, for inequality, the C-based languages use !=, Lua uses ~=, Fortran 95+ uses .NET or <>, and
ML and F# use <>. JavaScript and PHP have two additional relational operators, === and !==. These
are similar to their relatives, == and !
Boolean Expressions:
ASSIGNMENT STATEMENTS:
It provides the mechanism by which the user can dynamically change the bindings of values
to variables.
Simple Assignments:
All programming languages use the equal sign for the assignment operator. ALGOL 60 pioneered the
use of := as the assignment operator, which avoids the confusion of assignment with equality. Ada
also uses this assignment operator.
Conditional Targets:
Perl allows conditional targets on assignment statements. For example, consider ($flag ? $count1 :
$count2) = 0; which is equivalent to if ($flag) { $count1 = 0; } else { $count2 = 0; }
operator. For example, sum += value; is equivalent to sum = sum + value; The languages that support
compound assignment operators have versions for most of their binary operators .
Unary Assignment Operators: The C-based languages, Perl, and JavaScript include two special unary
arithmetic operators that are actually abbreviated assignments. They combine increment and
decrement operations with assignment. The operators ++ for increment and --for decrement can be
used either in expressions or to form stand-alone single-operator assignment statements. They can
appear either as prefix operators, meaning that they precede the operands, or as postfix operators,
meaning that they follow the operands. In the assignment statement sum = ++ count; the value of
count is incremented by 1 and then assigned to sum.
This operation could also be stated as count = count + 1; sum = count; If the same operator is used as
a postfix operator, as in sum = count ++; the assignment of the value of count to sum occurs first;
then count is incremented. The effect is the same as that of the two statements sum = count; count =
count + 1; An example of the use of the unary increment operator to form a complete assignment
statement is count ++; which simply increments count. It does not look like an assignment, but it
certainly is one. It is equivalent to the statement count = count + 1;
Assignment as an Expression: In the C-based languages, Perl, and JavaScript, the assignment
statement produces a result, which is the same as the value assigned to the target. It can therefore
be used as an expression and as an operand in other expressions. For example, the expression a = b +
(c = d / b) - 1 denotes the instructions Assign d / b to c Assign b + c to temp Assign temp - 1 to a Note
that the treatment of the assignment operator as any other binary operator allows the effect of
multiple-target assignments, such as
sum = count = 0; in which count is first assigned the zero, and then count’s value is assigned to sum.
This form of multiple-target assignments is also legal in Python. There is a loss of error detection in
the C design of the assignment operation that frequently leads to program errors. In particular, if we
type
if (x = y) ...
instead of
if (x == y) ...
Multiple Assignments:
Several recent programming languages, including Perl, Ruby, and Lua, provide multiple-target,
multiple-source assignment statements. For example, in Perl one can write ($first, $second, $third) =
(20, 40, 60); The semantics is that 20 is assigned to $first, 40 is assigned to $second, and 60 is
assigned to $third. If the values of two variables must be interchanged, this can be done with a single
assignment, as with
This correctly interchanges the values of $first and $second, without the use of a temporary
variable.
All of the identifiers used in pure functional languages and some of them used in other functional
languages are just names of values. As such, their values never change. For example, in ML, names
are bound to values with the val declaration, whose form is exemplified in the following: val cost =
quantity * price; If cost appears on the left side of a subsequent val declaration, that declaration
creates a new version of the name cost, which has no relationship with the previous version, which is
then hidden.
CONTROL STRUCTURES:
Selecting among alternative control flow paths (of statement execution) and some
means of repeated execution of statements or sequences of statements. Statements that provide
these kinds of capabilities are called control statements.
SELECTION STATEMENTS:
A selection statement provides the means of choosing between two or more execution paths in a
program. Selection statements fall into two general categories: two-way and n-way, or multiple
selection.
if control_expression
then clause
else clause
Control expressions are specified in parentheses if the then reserved word is not used to
introduce the then clause. In those cases where the then reserved word is used, there is less need
for the parentheses, so they are often omitted, as in Ruby.
Clause Form:
In many languages, the then and else clauses appear as either single statements or compound
statements. Many languages use braces to form compound statements, which serve as the bodies of
then and else clauses. In Python and Ruby, the then and else clauses are statement sequences,
rather than compound statements. The complete selection statement is terminated in these
languages with the reserved word.
For example,
if x > y :
x=y
Notice that rather than then, a colon is used to introduce the then clause in Python.
10
Nesting Selectors: That ambiguous grammar was as follows: → if then | if then else Consider the
following Java-like code: if (sum == 0) if (count == 0) result = 0; else result = 1; This statement can be
interpreted in two different ways, depending on whether the else clause is matched with the first
then clause or the second. Notice that the indentation seems to indicate that the else clause belongs
with the first then clause. The crux of the problem in this example is that the else clause follows two
then clauses with no intervening else clause, and there is no syntactic indicator to specify a matching
of the else clause to one of the then clauses. In Java, as in that the else clause is always paired with
the nearest previous unpaired then clause. So, in the example, the else clause would be paired with
the second then clause. To force the alternative semantics in Java, the inner if is put in a compound,
as in
if (sum == 0)
if (count == 0)
result = 0;
Else
result = 1;
Perl requires that all then and else clauses be compound, it does not. In Perl, the previous code
would be written as follows:
if (sum == 0)
if (count == 0)
result = 0;
else {
result = 1;
if (sum == 0) {
if (count == 0)
result = 0;
11
else {
result = 1;
Another way to avoid the issue of nested selection statements is to use an alternative means of
forming compound statements. Consider the syntactic structure of the Java if statement. The then
clause follows the control expression and the else clause is introduced by the reserved word else.
When the then clause is a single statement and the else clause is present, although there is no need
to mark the end, the else reserved word in fact marks the end of the then clause. When the then
clause is a compound, it is terminated by a right brace. However, if the last clause in an if, whether
then or else, is not a compound, there is no syntactic entity to mark the end of the whole selection
statement.
acount = acount + 1
else
end
The design of this statement is more regular than that of the selection statements of the Cbased
languages, because the form is the same regardless of the number of statements in the then and else
clauses. The first interpretation of the selector example at the beginning of this section, in which the
else clause is matched to the nested if, can be written in Ruby as follows:
if sum == 0 then
if count == 0 then
result = 0
else result = 1
end
end
Because the end reserved word closes the nested if, it is clear that the else clause is matched to the
inner then clause. The second interpretation of the selection statement at the beginning of this
section, in which the else clause is matched to the outer if, can be written in Ruby as follows: if sum
== 0 then if count == 0 then result = 0 end else result = 1 end The following statement, written in
Python, is semantically equivalent to the last Ruby statement above:
if sum == 0 :
12
if count == 0 :
result = 0
else:
result = 1
Selector Expressions: Consider the following example selector written in F#: let
y = if x > 0
then x
else 2 * x;
This creates the name y and sets it to either x or 2 * x, depending on whether x is greater than zero.
Multiple-Selection Statements: The multiple-selection statement allows the selection of one of any
number of statements or statement groups. It is, therefore, a generalization of a selector. In fact,
two-way selectors can be built with a multiple selector. The need to choose from among more than
two control paths in programs is common. Although a multiple selector can be built from twoway
selectors and go to s. Examples of Multiple Selectors: The C multiple-selector statement, switch,
which is also part of C++, Java, and JavaScript, is a relatively primitive design. Its general form is
switch (expression) { case constant_expression1:statement1; . . . case constant n: statement n;
[default: statement n + 1} The optional default segment is for unrepresented values of the control
expression. If the value of the control expression is not represented and no default segment is
present, then the statement does nothing.
The break statement, which is actually a restricted goto, is normally used for exiting switch
statements. break transfers control to the first statement after the compound statement in which it
appears. The C switch statement has virtually no restrictions on the placement of the case
expressions, which are treated as if they were normal statement labels.
switch (x)
default:
if (prime(x))
process_prime(x);
else
process_composite(x);
switch (value)
case -1:
Negatives++;
13
break;
case 0:
Zeros++;
goto case 1;
case 1:
Positives++;
default:
ITERATIVE STATEMENT:
Counter-Controlled Loops:
A counting iterative control statement has a variable, called the loop variable, in which the count
value is maintained. It also includes some means of specifying the initial and terminal values of the
loop variable, and the difference between sequential loop variable values, often called the stepsize.
The initial, terminal, and stepsize specifications of a loop are called the loop parameters.
loop body
The loop body can be a single statement, a compound statement, or a null statement. The
expressions in a for statement are often assignment statements. The first expression is for
initialization and is evaluated only once, when the for statement execution begins. The second
expression is the loop control and is evaluated before each execution of the loop body. The last
expression in the for is executed after each execution of the loop body. It is often used to increment
the loop counter.
expression_1
loop:
[loop body]
expression_3
goto loop
out: . . .
...
or loop_variable in object
: - loop body
else:
- else clause
The loop variable is assigned the value in the object, which is often a range, one for each execution
of the loop body. The else clause, when present, is executed if the loop terminates normally.
Consider the following example:
print count
produces
246
Counter-Controlled Loops:
()
else
loopBody()
In this function, the parameter loopBody is the function with the body of the loop and the parameter
reps is the number of repetitions. The reserved word rec appears before the name of the function to
indicate that it is recursive.
In many cases, collections of statements must be repeatedly executed, but the repetition control is
based on the Boolean expression rather than counter
. The pretest and posttest logical loops have the following forms:
loop body
and
do
loop body
while
loop:
[loop body]
do-while
loop:
[loop body]
In some situations, it is convenient for a programmer to choose a location for loop control other
than the top or bottom of the loop body. Such loops have the structure of infinite loops but include
one or more user-located loop exits.
C, C++, Python, Ruby, and C# have unconditional unlabelled exits (break). Java and Perl have
unconditional labelled exits (break in Java, last in Perl).
Following is an example of nested loops in Java, in which there is a break out of the outer loop from
the nested loop:
outerLoop:
16
sum += mat[row][col];
break outerLoop;
continue, that transfers control to the control mechanism of the smallest enclosing loop. This is not
an exit but rather a way to skip the rest of the loop statements on the current iteration without
terminating the loop construct. For example, consider the following:
getnext(value);
sum += value;
A negative value causes the assignment statement to be skipped, and control is transferred instead
to the conditional at the top of the loop. On the other hand, in
getnext(value);
sum += value;
A "guarded statement" is a concept used in various contexts such as programming, logic, and even
general conversation, where a statement or claim is made with certain conditions or reservations
attached. The idea is to qualify a statement, making it more cautious, tentative, or conditional to
prevent misunderstanding or misinterpretation.
Example:haskell
factorial 0 = 1
```
Here, the guarded clauses `n > 0` and `_` are used to apply conditions to the factorial function.
In formal logic or reasoning, a **guarded statement** may refer to a claim that is only valid under
certain conditions or assumptions. This is often seen in proofs or logical deductions, where
statements are contingent on the truth of certain premises.
Example:
These are examples of guarded statements because the outcome depends on a specified condition.
In informal language, a guarded statement is one where the speaker expresses caution or makes a
conditional remark to avoid being overly definitive or making an absolute claim.
Example:
- "I think the meeting might go well, assuming everyone stays on topic."
- "I could be wrong, but I believe this approach might work better."
These are "guarded" because they leave room for doubt or acknowledge the possibility of other
factors influencing the outcome.