Imperative Programming Languages (your book performs examples in Pascal, I’ll show examples in Ada, and C, An excellent site for learning Ada is: http://www.scism.sbu.ac.uk/law/lawhp.html) Imperative programming languages are a family of languages where computations are performed through a sequence of actions which manipulate the contents of variables which represent values stored in memory. Imperative programming languages are also frequently referred to as procedural programming languages. Variables: Variables are “named” storage locations, which have a program defined identifier associated with a “block” of storage somewhere in memory. Variables are created via the process of “declaration” where an identifier (legal name in some programming language) is associated with storage, the size of which is determined by the variables “type”. As variables are used it may take on many different values throughout the life of the program. Each time an assignment is performed, the pervious value of the variable is lost. int x; Actions: are language statements that in some way use or manipulate the contents of a variable. Expressions, Assignment, I/O, Function Calls x = 10; printf(“%d”, x); The collection of identifiers and their associated values and the location of control in the program constitute the “state” of the program. The state is a logical model of storage which is an association between memory locations and values. A program in execution generates a sequence of states. The transition from one state to the next is determined by assignment operations and sequencing commands (alter the flow of control). Programs are a sequence of individual actions which together perform some computation. Unless carefully written an imperative program can only be understood in terms of its execution behavior. IE: for the most part there are no restrictions on how a programmer can write a program. The reason is that during the execution of the code, any variable may be referenced, control may be transferred to any arbitrary point and any variable binding may be changed (variables and a value is bound by an assignment). Thus the whole program may need to be examined in order to understand even a small portion of code. When imperative programming is combined with subprograms it is called procedural/structured programming. Structured programming is a style of programming that emphasizes hierarchical program structures in which each command has one entry point and one exit point. The goal of structured programming is to provide control structures that make it easier to reason about imperative programs (subprograms, conditional statements, and constructs for iteration.) An program written in an imperative programming language is structured as a set of one or more “subprograms”: Procedures or Functions: Function is a type of subprogram which generates and returns a value to the calling program. A procedure only communicates with the calling program by the parameters passed to it. The general format of a C program is: <compiler directives> <function prototypes> int main () { declarations executable statements > <function implementations> A C program consists of one or more functions one of which MUST be named main. C only supports one type of subprogram and that is the function. Execution of a C program is initiated with the function main. There are many types of compiler directives, but to begin with we will just consider 2. 1) #include <.ksfk> refers to s system library header file, 2) #include “ “ refers to a user defined header file. Header files give our programs access to pre-built resources such as the definitions of constants, data types, and function prototypes. These resources are typically grouped by type, based on similar purpose. Ie: stdio.h - io functions The implementation of functions declared in the system header files are contained in the C run-time library. The header file provides information about the resources to the compiler and the linker causes the function bodies to be included in the program . The C compiler pre-processor replaces the #include statement with the contents of the appropriate header file #include <stdio.h> provides our program with access to the standard i/o functions #include <math.h> provides our program with access to the mathematical functions that constitute the math library. A second useful compiler directive is #define #define name value is a way to define constants in our program….. These are not variable constants…. #define MAXIMUM 100 ---tradition is to use uppercase letters.. The preprocessor replaces the string “name” everywhere it appears in our program except within comments with the literal string “value”. The compiler makes NO attempt at verification, and the replacement could cause a compiler error. In C all statements end with a semicolon ----------- ; Comments begin with /* and end with */ and can continue across several lines… Look at example 1 Variables & bindings: Imperative programs are characterized by a sequence of “bindings” in which a name is bound to a value at one point in the program and later bound to a different value. Most descriptions of IPL’s are tied to hardware and implementation considerations where a name is bound to an address, a variable to a storage cell and a value to a bit pattern. An identifier is tied to three bindings, a binding to a location, a binding to a data type, and to a value. The location is called the lvalue, and the value is called the rvalue. X = X +1; The x on the Left of the assignment denotes a location while the X on the right hand side denotes a value. Assignment change the value at a location. When a binding occurs varies: A programming language may have static or dynamic bindings. A binding is static is it occurs before runtime, or dynamic if it occurs at run time. Languages such as C and Ada, statically bind a data type to a variable at compile type. An “lvalue” may be bound at compile time (rare), or a load time (for languages with static allocation: C, Ada, Pascal) or at run time (for languages with dynamic allocation. The “rvalue” associated with a variable may change many times during the execution of a program, so the “rvalue” binding is dynamic. Variable declarations are used to determine the amount of storage required by a program. Forms: C: Ada: Type name; name: Type; Every programming language has it’s own set of “data types” which define both the types of values that can be stored in a variable, and the amount of storage that will be allocated to the variable. C: Short, long, & int: integer types in the range 2-4 bytes Int x = 10; Float & Double: Floating point numbers, 4 & 8 bytes respectively Float y = 4.5; Char: Character variables, 1 byte Char z = ‘a’; Identifiers: a-z, A-Z, 0-9 and “_”. First character can not be a digit. Identifier length can vary between operating systems and C compilers. Ansi C considers the first 31 only, and disregards the remainder. Ada: Integer: Whole numbers X : integer := 10; Float: Real Numbers, and integer part , a decimal portion with a decimal point in between. Y: float := 10.50; Boolean: True & False flag: boolean := true; String: Data consisting of one or more characters. letters, digits or special symbols. It must have a defined length. name: string (1..25) := “Camille Hayhurst”; Character: single character. C: char := ‘c’; identifiers (names): (A-Z & a-z, 0-9), and underline, must begin with a letter. Not case sensitive. NOTE: integer, char, enumeration and Boolean data types are often referred to as discrete types, because they have a predefined, limited set of values. Note: Variables are different than named constants.. the value associated with a variable can change throughout the program execution, while the value associated with a constant can not: In ada we define constants like variables, but use the KEYWORD “constant”. Constants must be assigned an initial value when they are declared!!!. Ada PI: constant float := 3.1415; C basically offers two types of constants: C: Const type identifier=initial value; const int X = 12; Defines a named constant, which is a variable whose value can not be changed. These are treated as variables by the compiler, and allows the compiler to perform consistency checking when a program is compiled. #define identifier value #define MAX 100 defines a “symbolic” constant, which is simply a literal string, that can be included in header file. The preprocessor simply does a “blind” substitution of the value anywhere that the “identifier” is encountered in the program text. This can cause compilation errors. Binding of values The variable and a value is Bound by an assignment statement: C: Ada: V = E; V := E; Where V is a variable name, and E is some expression. Either a constant or an expression which yields some value. Assignment is what distinguishes imperative programming languages from other programming languages, it is read “assign the name V to the value of the expression E until the name V is reassigned to another value. X := 3 X := X +1 There are many kinds of assignments: X := X op E --- calculation X=X+1 X := V -- V is a literal value X = 3 “C” X op= E multiple assignment X0:= X1:=X2:= E; C is NOT a strongly typed language, which means we can have variables of different types in the same expression. Ada IS a strongly typed language. We may not have variables of differing types in the same expression, unless we explicitly perform a type conversion. In Ada: X: integer =0; Y: float := 3.4; X := integer(Y) +1; A type conversion is C is called “casting”. Where we basically coerce a value of one type into another. X = (int) Y +1; Flow of Control: The order in which statements are executed is the “flow of Control” within a program. The default “flow” of control is sequential, statements are executed one after the other in the order in which they occur in the file. Control Structures: Are syntactic structures that define the order in which executable statements are performed. These are sequence control mechanisms and fall into 4 categories :sequential composition, alternation and iteration. Sequential Composition: Sequential composition specifies a linear ordering for the execution of commands. It is the default flow of control, where statements are executedc one after the other in the order in which they occur in the file. It is usually indicated by placing commands in textual sequence and a special symbol (ie: ;) is used as either a line separator or line terminator the termination. C & Ada: ; is a line terminator Sequential composition is a sequence of two or more commands, it is the simplest form of composition, and available in every imperative language: s0;s1;s2 C: Hello World #include "stdio.h" int main (void) { printf ("HELLO WORLD\n"); return; } Ada: Hello World with Text_Io; procedure Hello is begin Text_Io.Put(“Hello World!!”); Text_Io.New_line; Text_io.Put(“I am Camille”); end Hello; Selection: (alternation): Selection permits the specification of alternate sequences of actions (commands), where the selection of a particular sequence to execute is based on the value of a logical expression. Examples of these statements are the if & case (switch) constructs. Alternation: may contain a number of alternative sequences of command from which EXACTLY ONE is chose to be executed: if, nested if, and case. Classic forms: of selection statements are: E represents a logical expression which can be evaluated to true or false, and S, S1 & S2 represent sets of one or more executable statements. If E Then S1; If the logical expression E is true, then execute step S1, other wise continue with the next executable statement Decision Tree for simple If construct true E S1 In C the basic if statement has the following format: if (logical expression) statement; A logical expression in C consists of a series of one or more conditional expression. A conditional expression has the form: Value1 relational-operator value2 Where value1 & value2 can be a variable name, a literal value, or the value returned from a function call. In C, the execution of a conditional statement has a numeric value. A value of 0 (zero) is defined as “false” and every thing else is considered as true!!!! Relational operators: <, >, <=, >=, = = , != (left to right associatively) Compound logical expressions contain a series of 2 or more conditional expressions, joined with the logical and (&&), or the logical or (||) operators. The logical not (!) negates (reverses) the initial value of an expression. Truth table: P True True False False Q True False True False P && Q True False False False P || Q True True True False !P False False True True Sample expressions: 1<9 X==y X > 0 && X < 10 Peculiarities: int a = 5; if ( a == 10) is false if ( a = 10) is TRUE… Assignment is frequently incorrectly used in place of the equality operator with is two equal signs. This assignment would assign the value of 10 to a, this non zero value is interpreted as true!!!!! Short circuit evaluation; The evaluation of multiple logical expressions ( with the && and ||), evaluation stops as soon as the truth of the expression is known: P && Q if P is true then Q is evaluated. If P is false there is no need to evaluate Q. P || Q if P is false then Q is evaluated. If P is true then there is no need to evaluate Q. Ada In Ada, the general format of the IF statement is: if condition then statement (s) end if; The relational operators are: =, /=, <, <=, >, >= The logical operators are: and, or, not Short circuit evaluation of expressions is NOT performed, there are special “short circuit operators”: and then, and the or else Sample 1- C:, simple if with one executable statement. #include <stdio.h> int main(void) { int num; printf ("enter a number: "); scanf ("%d", &num); if (num > 0) printf("number is positive"); printf ("All done"); return; } with Text_Io; package Int_io is new Text_io.Integer_io (num=>Integer); procedure if_demo is num: integer; begin Text_io.Put(“enter a number: ”); text_io.new_line; Int_io.Get(item=> num); if (number > 0 ) then Text_io.put(“number is positive”); end if; text_io.New_line; Text_io.put(“All done”); end if_demo; The second general from of the IF statements is the “if then else” construct. Which specifies a series of actions to perform if the expression is true, and an alternate group of statements if the expression is false. If E Then S1 Else S2; False true E S1 S2 If then Else example C #include <stdio.h> int main (void) { int magic; int guess; int i; magic = 1325; printf ("Enter your guess: "); scanf ("%d", &guess); if (guess == magic) printf (" You are RIGHT!!!"); else printf ("... Sorry you are wrong.."); } return; } Ada with Text_Io; package Int_io is new Text_io.Integer_io (num=>Integer); procedure if_demo is number, guess: integer; begin number := 2; Text_io.Put(“Guess a number between 1 and 10”); text_io.new_line; Int_io.Get(item=> guess); if number = guess then Text_io.put(“you guessed correctly good for you!”); else Text_io.put(“Sorry, you guessed wrong”); end if; end if_demo; Multiple executable statements in an if clause: C #include <stdio.h> int main (void) { int magic; int guess; int i; magic = 1325; printf ("Enter your guess: "); scanf ("%d", &guess); if (guess == magic) { printf (" You are RIGHT!!!"); printf (“the number was: %d, magic); } else printf ("... Sorry you are wrong.."); } return; } ada with Text_Io; package Int_io is new Text_io.Integer_io (num=>Integer); procedure if_demo is number, guess: integer; begin number := 2; Text_io.Put(“Guess a number between 1 and 10”); text_io.new_line; Int_io.Get(item=> guess); if number = guess then Text_io.put(“you guessed correctly good for you!”); Text_io.put(“You are so smart!”); else Text_io.put(“Sorry, you guessed wrong”); Text_io.put(“You big dummy!”); end if; end if_demo; Nesting of If statements: It is possible to include additional if-then-else constructs inside the set of executable statements for an if statement. When this occurs this is called “nesting”, because one if statement in contained inside another: C #include <stdio.h> int main(void) { int a,b; char ch; printf ("Do you want to Add, Subtract, Multiply or Divide?\n"); printf ("Please enter the first letter of what you would like to do?"); ch = getchar(); printf ("Enter first number: "); scanf ("%d", &a); printf ("Enter second number: "); scanf ("%d", &b); if (ch == 'A') printf ("Answer is %d", a+b); else { if (ch == 'S') printf ("Answer is %d", a-b); else { if (ch == 'M') printf ("Answer is %d", a*b); else { if (ch == 'D' && b!=0) printf ("Answer is %d", a/b); } } } return; } Ada with Text_io; procedure nestedif is package Int_io is new Text_Io.Integer_Io(Num=>Integer); number1, number2, result: integer:=0; operator: char; begin Text_io.put(“please enter 2 integer numbers: “); Int_Io.Get(number1); Int_Io.Get(number2); Text_Io.new_line(2); Text_Io.put(“Please enter an arithmetic operator”); Text_Io.get(operator); if operator = '*' then result := number1 * number2 elsif operator = '/' then result := number1 / number2 elsif operator = '+' then result := number1 + number2 elsif operator = '-' then result := number1 - number2 else Text_Io.put(“invalid operation”); end if; text_io.put(“the result is: “); int_io.put(result); text_io.new_line; end nestedif; Switch/Case statements can be used to simplify the logic of nested/multiple if statements when only ONE execution path will be executed between all of the alternatives: C: #include <stdio.h> int main(void) { int a,b; char ch; printf ("Do you want to Add, Subtract, Multiply or Divide?\n"); printf ("Please enter the first letter of what you would like to do?"); ch = getchar(); printf ("Enter first number: "); scanf ("%d", &a); printf ("Enter second number: "); scanf ("%d", &b); switch(ch) { case 'A': printf ("Answer is break; case 'S': printf ("Answer is break; case 'M': printf ("Answer is break; case 'D': if (b!=0) printf ("Answer break; } %d\n", a+b); %d\n", a-b); %d\n", a*b); is %d\n", a/b); Ada with Text_io; procedure nestedif is package Int_io is new Text_Io.Integer_Io(Num=>Integer); number1, number2, result: integer:=0; operator: char; begin Text_io.put(“please enter 2 integer numbers: “); Int_Io.Get(number1); Int_Io.Get(number2); Text_Io.new_line(2); Text_Io.put(“Please enter an arithmetic operator”); Text_Io.get(operator); case operator is when ‘+’ => result = number1+ number2; when ‘-‘ => result = number1-number2; when ‘* ‘=> result = number1*umber2; when ‘/ ‘=> result = number1/number2; when others => Text_Io.put(“invalid operation”); end case; text_io.put(“the result is: “); int_io.put(result); text_io.new_line; end nestedif; Iteration: Iteration specifies that a sequence of commands may be executed zero or more times. At run time the sequence is repeatedly executed, the number of times the repetition occurs is determined by a logical expression. Examples of these statements are while, for, repeat, and loop. “Definite Iteration “. In a “definite” iteration the loop is executed a predetermined number of times. In other words by the time control reaches the loop, the number of iterations is already known. The classic construct for definite iteration is the classis “FOR” loop. These type of loop constructs typically specify an index variable, and range of values which the index variable will be assigned. The index is assigned the value of the lower bound during the first iteration through the loop. Then the index variable is incremented (or decremented) during each successive iteration, until the “upper bound” is reached. In Ada, the “for” loop is a definite loop construct: ADA --For loops which increment the index variable by 1 for index in lower .. upper loop statements in loop body end loop; -- for loops which “decrement” the index variable by -1 for index in reverse lower .. upper loop statements end loop; EXAMPLE For I in 1..10 loop Int_Io.Put(I); end loop; Differences with Ada: 1) the Index variable “I” is automatically declared for you, and is a local variable inside the loop. 2) It is ILLEGAL to change the value of the index variable inside the loop. I can appear in the right hand side of expressions, but can not be assigned a value inside the loop. 3) After the loop terminates, the index variable ceases to exist. Note the for loop in C is not a definite loop construct. Indefinite Loop constructs: An “indefinite” iteration is one for which the number of iterations through the loop is not know when the control reaches the loop. The number of iterations is determined through the computations performed inside the loop. The classic indefinite iteration construct is the WHILE loop. PRE-TEST LOOPS: The classis while loop is also one type of “pre-test” loops. Pre-test loop are loop constructs where the exit condition is checked at the BEGINNING of each iteration. Therefore the body of a pre-test loop can be executed ZERO OR MORE times. Ada While Loop: while condition loop statements; end loop; Example: I := 10; while (I <=100) loop Int_IO.put(I); I := I+10; end loop POST TEST LOOPS A post-test loop construct checks the exit condition at the END of each iteration through the loop. This means the body of the loop will ALWAYS be executed at least ONCE. ada: label: loop statements; exit label when condition; end label; example: sum the numbers from 1 to 10 sum, I: integer; I := 1; Sum := 0; Sumit: Loop Sum := sum +I; I := I +1; Exit sumit when I > 10; End sumit; NOTE: In ada the “loop” construct is a generic loop, the exit condition can appear anywhere inside the body of the loop. Abstraction: A sequence of commands that is named and the name is used to invoke the sequence of commands causing them to be executed : functions, procedures, subprograms