PPL Unit 2 Notes
PPL Unit 2 Notes
2.1 Names
In a programming language, a name refers to an identifier that is used to uniquely identify various
program elements, such as variables, functions, classes, objects, modules, and other entities. Names are
essential for giving meaningful labels to different parts of the code, making it easier for programmers to read,
write, and understand the program's structure and logic.
2.2 Variables
. In programming languages, a variable is a named storage location that holds a value. Variables are
used to store and manipulate data in a program, and they play a crucial role in enabling the program to work
with different values and perform computations. Each variable has a name, a data type that specifies the type
of value it can hold, and an associated value that can be read from or written to.
In programming languages, binding refers to the process of associating a name (an identifier) with a value,
type, or other entity in the program. Binding occurs when you declare a variable, assign a value to it, or
associate a function with a name. The term "binding" is used to describe the linkage between a name and the
entity it represents
• Def: An implicit declaration is a default mechanism for specifying types of variables (the
first appearance of the variable in the program)
• FORTRAN, PL/I, BASIC, and Perl provide implicit declarations
– Advantage: writability
– Disadvantage: reliability (less trouble with Perl)
• Dynamic Type Binding (JavaScript and PHP)
• Specified through an assignment statement e.g., JavaScript
list = [2, 4.33, 6, 8];
list = 17.3;
Type checking is a process in programming languages that ensures the compatibility and correctness
of data types used in various operations, assignments, and expressions. It involves verifying that the types of
values being used or manipulated are appropriate and consistent according to the language's type system
rules. Type checking helps catch errors and inconsistencies related to data types before they cause runtime
issues.
• Type checking is the activity of ensuring that the operands of an operator are of compatible types
• A compatible type is one that is either legal for the operator, or is allowed under language rules to
be implicitly converted, by compiler- generated code, to a legal type. This automatic conversion is
called as coercion.
• A type error is the application of an operator to an operand of an inappropriate type
• If all type bindings are static, nearly all type checking can be static
• If type bindings are dynamic, type checking must be dynamic
• Def: A programming language is strongly typed if type errors are always detected
Type compatibility in programming languages refers to the rules and conditions that dictate how
different data types can interact with each other within a program. It determines whether operations involving
values of different types are valid or whether type conversions are required to perform such operations. Type
compatibility is a fundamental aspect of a programming language's type system, and understanding it is
essential for writing correct and maintainable code.
• Def: Name type compatibility means the two variables have compatible types if they are in either
the same declaration or in declarations that use the same type name
• Easy to implement but highly restrictive:
– Subranges of integer types are not compatible with integer types
– Formal parameters must be the same type as their corresponding actual parameters (Pascal)
• Structure type compatibility means that two variables have compatible types if their types
have identical structures
• More flexible, but harder to implement
• Consider the problem of two structured types:
– Are two record types compatible if they are structurally the same but use different field
names?
– Are two enumeration types compatible if their components are spelled
differently?
– Are two array types compatible if they are the same except that the subscripts are different?
(e.g., [1..10] and [0..9])
– With structural type compatibility, you cannot differentiate between types of the same
structure (e.g., different units of speed, both float)
• Language examples:
Strong typing is a concept in computer programming and type systems that refers to the strict enforcement of
type rules and compatibility during the compilation or execution of a program. In a strongly typed language,
the type of a value is enforced and maintained at compile time and runtime, and implicit type conversions
(also known as type coercion) are limited or disallowed. This approach helps prevent certain types of errors
and enhances the reliability and safety of the program.
• Advantage of strong typing: allows the detection of the misuses of variables that result in
type errors
• Language examples:
– FORTRAN 77 is not: parameters, EQUIVALENCE
– Pascal is not: variant records
– C and C++ are not: parameter type checking can be avoided; unions are not type
checked
– Ada is, almost (UNCHECKED CONVERSION is loophole)
(Java is similar)
• Coercion rules strongly affect strong typing--they can weaken it considerably (C++ versus
Ada)
• Although Java has just half the assignment coercions of C++, its strong typing is still far
less effective than that of Ada
2.7 Scope
In programming, scope refers to the region of a program where a particular identifier (such as a variable,
function, class, or other named entity) is recognized and can be accessed. The scope determines the visibility
and lifetime of identifiers, ensuring that they are used in a well-defined and controlled manner within a
program. Scopes help prevent naming conflicts and allow for modular and organized code.
Local Scope: A local scope, also known as a block scope, is a limited region within a program where an
identifier is valid and accessible. Variables and other entities declared within a local scope are typically only
accessible within that scope. Examples of local scopes include function bodies and code blocks within control
structures (such as loops and conditional statements).
Global Scope: The global scope is the highest level of scope in a program. Variables and entities declared in
the global scope are accessible from anywhere in the program, including within functions and other scopes.
Global variables can be accessed and modified throughout the program's execution, which can lead to
unintended side effects and potential issues.
Lexical Scope (Static Scope): Lexical scope, also known as static scope or compile-time scope, is
determined by the program's structure and nesting of blocks. In a lexically scoped language, the visibility of
an identifier is determined by its position in the source code. This type of scoping is used in many programming
languages, including C, Python, and JavaScript.
Dynamic Scope: Dynamic scope, unlike lexical scope, is determined by the order of function calls during
runtime. In a dynamically scoped language, the visibility of an identifier is determined by the sequence of
function calls that led to the current point of execution. Dynamic scoping is less common and can lead to less
predictable behavior, which is why most languages use lexical scoping .
Nested Scopes: Scopes can be nested within each other, creating a hierarchy of visibility. Identifiers declared
in an outer scope are accessible from inner scopes, but the reverse is not true. This nesting allows for modular
and organized code, as well as the reuse of variable names within different contexts.
Shadowing: Shadowing occurs when an identifier declared in an inner scope has the same name as an
identifier in an outer scope. The inner identifier "shadows" the outer one, temporarily hiding it within the inner
scope. This can lead to confusion and unintended behavior if not managed carefully.
Lifetime: The lifetime of an identifier corresponds to the duration during which the identifier is valid and holds
a value. It is related to scope, as local variables within a scope typically have a limited lifetime that starts when
the scope is entered and ends when the scope is exited.
Scoping rules in programming languages determine where and how identifiers (such as variables, functions,
classes, and other named entities) are recognized and accessible within a program. Scoping rules play a
crucial role in managing the visibility and lifetime of identifiers, which helps prevent naming conflicts and
ensures that variables are used correctly. There are several common types of scoping rules:
Lexical scoping, also known as static scoping, is determined by the structure of the program's source code
.In a lexically scoped language, the scope of an identifier is defined by its location in the source code,
specifically the nesting of blocks or functions. Identifiers are accessible in the block where they are defined
and any nested blocks within it. This scoping rule is used in languages like C, C++, Python, JavaScript, and
Ruby.
Block Scoping:
Block scoping is a type of lexical scoping where identifiers are scoped within a block of code (enclosed by
curly braces), such as loops or conditionals. Variables declared within a block are only accessible within that
block and any nested blocks. This type of scoping is common in languages like C and JavaScript with the let
and const keywords.
Function Scoping:
Function scoping is a type of lexical scoping where identifiers are scoped within a function. Variables declared
within a function are only accessible within that function .Function scoping is common in languages like
JavaScript (before the introduction of let and const), as well as in some older programming languages.
Global Scoping:
The global scope is the outermost scope and encompasses the entire program. Identifiers declared in the
global scope are accessible from any part of the program, including within functions and other scopes. Global
variables can lead to naming conflicts and potential issues if not managed carefully.
Namespace Scoping:
Some languages provide the concept of namespaces, which allow you to organize and group identifiers to
avoid naming conflicts. Identifiers within a namespace are accessible using dot notation, such as namespace.
identifier. Namespaces help modularize code and improve organization.
Module Scoping:
In languages that support modules or packages, identifiers can be scoped within a specific module. Modules
allow you to encapsulate functionality and provide a controlled environment for identifier visibility. Identifiers
from one module are not automatically accessible in another module without explicit import or namespace
qualification.
Dynamic Scoping:
Dynamic scoping is determined by the order of function calls during runtime, rather than by the program's
source code structure .In a dynamically scoped language, the visibility of an identifier is determined by the
calling sequence of functions .Dynamic scoping is less common and can lead to less predictable behavior
compared to lexical scoping.
Scoping rules are crucial for managing the visibility of identifiers and ensuring that variables and other named
entities are used correctly within a program. Proper understanding and management of scoping rules
contribute to writing clean, modular, and maintainable code.
Array Indexing
• Indexing (or subscripting) is a mapping from indices to elements
array_name (index_value_list) an element
• Index Syntax
– FORTRAN, PL/I, Ada use parentheses
• Ada explicitly uses parentheses to show uniformity between array references and function
calls because both are mappings
– Most other languages use brackets
Array Initialization
• Some language allow initialization at the time of storage allocation
– C, C++, Java, C# example
int list [] = {4, 5, 7, 83}
01 EMP-REC.
02 EMP-NAME.
Emp_Rec: Emp_Rec_Type;
References to Records
Operations on Records
• A union is a type whose variables are allowed to store different type values
at different times during execution
• Design issues
– Should type checking be required?
– Should unions be embedded in records?
Pointer Operations
• Two fundamental operations: assignment and dereferencing
• Assignment is used to set a pointer variable‘s value to some useful address
• Dereferencing yields the value stored at the location represented by the pointer‘s
value
– Dereferencing can be explicit or implicit
– C++ uses an explicit operation via *
j = *ptr sets j to the value located at ptr
Pointers in Ada
• Some dangling pointers are disallowed because dynamic objects can be
automatically deallocated at the end of pointer's type scope
• The lost heap-dynamic variable problem is not eliminated by Ada (possible with
UNCHECKED_DEALLOCATION)
float stuff[100];
float *p;
p = stuff;
*(p+5) is equivalent to stuff[5] and p[5]
*(p+i) is equivalent to stuff[i] and p[i]
Reference Types
• C++ includes a special kind of pointer type called a reference type that is
used primarily for formal parameters
Evaluation of Pointers
• Dangling pointers and dangling objects are problems as is heap management
• Pointers are like goto's--they widen the range of cells that can be accessed by
a variable
• Pointers or references are necessary for dynamic data structures--so we can't
design a language without them
Representations of Pointers
• Large computers use single values
• Intel microprocessors use segment and offset
Heap Management
• A very complex run-time process
• Single-size cells vs. variable-size cells
• Two approaches to reclaim garbage
– Reference counters (eager approach): reclamation is gradual
– Mark-sweep (lazy approach): reclamation occurs when the list of variable
space becomes empty
Reference Counter
• Reference counters: maintain a counter in every cell that store the number of
pointers currently pointing at the cell
– Disadvantages: space required, execution time required, complications
for cells connected circularly
– Advantage: it is intrinsically incremental, so significant delays in the
application execution are avoided
Mark-Sweep
• The run-time system allocates storage cells as requested and disconnects
pointers from cells as necessary; mark-sweep then begins
– Every heap cell has an extra bit used by collection algorithm
– All cells initially set to garbage
– All pointers traced into heap, and reachable cells marked as not garbage
– All garbage cells returned to list of available cells
– Disadvantages: in its original form, it was done too infrequently. When done,
it caused significant delays in application execution. Contemporary marksweep
algorithms avoid this by doing it more often—called incremental marksweep
Variable-Size Cells
• All the difficulties of single-size cells plus more
• Required by most programming languages
• If mark-sweep is used, additional problems occur
– The initial setting of the indicators of all cells in the heap is difficult
– The marking process in nontrivial
– Maintaining the list of available space is another source of overhead
Design Issues
Design issues for arithmetic expressions
• Operator precedence rules?
• Operator associativity rules?
• Order of operand evaluation?
• Operand evaluation side effects?
• Operator overloading?
• Type mixing in expressions?
Operators
• A unary operator has one operand.
• A binary operator has two operands.
• A ternary operator has three operands.
The operator associativity rules for expression evaluation define the order inwhich
adjacent operators with the same precedence level are evaluated.
else
average = sum /count
Operand Evaluation Order
Potentials for Side Effects Functional side effects: when a function changes a two-
way parameter or a non-local variable
Overloaded Operators
Use of an operator for more than one purpose is called operator overloading.
Problems:
Potential problems:
Type Conversions
A narrowing conversion is one that converts an object to a type that cannot
include all of the values of the original type.
e.g., float to int
A widening conversion is one in which an object is converted to a type that can
include at least approximations to all of the values of the original type.
e.g., int to float
Mixed Mode
A mixed-mode expression is one that has operands of different
types.
Disadvantage of coercions:
– They decrease in the type error detection ability of the compiler
• In most languages, all numeric types are coerced in expressions, using widening
conversions.
• In Ada, there are virtually no coercions in expressions
Explicit Type Conversions called as casting in C-based languages
. Examples:-
C: (int)angle, Ada: Float (Sum)
– Note that Ada’s syntax is similar to that of function calls
Relational Expressions:
• Use relational operators and operands of various types
• Evaluate to some Boolean representation
• Operator symbols used vary somewhat among languages (!=, /=, .NE., <>, #)
• JavaScript and PHP have two additional relational operator, === and !==
• Similar to their cousins, == and !=, except that they do not coerce their
operands
Boolean Expressions:
Operands are Boolean and the result is Boolean
Example operators
FORTRAN 77 FORTRAN 90 C Ada
.AND. and && and
.OR. or || or
• No Boolean Type in C
• C89 has no Boolean type--it uses int type with 0 for false and nonzero for true
• One odd characteristic of C‘s expressions: a<b<c is a legal expression, but
the result is not what you might expect:
• Left operator is evaluated, producing 0 or 1
• The evaluation result is then compared with the third operand (i.e., c)
Which is equivalent to
if ($flag){$total = 0}
else {$subtotal = 0}
Assignment as an Expression
In C, C++, and Java, the assignment statement produces a result and can
be used as operands.
while ((ch = getchar())!= EOF){…}
ch = getchar() is carried out; the result (assigned to ch) is used as a conditional
value for the while statement
List Assignments
Perl and Ruby support list assignments
e.g., ($first, $second, $third) = (20, 30, 40);
Mixed-Mode Assignment
Assignment statements can also be mixed-mode, for example
int a, b;
float c;
c = a / b;
– In Fortran, C, and C++, any numeric type value can be assigned to any
numeric type variable.
– In Java, only widening assignment coercions are done.
– In Ada, there is no assignment coercion.
Control structures are fundamental constructs in programming languages that allow developers to control
the flow of execution in a program. They determine the order in which statements are executed and
enable programmers to make decisions and repeat actions based on certain conditions. Control
structures are essential for creating dynamic and responsive programs. Here are some common control
structures found in programming languages:
1. Conditional Statements:
• if-else Statement: Executes one block of code if a condition is true and another block if
it's false.
• switch Statement: Provides a way to select from multiple alternative paths based on the
value of an expression.
2. Looping Structures:
• for Loop: Executes a block of code for a specific number of iterations, often with a loop
control variable.
• while Loop: Repeatedly executes a block of code as long as a given condition is true.
• do-while Loop: Similar to a while loop, but the condition is evaluated after the loop body
is executed, ensuring at least one execution.
• foreach Loop (or for-each Loop): Iterates over elements of a collection (e.g., arrays,
lists) without explicit control variables.
3. Jump Statements:
• continue Statement: Skips the rest of the current iteration and moves to the next
iteration of a loop.
4. Exception Handling:
• try-catch Block: Encloses code that might raise exceptions and provides a mechanism
to catch and handle those exceptions.
• goto Statement: Transfers the control flow to a labeled statement elsewhere in the code.
Note that the use of goto is generally discouraged due to its potential to create confusing
and hard-to-maintain code.
Different programming languages may have variations or additional control structures, but the concepts
described above are common across many languages. The choice of which control structure to use
depends on the specific problem being solved and the programming language being used. Effective use
of control structures is essential for writing clear, maintainable, and efficient code.
if control_expression
then clause
else clause
Design Issues:
• What is the form and type of the control expression?
• How are the then and else clauses specified?
• How should the meaning of nested selectors be specified?
The Control Expression
• If the ‘then’ reserved word or some other syntactic marker is not used to
introduce the ‘then’ clause, the control expression is placed in parentheses.
• In C89, C99, Python, and C++, the control expression can be arithmetic.
• In languages such as Ada, Java, Ruby, and C#, the control expression must
be Boolean.
Clause Form
• In many contemporary languages, the then and else clauses can be single
statements or compound statements
if (sum == 0)
if (count == 0)
result = 0;
else result = 1;
if (sum == 0) {
if (count == 0)
result = 0;}
else result = 1;
if sum == 0
then if count == 0
then result = 0
else
result = 1
end
end
-Python
if sum == 0 :
if count == 0 :
result = 0
else :
result = 1
switch (expression) {
case const_expr_1: stmt_1;
…
case const_expr_n: stmt_n;
[default: stmt_n+1]}
• Design choices for C‘s switch statement
• Control expression can be only an integer type
• Selectable segments can be statement sequences, blocks, or
compoundstatements
• Any number of segments can be executed in one execution of the construct
(there is no implicit branch at the end of selectable segments)
• default clause is for unrepresented values (if there is no default, the whole
statement does nothing)
• C#
– Differs from C in that it has a static semantics rule that disallows the
implicit execution of more than one segment
– Each selectable segment must end with an unconditional branch (goto
or break)
• Ada
case expression is
when choice list => stmt_sequence;
…
when choice list => stmt_sequence;
when others => stmt_sequence;
end case;
More reliable than C‘s switch (once a stmt_sequence execution is completed, control is
passed to the first statement after the case statement
• Ada design choices:
1. Expression can be any ordinal type
2. Segments can be single or compound
3. Only one segment can be executed per execution of the construct
4. Unrepresented values are not allowed
• Constant List F orms:
1. A list of constants
2.Can include:
- Subranges
- Boolean OR operators (|)
Multiple Selectors can appear as direct extensions to two-way selectors, using else-if
clauses, for example in Python:
if count < 10 :
bag1 = True
elsif count < 100 :
bag2 = True
elseif count < 1000 :
bag3 = True
Iterative Statements
Iterative Statements: Examples
FORTRAN 95 syntax
DO label var = start, finish [, stepsize]
Stepsize can be any value but zero
Parameters can be expressions
Design choices:
1. Loop variable must be INTEGER
2. Loop variable always has its last value
3. The loop variable cannot be changed in the loop, but the parameters can;
because they are evaluated only once, it does not affect loop control
4. Loop parameters are evaluated only once
• FORTRAN 95 : a second form:
[name:] Do variable = initial, terminal [,stepsize]
End Do [name]
• Ada
for var in [reverse] discrete_range
loop ... end loop
• Design choices:
– Type of the loop variable is that of the discrete range (A discrete range is
a sub-range of an integer or enumeration type).
– Loop variable does not exist outside the loop
– The loop variable cannot be changed in the loop, but the discrete range
can; it does not affect loop control
– The discrete range is evaluated just once
– Cannot branch into the loop body
• C-based languages
for ([expr_1] ; [expr_2] ; [expr_3]) statement
– The expressions can be whole statements, or even statement
sequences, with the statements separated by commas
– The value of a multiple-statement expression is the value of the last
statement in the expression
– If the second expression is absent, it is an infinite loop
• Design choices:
– There is no explicit loop variable
– Everything can be changed in the loop
– The first expression is evaluated once, but the other two are evaluated
with each iteration
• C++ differs from C in two ways:
– The control expression can also be Boolean
– The initial expression can include variable definitions (scope is from the
definition to the end of the loop body)
• Java and C#
– Differs from C++ in that the control expression must be Boolean
– Iterative Statements: Logically-Controlled Loops
– Repetition control is based on a Boolean expression
• Design issues:
– Pretest or posttest?
– Should the logically controlled loop be a special case of the counting
loop statement or a separate statement?
• Iterative Statements: Logically-Controlled Loops: Examples
C and C++ have both pretest and posttest forms, in which the control expression
can be arithmetic:
while (ctrl_expr) do
loop body loop body
while (ctrl_expr)
• Java is like C and C++, except the control expression must be Boolean (and the
body can only be entered at the beginning -- Java has no goto
• Iterative Statements: Logically-Controlled Loops: Examples
• Ada has a pretest version, but no posttest
• FORTRAN 95 has neither
• Perl and Ruby have two pretest logical loops, while and until. Perl also has two
posttest loops
• Sometimes it is convenient for the programmers to decide a location for loop control (other
than top or bottom of the loop)
• Simple design for single loops (e.g., break)
• Design issues for nested loops
– Should the conditional be part of the exit?
– Should control be transferable out of more than one loop? User-Located
Loop Control Mechanisms break and continue
• C , C++, Python, Ruby, and C# have unconditional unlabeled exits (break)
• Java and Perl have unconditional labeled exits (break in Java, last in Perl)
• C, C++, and Python have an unlabeled control statement, continue, that skips the
remainder of the current iteration, but does not exit the loop
• Java and Perl have labeled versions of continue
Iterative Statements: Iteration Based on Data Structures
• Number of elements of in a data structure control loop iteration
• Control mechanism is a call to an iterator function that returns the next element in
some chosen order, if there is one; else loop is terminate
• C's for can be used to build a user-defined iterator:
for (p=root; p==NULL;
traverse(p)){ }
• Perl has a built-in iterator for arrays and hashes, foreach Unconditional Branching
• Transfers execution control to a specified place in the program
• Represented one of the most heated debates in 1960‘s and 1970‘s
• Well-known mechanism: goto statement
• Major concern: Readability
• Some languages do not support goto statement (e.g., Java)
• C# offers goto statement (can be used in switch statements)
• Loop exit statements are restricted and somewhat camouflaged goto‘s
Guarded Commands
• Designed by Dijkstra
• Purpose: to support a new programming methodology that
supported verification (correctness) during development
• Basis for two linguistic mechanisms for concurrent programming (in CSP and Ada)
• Basic Idea: if the order of evaluation is not important, the program should not
specify one