Anda di halaman 1dari 27

5.

APPLIED INFORMATICS

The chapter presents structures applied to data, knowledge, strategies, types, or higher abstractions. For the computable aspects of these concepts, the structures are generated, induced, combined by specific algorithms. Lists: order in n1 dimensions // examples are in C++ (2nd version) Trees: representation of Divide et Impera et Intellige. Graphs: representation of any structures. Class is the essential element of the programming/ hardware-description language. The concept can represent abstract or concrete types, structure nodes, process operations, interfaces, templates for applications, intermediaries of complex activities etc. The object-orientation paradigm contains at least the first 4 listed principles, being extendable to, e.g., the last three of following list: 1. Abstraction: essential characteristics of the object equivalence classes, and representatives by that precise conceptual borders are established, in the context of subjects perspective; 2. Hierarchy: structure of abstractions, i.e., simple/ multiple inheritance, symbolization degree, structure of the objects, abstraction level of the description, abstraction level of the (self-)knowledge; 3. Encapsulation: organized abstraction to represent the structure-behavior (implementation, interface); 4. Modularity: dividing in autonomous parts, i.e., relatively independent easy to intercommunicate; 5. Type: reducing the variability of the objects, usually with exceptions; 6. Parallelism: nondeterminism that differentiates the active objects from the passive ones; 7. Persistence: extended existence, spatial, temporal, or space-temporal. 5.1 Programming paradigms. Functions. Complexity Programming is conceived mostly as representation and processing of information: a paradigm is defined in relation to the priority of one of these activities. An adequate example is the factorial function, so natural n! and so real as it rises as nne-n(2n) n as power, e, and . Imperative programming: important is the algorithm to solve a problem, and this is a finite sequence of commands addressed to a machine to execute them. Data and algorithms are stored in disjoined memory zones.
long unsigned fi (unsigned n) {long unsigned f=l; for(;n;) f*=n--; return f; }

Declarative programming: important is the representation of the approach domain that contains both the domain of the problem to solve, as the approach method. The problems solution results from manipulation the representation. The structure of the concrete machine does not reflect that of the abstract system addressed by the programming language. It contains entities general enough to represent both data and algorithms.
long unsigned frtd (unsigned n) {return n? n*frtd(n-1):1}// recursive top-down

Functional programming: the first entities for general representation were lDn={iIN| i<=n}V, the lists, building a support to express the functions and to address a function evaluator, e.g., LISP (List Processing)
Defun fact(n) (if (= n 1) 1 (* n fact(- n 1) ) ) ) // recursive top-down

Logical programming: logical formulas are general representation entities, and the abstract computing system applies logical inference rules, e.g., Prolog (programmation logique).
fact(0,1). // recursive bottom-up fact(N,F) :- fact(N1,F1), N is N1+1, F1 is F*N // facts, rules, and questions unsigned frtd(unsigned f,unsigned i,unsigned n) {return(n-i)? frtd(f*i,++i,n):f;}

Object-oriented programming: enhancing declarative programming with modularity, together with representation of information by more general entities than mathematical function or logical formula enables static declarative and dynamic procedural aspects of the represented object to coexist in a unitary concept. This emerged to object-oriented programming. The addressed abstract system is a hierarchically organized set of objects, each representing a system described by static aspect and behavior in the context of the other objects. E.g., Smalltalk, call: n fact, definition: self > 1 ifTrue [^(self 1) fact * self].
self < 0 ifTrue [^self error: negative fact] ^1

C++ An object is defined by (identity, state, behavior); it possesses a constant self-directed pointer this, and is the instance of a class; its attributes are member data and methods (functions) that can be declared: public accessible from the whole validity domain, realizing the objects interface protected accessible to the methods of the class and the derived classes, assisting the inheritance private (implicitly) accessible to the methods of the class. Classes have constructors and a destructor (~); friend functions can access private components of the class: const methods do not modify the state of the object they are called for inline functions ask the compiler for efficient implementation; the methods defined inside the class declaration are implicitly inline. header files contain definitions; and can be included anywhere they are needed. 71

// fact-oo.h class Nat{long unsigned n; //class for IN with private static attribute n public: Nat(){n=0;} //constructor without parameters Nat(long unsigned m){n=m;} //constructor with parameters ~Nat(){delete &n;} //destructor for dynamic bool equal (const Nat alt) const {return (n==alt.n);} //implicit inline Nat predec () {return Nat(--n);} //constructor by type conversion Nat minus (const Nat alt) const {return n-alt.n;} //constant object& parameter friend Nat operator* (Nat, Nat); Nat fr () const; long unsigned val() const {return n;} ); inline Nat operator* (Nat a, Nat b) {return Nat(a.n*b.n);} Nat Nat::fr(){Nat zero; Nat one(1); return equal(zero)?one:(*this)*minus(one).fr();} // fact-oo.cpp long unsigned fi(unsigned n) {long unsigned f=l; for(;n;)f*=n--; return f;} #include <iostream.h> #include fact-oo.h Nat fre (Nat a) {Nat zero; Nat one (1); return a.egal(zero)?one:fre(a.predec())*a} void main() {unsigned n; do {cin>>n; Nat a(n); cout<<n<<!=<<a.fr().val())<<=<<fre(a).val()<<=<<fi(n)<<endl;} while(n);}

Functions: The subprograms communicate with the refined (sub)program by results and parameters. Parameters are formal objects, e.g., variables, subprograms, included in a subprogram declaration to be substituted by effective objects at the call. The correspondence between formal and effective parameters, as that between the resulted local object and the object to actualize by calling the function, is realized by: 1. Transfer by value: The formal parameter belongs exclusively to the subprogram defining it, being just initialized by a constant of a compatible type received from the calling (sub)program when the called subprogram is activated. Like this only input objects can be transmitted to the subprogram, nothing of the possible alteration of the formal parameters returns to the calling (sub)program. Transmission is actually an assignment so the effective parameters can be expressions of the same type or a compatible one with the correspondent formal parameters. By value are transmitted: data, subprograms, arguments, and function results. In C++, all objects except vectors are transmitted implicitly by value. 2. Transfer by reference: The formal parameter is a local renaming of an object (variable or part of a structured variable) belonging to the superior calling (sub)program. Hence, any action on the formal parameters influences the effective ones; evidently, these have to be objects or parts of structured objects. Input and output data can be transmitted by reference. Often, input parameters representing structured data are transmitted by reference for efficiency space and assignment time for the local variable are saved; for the same reason vectors are transmitted implicitly by reference. For nonvector objects the reference transmission can be forced by appending to the type name of the parameter the symbol &, or transmitting by value a pointer to the object to transmit. If the reference parameter is not to be altered, it is prefixed by const. Transmission by reference for a function result has to be used carefully, not to return the address of a local object that is destroyed when the function ends/ exists. Example 1: Conversion of arithmetic expressions Infixed expressions, i.e., operators between operands are used in human communication; the postfixed form does not need parentheses, hence are easier to evaluate by a computer. The recurrent conversion follows in detail the recursive definitions for the 2 forms of expression - simplified to emphasize the idea. The metalanguage to describe the expressions language uses the signs: | global alternative, ::= syntactical structure. Infixed form: expression ::= term | term additive_operator expression ex: a+(b*(c+d)*e+f) additive_operator ::= + | multiplicative_operator::= * term ::= factor | factor multiplicative_operator term factor ::= prime | (expression) Postfixed form: expression ::= term | term expression additive_operator ex: abcd+e**f++ term ::= factor | factor term multiplicative_operator factor ::= prime | expression prime ::=char Example 2: Pascal/ Exercise C++: Recursive conversion & evaluation following the anterior syntax. a) Global variables:
Program {Pascal} conversion; {infix --> postfix} var c: char; {global variable} procedure search; begin repeat read(c) until (c<>' ') end; {blanks between elements} procedure expression; var op: char; procedure term; procedure factor; begin if c='(' then begin search; expression end else write(c); search end; begin {term} factor; if c='*' then begin search; term; write('*') end end{term}; begin {expression} term; if c in ['+','-'] then begin op:=c; search; expression; write(op) end end{expression}; begin {conversion} search; repeat write(' '); expression until c = '.' end.

72

b) Autonomous functions:
program conversion; {infix --> postfix}procedure expression; var c: char; function term: char; var c: char; function factor: char; var c: char; function search: char; var c:char; begin repeat read(c) until (c<' '); search:=c end; begin c:=search; if c='('then expression else write(c);factor:= search begin {term} c:=factor; if c='*' then begin term:=term; write(c) end else term:= c end; {term} begin c:=term; if c in ['+','-'] then begin expression; write(c) end end; begin {conversion} repeat write(' '); expression; readln until eof end.

end;

c) Writing the expression to a file needs variable/ global parameters:


procedure expr (var f_expr: text); var c: char; function fact (var f_expr: text): char; var c:char; function search: char;...begin {fact} c := search; if c='(' then expr(f_expr) else write(f_expr,' "',c); fact:=search; end; function term (var f_expr: text): char; ... begin {expr} c:=term (f_expr); if c in ['+','-'] then begin expr(f_expr); write(f_expr,' ',c) end end;

Complexity analysis IN is the set of natural numbers. The problem size is symbolized by a natural number n, e.g., representing the cardinal of the processed data. The algorithms complexity for abstract operations independent of the implementation depends on n: it is asymptotically computed, i.e., n, to estimate the size of the problems that can be approached with the studied algorithm. For efficiency, the important to estimate cases are the average and the worst; for comprehension, the best is crucial. "O": f, g IN IN, if c0, n0 IN n > n0 f(n) < c0 g(n), then f = O(g). Exercise: general case. " " (asymptotically equal): f, g ININ. limn f(n)/g(n)1 f g. Medium temporal complexity for recurrent relations (C1{0, 1}): 1. Cn = Cn/2 + 1 (recursive function the halves the number of inputs at each call): case n = 2m, recurrence relation, definition. Cn log2n 2. Cn =Cn/2+n (recursive function the halves the number of inputs traversing all at each call ): case n = 2m, recurrence relation, the sum of the resulted series, definition. Cn 2 n 3. Cn = 2 Cn/2 + n (recursive function traversing all inputs while halving them at each call) Divide et Impera et Intellige: Cnnlog2ncase n=2m, recurrence, , Cn=2Cn/2+nCn/n=2Cn/2/n+1= ..= m. 4. Cn = 2Cn/2 + 1 (recursive function that divides the number of inputs at each call): Cn2n case n=2m, recurrence relation, definition, Cn=2Cn/2+nCn/n=2Cn/2/n+1/n=..=1+1/2+..+1/2m2. 5. Cn = Cn-1 + n (recursive function decrementing the number of inputs at each call) (C1 = 1): Cn n2 / 2 recurrence relation, definition , Cn = Cn-1 + n = ... = 1 + 2 + ... + n = n (n-1) / 2. 5.2 Storage classes. Pointer types Predefined data structures, static or dynamic are homogeneous finite sequences (vector: [])/ nonhomogeneous (record: struct). Declared variables have an associated identifier and a textual controlled existence: local: exist as long as the block they were declared in is active; declared as register recommends to the compiler to store them in a high speed register; global: exist during the whole program execution; declared as static: - global: belong to the file, - local: retain their value when also outside their validity domain, - data member of a class: characterize the state of the class; declared as extern become accessible to all the files that compose a modular program. Example 3: Filter for prime numbers (sieve of Eratostene) // primerat.h: construction of a Boolean vector indicating the primes n
void eratostene(const unsigned n, unsigned prim[]) {prim[0]=0; // initialization for (unsigned i=1; i<=n; i++) prim[i]=1; // multiples of primes are not prime for (unsigned i = 2; i <= n / 2; i++) if (prim[i]) for (unsigned j = 2; j <= n / i; j++) prim[i*j] = 0; }

// primerat.cpp: test whether a number is prime


#include <iostream.h> #include "primerat.h" void main(){const unsigned m=1000; unsigned n; char* txt; unsigned prim[m+1]; eratostene(m, prim);//vectors are transmitted by reference implicit do{cin>>n;txt=prim[n]?" ":" no ";cout<<n<<txt<<"is prime"<<endl;}while(n);}

Exercise: testPrime <= binary search in a linked list of natural numbers. Example 4: Dividing integers //divInt computes (quotient, rest)
struct IntPair {int q; int r;}; IntPair divAux (unsigned m, unsigned n, unsigned q) {if (m>=n) return divAux(m-n,n,++q); IntPair p; p.q=q;p.r=m; return p;}

73

inline unsigned mod (int m) {return m >= 0 ? m : -m;} // absolute value IntPair divInt (int m, int n) { IntPair p = divAux (mod(m), mod(n), 0); if ((m >= 0) && (n < 0)) {p.q = -p.q; return p;} if ((m < 0) && (n > 0)) {p.q = -(++p.q); p.r = n - p.r; return p;} if ((m < 0) && (n < 0)) {p.q++; p.r = -n - p.r; return p;} return p;}

Example 5:

Mode and frequency (the most frequent element in an ascendently sorted vector)

typedef int* vect; struct ModFreq {int mod; unsigned freq;}; void sort (vect, unsigned); // declaration functionDomain Codomain ModFreq det (vect, unsigned); ModFreq detIter (vect, unsigned); ModFreq detRec (vect, unsigned); #include <iostream.h> void main () { while (1) { vect a; unsigned n; ModFreq mf; cin >> n; for (unsigned i = 0; i <= n; i++) cin >> a[i]; sort (a,n); mf = detIter (a, n); cout<<mf.mod<< ' ' <<mf.freq<<endl; mf = detRec (a, n); cout<<mf.mod<< ' ' <<mf.freq<<endl; mf = det (a, n); cout<<mf.mod<< ' ' <<mf.freq<<endl; } } void ord (int *p, int *q) int t; // interchange by pointers to addresses {if (*p>*q) {t=*p; *p=*q; *q=t;}} // transfer by value unsigned minPos (Vect a,unsigned i,unsigned n){int min=a[i];unsigned pos=i; for(unsigned j=i+1;j<=n;j++)if(a[j]<min){min=a[j]; pos=j;} return pos;} void sort (Vect a, unsigned n) { // iterative selection Sort if (n>1) for (unsigned i=0; i<n; i++) ord (&a[i], &a[minPos(a,i,n)]);} ModFreq detIter(Vect a,unsigned n){ModFreq mf; unsigned ftmp=1; mf.freq=1; mf.mod=a[0]; for (unsigned i=1;i<=n;i++) if (a[i]-a[i-1]) ftmp = 1; else if(++ftmp>mf.freq){mf.freq=ftmp; mf.mod = a[i]; return mf;} ModFreq detRec (Vect a, unsigned n) {ModFreq mf; if (!n) { mf.mod=a[0]; mf.freq=1; return mf;} mf=detRec(a, n-1); if (a[n]==a[n-mf.freq]){mf.mod = a[n]; mf.freq++;} return mf;} ModFreq det(Vect a, unsigned n){ModFreq mf; mf.mod=a[0]; mf.freq=1; for(unsigned i=1;i<=n;i++)if(a[i]==a[i-mf.freq]) {mf.mod=a[i]; mf.freq++;} return mf; }

Example 6:

Quicksort (complementary to mergesort) (C.A.R.Hoare)

typedef int T; //qsort.h void intch (T &p, T &q) {T t=p; p=q; q=t;} // transfer by reference unsigned part(T a[],unsigned p,unsigned u) {T v=a[u]; p--; unsigned q=u; do {while (a[++p]<v); while (a[--u]>v); // the last element is used to partition if (p<u) intch (a[p], a[u]); else {intch (a[p], a[q]); return p;} } while (1); }// cycle with interior test simulated by infinite cycle + return void qSort (Tip a[], unsigned p, unsigned u){ // quick sort if (p<u) {unsigned q=part (a, p, u); // partition element qSort (a, p, q-1); qSort (a, q+1, u);}} //qsort.cpp #include <iostream.h> #include "qsort.h" void main () {T a[10]; unsigned n; while (1) {a[0]=-100; // the first element is minimal in T cin >> n; for (unsigned i = 1; i <= n; i++) cin >> a[i]; qSort (a,1,n); cout<<"a sorted:"<<'\n'; for (i=1;i<=n;i++) cout<<a[i]<<' '; cout<<endl;}} /* otherwise */ typedef int Tip; struct Pair {unsigned p; unsigned u;}; /* qsort.h */ void intch (Tip &p, Tip &q) {Tip t = p; p = q; q = t;} Pair part (Tip a[], unsigned p, unsigned u){Tip v=a[p];// first element partition while (p<=u) {while (a[p]<v) p++; while (a[u]>v) u--; if (p < u) {intch (a[p], a[u]); p++; u--;}} Pair pair; pair.p = p; pair.u = u; return pair;} void qSort (Type a[], unsigned p, unsigned u){Pair pair=part(a,p,u);//partition element if (pair.p<u) qSort(a, pair.p, u); if (pair.u>p) qSort(a, p, pair.u);}

Example 7:

String - character sequence (equality, copy, length, transform to number)

#include <iostream.h> #include <string.h> unsigned streq(char s1[],char s2[]){unsigned i=0; return (s1[i] != s2[i++])? 0: (s1[i] == s2[i]);} unsigned streqre (char s1[], char s2[]) { unsigned i = 0; while((s1[i]!='\0')&&(s2[i]!='\0')&&(s1[i]==s2[i]))i++; return(s1[i]==s2[i]);} void strcopy(char s1[],char s2[]) {unsigned i=0; while ((s2[i]=s1[i]) != '\0') ++i;} unsigned strlen1(char s[]){unsigned len=0; while (s[len] != '\0')++len; return len;} unsigned strlen2(char *s) {unsigned len=0; while (*s!='\0'){++len;++s;} return len;} unsigned atoi (char s[]){ unsigned i; unsigned n = 0; for (i=0; s[i]>='0'&&s[i]<='9'; ++i) n = 10*n+s[i]-'0'; return n; }

74

Dynamic variables. Pointer types In contrast to static variables, dynamic variables can be created/ destroyed anytime during the execution, by executable instructions. Being undeclared, no identifier is associated to a dynamic variable. Its reference needs knowing its address in the memory. The advantage of the possibility to work with dynamic variables is twofold. a) The implemented structuring modes are limited. While for an algorithm austerity for structuring modes is recommended, for data the access to most general structures is necessary for representing arbitrary relations between elements of a set, and for compatibility of different types without compromising typing. Reflecting in the language of the computer representation on that the programming paradigm bases -low level aspects can contribute to program efficiency, e.g., C++: predefined functions to create new and destroy - delete dynamic variables of any type; data types whose variables contain addresses in a pointer/ reference; dynamic variables can be referred by pointer variables of a type associated to the dynamic variables type pointer type linked to a certain type; this is the mechanism to represent arbitrary relations; a universal pointer type compatible in assignments to any linked pointer type - void*; such variables can not refer a dynamic variable without an anterior type match; for pointer types 0 is the constant that represents the state of a variable that indicates nothing. Example 8: typedef char*Data; struct Elem{Data d; Elem*nxt}; typedef Elem* Leg; Interpretation: a pointer type has as support a theoretical infinite set of values representing addresses of object of the type it is linked to. The operations defined for these types, including void*, are assignment, equality test, and arithmetic; for the linked pointer types, applying the addressing operation * to a pointer type expression, extracts the result: * pointer-type-expression. Addressing is not applicable to void* expressions, nor to 0 valued pointers. Any pointer type variable can have the value 0. Type void* enables exceptions from the strong typing by type hiding and type conversion. Creation of dynamic variables is handled by new type-name //[] that allocates a memory-space accessible to the whole program, and adequate to a variable sent as parameter, the address of the memory zone being returned. Destroying a dynamical variable follows the call: delete pointer-type-expression delete []pointer-type-expression 5.3 Dynamic data structures The data structures based on global relations are vectors name[] and struct-ures. Different kind of vectors, e.g., vectors of vectors equivalent to multidimensional vectors, and objects of a struct type representing heterogeneous ordered sets, can be created/ destroyed, statically (textual)/ dynamically (executable instructions). Using dynamic variables and pointers we can represent structured sets by local relations among objects. Together with the information associated to each object, the references to other objects are memorized. Objects are represented dynamic as struct, having fields to store different aspects of the associated information, and fields that link to other dynamic variables of the same type. Such a linked dynamic structure is accessible by a frontier that is formed of some records to that refer, possibly static, pointer variables, e.g., the first element of a one-sense linked list, the first and the last elements of a two-sense linked list, the root off a binary tree, all nodes of a finite graph. Access to an interior element of a dynamic linked structure is stepwise, starting from the frontier, and using the neighborhood relations. This disadvantage relative to direct access characterizing the structures defined by global relations, is the payment for the advantage of flexibility. Linked structures are easy to modify, adding/ deleting elements in/ from any arbitrary positions, or reorganizing the structure. Linked structures can be also represented as parallel vectors containing the information on the contents, respectively on the structure, but this solution rarely presents advantages. Example 9: Circular linked list (who remains the last if there are n in circle and are eliminated m by m?") // #include <iostream.h> The example suggests selfreference as another way to finish linking.
unsigned alaBala (const unsigned m, const unsigned n) { struct Pers {unsigned name; Pers* nxt;}; //type of the list component Pers* x = new Pers; x->name = 1; Pers* t = x; // construction of the list for (unsigned i=1; i<n; i++) {x->nxt = new Pers; x = x->nxt; x->name = i+1;} x ->name = n; x->nxt = t; // closing the circular list + deleting m by m while (x != x->nxt) {for (i = 1; i < m; i++) x = x->nxt; // traversing t = x->nxt; x->nxt = t->nxt; delete t; } return x->name; }

Example 10: Stack (Last In-First Out) Stack is a list that adds (push) and extracts (pop) at same end - top, addressed by the stack pointer, simulating the rule: first in = first out; as for any list it needs to determine the emptiness, and to initialize as any object. If implemented as vector (static or dynamic) it needs stack full test. a) Stack of T type components, dynamic vector: // invString.cpp inverts a sequence of integers
#include <iostream.h> #include "stack-to.h" // calls the constructor without parameters typedef int T; void main () { T c = 1; do {STACK st; while(c){cin>>c; st.push(c);} while(!st.empty()){c=st.pop();cout<<c;}}while(c);}

75

// stack-to.h class STACK { public:

unsigned dim; T* sp; T* sb; STACK () {sp = sb = new T [dim = 100];} STACK (unsigned d){sp = sb = new T [dim =d];} ~ STACK () {delete sb;} bool empty () {return (sp == sb);} void push (T c){*sp++ = c;} T pop () {return *--sp;} };

b)

Stack of void* type components, linked list:

// stack-vo.h declarations; the stack interface typedef void* DATA; // generic pointer universal stack struct ELEMENT {DATA d; ELEMENT* next;}; typedef ELEMENT* LINK; class STACK { protected: LINK sp; //for a possible inheritage - protected public: STACK (){sp = 0;} virtual ~ STACK (); // binds types at runtime; // for destructor, a pointer to the basis class can be used to delete an object of the derived class bool empty () const { return sp ? false: true;} void push (const DATA);// the by reference sent parameter can not be modified DATA pop (); }; // stack-vo.cpp definitions; stack implementation; is added to the project for invString #include "stack-vo.h" STACK:: ~ STACK () {while (sp) {LINK tp = sp; sp = sp->next; delete tp;} } void STACK:: push (const DATA d) {LINK newElem = new ELEMENT; newElem->d = d; newElem->next = sp; sp = newElem;} DATA STACK::pop(){LINK tp = sp; DATA td = sp->d; sp=sp->next; delete tp; return td;} // invString.cpp - inverts sequence of integers stacked by pointers stack application #include <iostream.h> #include "stack-vo.h" void main(){int c=1; do{STACK st; while(c){cin>>c; // int* pc=&c; st.push (pc)// error int* pc = new int; *pc = c; st.push (pc);} while(!st.empty()) {int* pc = (int*)(st.pop()); c = *pc; delete pc; cout<<c;}} while(c);}

c) Stack of void* type components, linked list finished by selfreference:


//st9v-ozo.h: stack interface; can be common for more implementations of the stack typedef void* DATA; class STACK { protected: struct ELEM {DATA d; ELEM* next;}; typedef ELEM* LINK; LINK sp; LINK z; public: STACK(); ~STACK(); bool empty(); void push (DATA); DATA pop();}; //st9v-ozo.cpp: implementation of stacks mthods #include "st9v-ozo.h" STACK::STACK() {sp = new ELEM; z = new ELEM; z->next = z; sp->next = z; } STACK::~STACK(){while (sp != z) {LINK t = sp; sp = sp->next; delete t;} } BOOLEAN STACK::empty() {return (sp->next == z)? true: false; } void STACK::push (DATA new_d){LINK newEl = new ELEM; newEl->d = new_d; newEl->next = sp->next; sp->next = newEl; } DATA STACK::pop () {LINK tp = sp->next; DATA td = (sp->next)->d; sp->next = tp->next; delete tp; return td; } //invst9vz.cpp: inversion of integers/ character sequence #include <iostream.h> #include "st9v-ozo.h" void main () { int i = 1; char c = ';'; do { STACK st_i; STACK st_c; while (i) {cin >> i; int* pi = new int; *pi = i; st_i.push (pi);} while (c!='.') {cin>>c; char* pc = new char; *pc = c; st_c.push(pc);} while (st_i.empty()==false){int*pi=(int*)(st_i.pop());i=*pi; cout<<i;} while (!st_c.empty()) cout << *(char*)(st_c.pop());} while(i); }

Example 11: Recursive inversion of a sequence (compilers stack simulates the recursivity)
#include <iostream.h> void invString () {char c; cin.get(c); // cout.put(c); if (c != '.') {invString (); cout.put(c);} else cout<<endl; }

Example 12: Queue (FIFO) Queue (waiting queue) is a linear list of elements, adding at rear, and extracting from top, using the rule: first in = first out. It needs to recognize emptiness, as any list, and to initialize, as any object. This abstract object is implemented as dynamic linked variables, or as vector as circular list to avoid apparent fullness, due to shift to the basis. Vectorial circular queue needs a pointer to the basis.
// queue-to.h class Q {unsigned dim; T* top; T* rear; T* qb; public: Q (){top = rear = qb = new T [dim = 100];} Q (unsigned d){top = rear = qb = new T [dim = d];} ~ Q () {delete qb;} bool empty () {return (top == rear);} unsigned full () {return (top == rear+1);} void put(T c){*rear++ = c; if (rear > qb + dim) rear = qb;} T get(){T buf = *top++; if (top > qb + dim) top = qb; return buf;}};

76

/* acr-q.cpp: acrostic */ typedef char T; #include "queue-to.h" #include <iostream.h> void main () {Q q; char s; do{do cin.get(s); while((s==' ')&&(s!='.')); if(s=='.') break; q.put(s); do cin.get(s); while((s!=' ')&&(s!='.'));} while (s != '.'); while (!q.empty()) cout.put(q.get()); cin >> s;}

Exercise: Communication between devices communicating by different speed (vin > vout), assisted by queue. 5.4 Hierarchic programming. Template
Exemplary problem: A list is an ordered set containing n elements therefore each element has an order number. Generally, additional to the position in the list, elements are value comparable, if the type of the elements, or of keys in structured elements, is also an ordered set. List sort means to order the elements corresponding to the order relation defined on the element type (ascending/ descending). 1. Preliminary analysis: The approach universe is the category of ordered sets. 2. Approach: linear. 3. Qualitative model: initial: (a1, a2, ..., an), final: (ai1, ai2, ..., ain), aij aik , ij, ik{1, ..., n} cu ij<ik , (i1, ..., in) = ((1, ..., n)), - permutation on {1, .., n} . 4. Alternative approach:
in fin sortat nesortat (min) in fin sortat nesortat sortare prin selec\ie (elementele sunt parcurse [n ordinea final`) sortare prin inserare (elementele sunt parcurse [n ordinea ini\ial`)

5. Mathematical model: Input/ Output data: list of elements/ permutation that sorts the list, or the sorted list Internal data: indicator for list traversing Program function: determining the permutation that sorts the list, and applying it. 6. Design: Memory space for a single list is allocated, as there exist in-place algorithms that solve the problem. 7. Pseudoprogram: Data (in-out) A = ( a1, a2, ..., an) Mn (aux) i Index = IZ {1, ..., n}, jM Algorithm(selection): read A; for i=1,n execute jminimumPosition (A, i, n), interchange (ai, aj); write A 8. C++ implementation: M = IZ, int in C++ ; other types are defined: lists Mn and indexes IZ{1, ..., n}. In the sorting algorithm appear abstract operations which are not for a C++ talking machine: interchange and minimumPosition List Index2 Index. The abstract operations are implemented in C++ as subprograms of the source program SelSort, or as modules of the object program, previously compiled, that will be edited together with the object program SelSort to form the executable program. 9. Program: void intch(int &p, int &q) {int t=p; p=q; q=t;} // transfer of parameters by reference /*void intch (int*p,int*q) {if(*p>*q){int t=*p;*p=*q;*q=t;}} // transfer by value void sort (int a[], int n) // ~n2/ 2 comparisons + n2 /2 interchanges // recursive bubble-sort, independent of input data, traverses the sorted sublist {if (n>0){for (int i=0; i<n; i++) intch (&a[i], &a[i+1]); sort (a, n-1);}}*/ int minPos (int a[], int i, int n) { int pos = i; for (int j = i+1; j <= n; j++) if (a[j] < a[pos]) pos = j; return (pos);} void sort (int a[], int n) // ~n2/2 comparisons + n interchanges // recursive descending selection sort, independent of input data, traverses the sorted sublist { if (n>0) {intch (a[n], a[minPos(a,0,n)]); sort (a, n-1); }} /*void sort (int a[], int n) // selSort iterative { if (n > 0)for (int i = 0; i < n; i++) intch (a[i], a[minPos(a,i,n)]);} void sort (int a[], int n) // ~n2/2 comparisons + n2/2 assignments (unpropitious case), // iterative insertionSort, medium case costs a half of the most unfortunate linear problem // traverses the unsorted sublist, complexity is linear for the favorable case. { if (n > 0) for (int i = 1; i <= n; i++) {int t = a[i]; int j = i; while ((j>0)&&(a[j-1]>t)) a[j--] = a[j-1]; a[j] = t; }} void ordIns (int a[],int j,int t) {while((j>0)&&(a[j-1]>t)) a[j--]=a[j-1]; a[j]=t;} void sort(int a[],int n) {if(n){sort(a,n-1); ordIns(a,n,a[n]);}}// recursive insertion sort */ #include <iostream.h> const int max_n = 50; void main () { int a[max_n], n; ... sort (a, n); ...

77

10. Formal verification Semantical correctness of the program is equivalent to next sentence: Let: nIN, aMn, M ordered set, e.g., IZ, Isi permutation on {1,.. , n} interchange element ai with aj = min {ah| i h n}, Isi (a) vector obtained by this interchange. Then: Isn-1 ... Is1 (a) is a sorted vector containing as elements. Proof: The conclusion results by induction of next proposition: If for i (1 i n) fixed ak aj k, j, 1 k < j i -1, and ajah j, h, 1 j i-1, i h n, then (Isi (a))k (Isi (a))j k, j , 1 k < j i , and (Isi (a))j (Isi (a))h j, h, 1 j i , i+1h n. But: ak aj k, j, 1k<ji -1 (Isi(a))k Isi(a))j k, j, 1 k<ji -1; ajah j, h, 1j i -1, i h n (Isi (a))k (Isi a))i k, 1ki-1, as m, imn , am = (Isi (a))i; ajah j, h, 1j i -1, i h n (Isi(a))j (Isi(a))h, j, h, 1ji-1, ihn; (Isi (a))i = min {ah| i hn} (Isi (a))i (Isi a))h , h, i+1hn. 11. Complexity analysis Elementary operations are assignments and comparisons, possibly grouped in interchange or order. The operation number of the algorithm for selection sort is independent of the initial order, Gau: (n-1) + (n-2) + ... + 1 = n (n-1) / 2, expression equivalent to n2. Complexity of insertion sort depends on the initial order of the list: it is minimal for an initial ascending sorted vector best case, respectively, maximal for an initial descending sorted vector worst case.

Template Independent concepts are represented independently and combined when necessary. The paradigm that accords priority to this principle is generic programming. Classes are transmitted as parameters; template of C++ instruments this for classes as for functions. Example 13: Conversion of expression infix postfix, evaluation postfix expression Postfixed notation - polish Jan Lukasiewicz permits to expressions not to need parentheses. The presented conversion supposes the infixed form completely assisted by parentheses and separating spaces (exercise: free format), and eliminates recursivity by mean of a stack of characters. The polish form is queued, to be got from the queue for evaluation, eliminating recursivity with a stack of natural numbers.
// st-templ.h template<class T> class STACK { unsigned dim; T* sp; T* sb; public: STACK (unsigned d){sp = sb = new T [dim =d];} ~STACK () {delete sb;} bool empty () {return (sp == sb);} void push (T c){*sp++ = c;} T pop () {if (sp-sb) return *--sp;} }; // que-circ.h class Q {unsigned dim; T* top; T* rear; T* qb; public: Q (){top = rear = qb = new T [dim = 100];} Q (unsigned d){top=rear=qb=new T[dim=d];} ~Q () {delete qb;} bool empty () {return (top == rear);} bool full () {return (top == rear+1);} void put(T c){*rear++ = c; if (rear>qb+dim) rear=qb;} T get(){T buf=*top++; if(top>qb+dim) top=qb; return buf;}}; // evalpost.cpp #include <iostream.h> #include "st-templ.h" typedef char T; #include "que-circ.h" void main () { char c = ' '; Q q; { STACK<char> st(100); // begins the existence domain of the character stack while (c != '.') { cin.get(c); // infix postfix if (c == ')') {c = st.pop(); q.put(c); cout.put(c);} if (c == '+') st.push(c); // exercise: more operations if (c == '*') st.push(c); while ((c >= '0') && (c <= '9')) {q.put(c); cout.put(c); cin.get(c);} if (c != '(') {q.put(' '); cout.put(' ');} } c=st.pop(); q.put(c); cout.put(c); cout<<endl;} //ends the existence domain of the character stack STACK<int> st(100); // postfix evaluation while (!q.empty()) {int x=0; do c = q.get(); while (c == ' '); if (c=='+') x=st.pop()+st.pop(); if (c=='*') x=st.pop()*st.pop(); while ((c >='0')&&(c <='9')) {x = 10*x+(c-'0'); c = q.get();} st.push(x);} cout << st.pop() << endl; cin >> c; }

5.5 Lists. Inheritance A (linear) list can be recursively defined and processed as such, as abstract object of type List[T], for any type T of the elements: list ::= void_list | first_element list Example 14: Object-orientation of the procedural for lists // superficial object-orientation External operations (public, interface of the system) of the list are based on the internal ones (private, internal structure of the system). 78

// LIST-oo.h class LIST { struct ELEM {INF inf; ELEM* nxt;}; typedef ELEM* LNK; LNK af; INF readInf (); void writeInf (INF); LNK newEl (INF); LNK constrList (INF term); void destrList (LNK); void printList (LNK); bool equalList (LNK, LNK) const; // const imposed by the call in equal LNK insList (LNK f, LNK ant, LNK new); LNK insOrd (LNK f, LNK new); LNK addList (LNK f, LNK new); LNK copyList (LNK); LNK extrFirst (LNK); public: LIST() {af=0;} LIST(INF term); LIST(const LIST& another); ~LIST(); bool void() const {return af ? false : true;} void print(); bool equal(const LIST& another) const; void add(INF new); void rest(); INF first() const; void insOrd(INF inf); }; // LIST-oo.cpp internal operations, definition of the declared LIST :: LNK LIST :: newEl (INF inf) { LNK new = new ELEM; new->inf = inf; new->nxt = 0; return new; } LIST :: LNK LIST :: constrList (INF term) {LEG f = newEl(readInf()); if (f->inf != term) f->nxt = constrList(term); return f; } void LIST::destrList(LNK f){if(f){ LNK c=f->nxt; delete f; destrList(c);}} void LIST::printList(LNK f){ LNK c=f; while(c) {writeInf(c->inf); c=c->nxt;}} bool LIST ::equalList (LNK f1, LNK f2) const {if (!f1 && !f2) return true; if (!f1 || !f2 || (f1->inf != f2->inf)) return false; return equalList(f1->nxt, f2->nxt); } LIST::LNK LIST::insList (LNK f, LNK ant, LNK new) {if(!ant){ new->nxt=f; return new;} new->nxt=ant->nxt; ant->nxt=new; return f; } LIST::LNK LIST::insOrd (LNK f, LNK new) {if ((!f)||( new->inf<=f->inf)) { new->nxt=f; return new;} f->nxt=insOrd(f->nxt, new); return f; } LIST::LNK LIST::adList (LNK f, LNK new) {if (!f) return new; LNK c = f; while (c->nxt) c = c->nxt; return insList(f, c, new); } LIST :: LNK LIST :: copiList (LNK f) {if (!f) return 0; LNK f1 = nouEl(f->inf); f1->nxt = copiList(f->nxt); return f1; } LIST :: LNK LIST :: extrFirst (LNK f) {if (f) { LNK c = f; scrInf(f->inf); f = f->nxt; delete c; return f;} return 0;} // external operations, definitions based on the internal ones LIST :: LIST (INF term) {af = constrList(term);} LIST :: LIST (const LIST& another) {af = copiList(another.af);} LIST :: ~LIST () {destrList(af);} void LIST ::print () {printList(af);} bool LIST::egal (const LIST& another) const {return egalList (af, another.af);} void LIST :: add (INF new) {addList (af, newEl(new));} void LIST :: rest () {af = extrFirst (af);} INF LIST :: first () const {return af->inf;} void LIST :: insOrd (INF inf) {af = insOrd(af, newEl(inf)); } // test-oo.cpp #include <iostream.h> typedef char INF; #include "LIST-oo.h" INF LIST :: readInf () {char c; cin.get(c); return INF(c);} void LIST :: writeInf (INF inf) {cout << inf;} LIST concat (LIST l1, LIST l2) { while (!l2.void()) {l1.add(l2.first()); l2.rest();} return l1;} void concatDestr (LIST& l1, LIST& l2) { while (!l2.void()) {l1.add(l2.first()); l2.rest();} }

Representation of unstructured sets Linked list flexibility Characteristic vector accessibility Hash table combination Declarations * *const & [] () C++ pointer/ function pointer prefix constant pointer prefix reference prefix vector postfix function postfix example const T*p T*const p (read right left) *a[] (*a)[] (*f)() *f() 79 observation : pointer to T type constant constant pointer to T type object vector of pointers pointer to vector pointer to function function returning pointer

Inheritance (derived classes) A derived class takes over the behavior or the structure of one or more classes simple or multiple inheritance. Class inheritance represents a basic hierarchy type. Usually, behavior or structure of the inherited class(es) are developed/ modified by the derived class. Constructors call correspondent constructors of the inherited (superior, basic), more abstract class, completing them with construction of the added data members. Access to protected members of the basic class by the derived classes or their friends, happens: directly to static members, or by pointer, reference or object of the derived class or derived of it, or by this of implementation. 1. class Derived: public Basic {..}; defines a subtype of the Basic one, i.e., interface of the subtype contains that of the type protected members of Basic keep their accessibility for Derived, remaining accessible by methods of classes derived of Derived. A pointer to something Derived can be (implicitly) converted to one to something of type Basic. An expression of type Derived can be assigned to a Basic object. A pointer to Basic member can be converted to one pointing to a Derived member relaxed mode to continue the inheritance. 2. class Derived: protected Basic {..}; does not define a subtype of the Basic one, although it contains its behavior/ structure; Basic public and protected members become protected for Derived, therefore accessible for methods of classes derived of Derived; conversion from Derived* to Basic* is possible only from methods of the derived class, or its friends, and from methods of classes derived of Derived, or their friends strong mode to continue the inheritance. Access to protected members of derived classes or their friends is directly possible only for static members; otherwise a pointer, a reference, or a Derived object is necessary; from implementation it is done by this possibly implicit. 3. class Derived: private Basic {..}; does not define a subtype of the Basic one, although it contains its behavior/ structure; Basic public and protected members become private for Derived, therefore inaccessible for Deriveds derived classes; conversion Derived* Basic* can be performed only from Derived methods or its friends mode to interrupt inheritance. virtual abstract classes: pure virtual members, i.e., =0; can not be instantiated; assists hierarchic approach; virtual basic classes: repeated inheritance, i.e., multiple indirectly inherited class different derivations, i.e., collaboration between tree-like inheritance and multiple inheritance, without multiple copies of an instance of a repeatedly inherited class; virtual destructor: deletes by pointer to an instance of the basic class; correctly implicitly destroys outside the existence domain; virtual member-function: specialized in derived classes; the called variant is decided by the pointer type; realized by late linking while execution, not at compilation; results into polymorphic types. Example 15: Hierarchy of o-o structures: stack, queue, list simple inheritance - linear
//enum bool {false, true}; typedef void* DATA; struct ELEM {DATA d; ELEM* next;}; typedef ELEM* LINK; class STACK { protected: LINK sp; public: STACK (){sp = 0;} virtual ~STACK (); bool empty () const {return sp ? false : true;} void push (const DATA); DATA pop (); // or: virtual void push(const DATA)- for polymorphism, e.g., adQ }; void STACK::push (const DATA d) {LINK newElem = new ELEM; newElem->d = d; newElem->next = sp; sp = newElem; } DATA STACK::pop(){LINK tp=sp; DATA td=sp->d; sp=sp->next; delete tp; return td; } STACK::~STACK () { while (sp) {LINK pc = sp; sp = sp->next; delete pc;} } class Q : public STACK { // sp is first protected: LINK last; // can be private if it is not inheritable any more public: Q() { STACK();} // empty is voidQ, pop is extrQ void addQ (const DATA); // otherwise we redefine push }; void Q::addQ (const DATA new_d) {LINK new = new ELEM; new->next=0; new->d=new_d; //! if (!sp) sp = new; else last->next = new; last = new; } class LIST : public Q { public: LIST(){STACK();}// empty - voidList, pop - extrFirst + rest, addQ - addLast LIST* d_copy (LIST& copy) const; // is copied into another list assignment operator // or LIST d_copy (LIST copy) const; copy constructor // not LIST* d_copy(LIST copy) const; returns address of local object void i_copy (const LIST&); // copies another list copy constructor friend void print (const LIST&); };

80

LIST* LIST::d_copy (LIST& copy) const { LINK ps = LINK pc = copy.sp = 0; // cleaning the copy while (ps) {pc = ps; ps = ps->next; delete pc;} LINK ps = sp; while (ps) {copy.addQ(ps->d); ps = ps->next;} return &copy; } void LISTA::i_copy (const LIST& original) { LINK ps = original.sp; while (ps) { addQ (ps->d); ps = ps->next; } } #include <iostream.h> void print (const LIST& st) { LINK pc = st.sp; while (pc) {cout << *(char*)(pc->d); pc = pc->next;} cout << endl; }

Beside different constructors and the destructor, a class of structured objects needs methods to cope with the basic operations: transfer by value and assignment: Copy constructor: CLASS (const CLASS&), transfer by value (inclusive the returned value). Assignment operator: void operator= (const CLASS&), has to clean up the space for assignment. Note: a = b means a.operator=(b), a and b being instances of class CLASS . Example 16: O-o list hierarchy simple tree-like inheritance
// listi-oo.h #include <iostream.h> //enum bool {false, true}; is an implicit type for modern C++ variants struct ELEMENT {DATA d; ELEMENT* n;}; typedef ELEMENT* LNK; class LIST { protected: LNK addrFirst; void constrLIST (LNK); void destrLIST(); LNK addLIST(LNK aF, DATA d); LNK invLIST(LNK); public: LIST (){addrFirst = 0;} virtual ~LIST () {destrLIST();} // copy constructor LIST (const LIST& another) {constrLISTA(another.v_addrFirst());} void operator= (const LIST& another) { // assignment operator destrLIST(); constrLIST(another.v_addrFirst()); } LNK v_addrFirst() const {return addrFirst;} bool void () const {return addrFirst? false: true;} DATA v_first () const {return void ()? DATA ("void"): addrFirst->d; } void rest (){if (void()==true) {cout << "void" <<endl;} else { LNK tp = addrFirst; addrFirst = addrFirst->n; delete tp;} } void insFirst (const DATA); void add (const DATA d) {addrFirst = addLIST (addrFirst, d); } unsigned length () const {LNK cp = addrFirst; unsigned l = 0; while (cp) {cp = cp->n; l++;} return l; } friend void print (const LIST&); }; class LIST_EXT : public LIST {unsigned length; public: LIST_EXT (){LIST(); length = 0;} LIST_EXT (const LIST_EXT& another) { LIST(another); length = 0; } void operator= (const LIST_EXT& another) { destrLIST(); constrLIST(another.v_addrFirst()); } unsigned long () const {return length;} bool elem (const DATA) const; // bool subset (const LIST&) const; // bool subsetRep(const LIST&) const; // bool sublist (const LIST&) const; // list bool subseq (const LIST&) const; // sequence LIST_EXT add (const DATA); DATA extrLast(); DATA selMax () const; DATA extrMax (); LISTA_EXT sortSel (); // LIST x t SortList, maximum selection LNK extrElem (LNK ant); // insOrd Data x SortList SortList, sequential searching LNK insOrd (LNK aF, LNK lL, LNK elem); //(addressFirst, lastLink, element) LNK sortIns (LNK aF); // insOrdBin: Data x ListSort ListSort, binary search LNK insOrdBin (LNK aF, LNK nL, LNK elem); LIST_EXT sortBinIns (); // LIST x t ListSort, sequential/ binary ordered insertion LIST_EXT interclass (const LISTA_EXT&); LIST_EXT sortInterclass (); LIST_EXT inverse(){addrFirst=invLISTA(v_addrFirst());return *this;} LIST_EXT map(DATA f(DATA)) const; // applies function to elements DATA delElem (LNK ant); void delVal (const DATA); // deletes elements of a certain value LIST_EXT filter (bool(*p)(DATA)) const; // deletes elements with p(elem) false }; // listi-oo.cpp typedef unsigned DATA; #include "listi-oo.h"

81

void LIST::constrLIST (LNK another){LNK prev; if (!another) addrFirst=0; else { addrFirst=prev=new ELEMENT; addrFirst->d = another->d; addrFirst->n = 0; another=another->n; while (another){LNK pc = prev->n = new ELEMENT; pc->d = another->d; pc->n = 0; prev = pc; another = another->n; } } } void LIST :: destrLIST () { while (addrFirst) {LNK pc = addrFirst; addrFirst = addrFirst->n; delete pc;} } LNK LIST::addLIST(LNK aF, DATA d){ if(!aF){aF=new ELEMENT; aF->n=0; aF->d=d;} else {LNK pc = aF; while (pc->n) pc = pc->n; pc->n = new ELEMENT; pc = pc->n; pc->n = 0; pc->d = d; } return aF; } LNK LIST::invLIST(LNK aF){return aF?addLIST(invLIST(aF->n), aF->d):aF;} void LIST::insFirst (const DATA new_d) { const LNK pc=addrFirst; addrFirst=new ELEMENT; addrFirst->n=pc; addrFirst->d=new_d; } /*void LIST::add (const DATA new_d) {if (void()) {addrFirst=new ELEMENT; addrFirst->n = 0; addrFirst->d = new_d;} else {LNK pc=addrFirst; while (pc->n) pc = pc->n; pc->n = new ELEMENT; pc = pc->n; pc->n = 0; pc->d = new_d; }}*/ LIST_EXT LIST_EXT :: add (const DATA new_d) { LIST :: add (new_d); length++; return *this; } LIST_EXT LIST_EXT::map (DATA f (DATA)) const {LIST_EXT l(*this); LNK pc = l.addrFirst; while (pc) {pc->d = f(pc->d); pc = pc->n;} return l; } DATA LIST_EXT :: delElem (LNK ant) {DATA d; if (ant) { LNK pt = ant->n; d = pt->d; ant->n = pt->n; delete pt;} else {LNK pt=addrFirst; d=pt->d; addrFirst=pt->n; delete pt;} return d; } void LIST_EXT :: delVal (const DATA d) {LNK pt = addrFirst; LNK pant = 0; while (pt) {while (pt && (d != pt->d)) {pant = pt; pt = pt->n;} if (pt) {pt = pt->n; delElem (pant);} } } LIST_EXT LIST_EXT :: filter (bool (*p) (DATA)) const { LIST_EXT l (*this); LNK pt = l.addrFirst; LNK pant = 0; while (pt) {while (pt && (!p(pt->d))) {pant = pt; pt = pt->n;} if (pt) {pt = pt->n; l.delElem (pant);} } return l; } bool structElem(DATA d, LNK aF) {return aF? (aF->d==d)||structElem(d,aF->n): false;} bool LIST_EXT :: elem (const DATA d) const {return structElem (d, addrFirst);} bool LIST_EXT::subset (const LIST& l) const {LNK pt=l.v_addrFirst(); bool bt=true; while (pt&&bt){bt = elem(pt->d); pt = pt->n;} return bt;} unsigned noRep (DATA d, LNK aF) {return aF? (aF->d==d)+noRep(d, aF->b): 0;} bool LIST_EXT::subsetRep (const LISTA& l) const {LNKpt=l.v_addrFirst(); bool bt=true; while (pt&&bt){ bt = noRep(pt->d, l.v_addrFirst()) <= noRep(pt->d, v_addrFirst()); pt=pt->n;} return bt; } LNK posElem (DATA d, LNK aF) {while (aF && (aF->d!=d)) aF = aF->n; return aF;} bool LIST_EXT :: sublist (const LISTA& l) const { LNK pt = l.v_addrFirst(); LNK pg = v_addrFirst(); LNK pg1 = pg; while (pg && pt) {pg = posElem(pt->d, pg1); pt = pt->n; pg1 = pg->n;} return pg ? true : false; } bool LIST_EXT :: subseq (const LISTA& l) const { LNK pt = l.v_addrFirst(); LNK pg = v_addrFirst();LNK pg1 = pg; while (pg&&pt){pt = l.v_addrFirst(); LNK pg1=pg->n; pg= posElem (pt->d, pg1); while (pg&&pt&&(pg->d==pt->d)) {pt=pt->n; pg=pg->n;} } return !pt ? true : false; } DATA LIST_EXT :: extrLast () {if (void()) return DATA ("void"); DATA pd; length--; if (!addrFirst->n){pd=addrFirst->d; delete addrFirst; addrFirst=0; return pd;} LNK pc=addrFirst; while(pc->n->n)pc=pc->n; pd=pc->n->d; delete pc->n; pc->n=0; return pd; } DATA LIST_EXT :: selMax () const {if (void()) return DATA ("void"); LNK pt = v_addrFirst(); DATA max = pt->d; while (pt) {while (pt && (pt->d <= max)) pt = pt->n; if (pt) {max = pt->d; pt = pt->n;} } return max; } DATA LIST_EXT :: extrMax () {if (void()) return DATA ("void"); LNK pt = v_addrFirst(); DATA max = pt->d; LNK ant = 0; LNK antMax = 0; while (pt) {while (pt && (pt->d <= max)) {ant = pt; pt = pt->n;} if (pt) {max = pt->d; antMax = ant; ant = pt; pt = pt->n;} } delElem (antMax); return max; } LIST_EXT LIST_EXT::sortSel(){LISTA_EXT ls; while(!void())ls=ls.add(extrMax()); return ls; } LNK LIST_EXT::extrElem(LNK ant){if(ant){LNK pt=ant->n;ant->n=pt->n; return pt;} LNK pt = addrFirst; addrFirst = pt->n; return pt; } LNK LIST_EXT :: insOrd (LNK aF, LNK lL, LNK el) { // sequential if ((aF == lL) || (el->d <= aF->d)) {el->n = aF; return el;} aF->n = insOrd(aF->n, lL, el); return aF; } LNK LIST_EXT::sortIns (LNK aF) {if (!aF || !aF->n) return aF; LNK ant=aF; do { LNKp = ant->n; LNK lL = p->n; aF = insOrd(aF, lL, extrElem(ant)); if (p == ant->n) ant = p;} while (ant->n); return aF; }

82

LNK LIST_EXT :: insOrdBin (LNK aF, LNK lL, LNK el) { // binary LNK aM=aF; LNK aS=aF; if(aF==lL) {el->n = lL; return el;} // void list if (aF->n == lL) { return (el->d < aF->d) // one element list ? (el->n=aF, el) : (el->n=aF->n, aF->n=el, aF); } while ((aS!=lL)&&(aS->n!=lL)) {aM= aM->n; aS = aS->n->n;}// middle LIST if (el->d < aM->d) aF = insOrdBin(aF, aM, el); else aM->n = insOrdBin(aM->n, lL, el); return aF;} LIST_EXT LIST_EXT :: sortInsBin () {LNK lL; // last link if (!addrFirst || !addrFirst->n) return *this; LNK ant = addrFirst; do { LNKp=ant->n; lL=p->n; addrFirst=insOrdBin(addrFirst, lL, extrElem(ant)); if (p == ant->n) ant = p;} while (ant->n); return *this; } void lastLink (LNK aF, LNK lL, LNK newlL) { if (aF) {while (aF->n != lL) aF = aF->n; aF->n = newlL; } } LNK intercl (LNK one, LNK another, LNK lL) { if ((one == lL) || (another == lL)) return (one != lL) ? one : another; if (one->d<=another->d) {one->n=intercl(one->n, another, lL); return one; } another->n = intercl(one, another->n, lL); return another; } LIST_EXT LIST_EXT :: interclass (LIST_EXT& another) { addrFirst = intercl(addrFirst, another.v_addrFirst(), 0); return *this; } LNK lastLink(LNK aF, LNK lL, LNK newlL) {if (aF != lL) { LNKp = aF; while (p->n != lL) p = p->n; p->n = newlL;} else aF = newlL; return aF;} LNK sortIntcl(LNK aF, LNK lL){if((aF==lL)||(aF->n==lL)) return aF;// 0/1 elem if (aF->n->n == lL) {if (aF->d > aF->n->d) {LNK aS = aF->n;// 2 elemente aS->n = aF; aF->n = lL; aF = aS;} return aF;} LNK aM = aF->n; LNK aS=aF->n->n; while (aS!=lL) {aM=aM->n; aS=aS->n->n;} aF = sortIntcl(lastLink(aF, aM, lL), lL); aM = sortIntcl(aM, lL); return intercl(aF, aM, lL); } LIST_EXT LIST_EXT :: sortInterclass () {LNK lL = new ELEMENT; lL->n = lL; addrFirst = lastLink(addrFirst, 0, lL); addrFirst = sortIntcl(addrFirst, lL); addrFirst = lastLink(addrFirst, lL, 0); delete lL; return *this; } // listp-oo.cpp typedef unsigned DATA; #include "listi-oo.h" void print_ext (const LIST& l) {LIST lc=l; for (unsigned length = l.length(); length; length--){ cout<<lc.v_first()<<" "; lc.rest();} cout<<endl<<(l.length()? "!nonvoid list!" : "!void list")<<endl;} void print (const LISTA& l) {LNK pc = l.addrFirst; while (pc) { cout << pc->d << " "; pc = pc->n;} cout<<endl; } class LIST_PURE : public LIST { public: LIST_PURE () {LIST();} LIST_PURE (const LIST_PURE& another)constrLIST(another.v_addrFirst());} void operator= (const LIST_PURE& another) { destrLIST(); constrLIST(another.v_addrFirst());} DATA prim() const {return v_first(); } LIST_PURE rest() const { LIST_PURE l(*this); l.LISTA::rest(); return l; } LIST_PURE cons(const DATA d) {insFirst(d); return *this; } void print() const { print_ext(*this); } LIST_PURE concat (LIST_PURE another) { while (!another.void()) { add(another.first()); another.LIST::rest();} return *this;} LIST_PURE insOrd (const DATA d) { if (void()||(d<=first())) return cons(d); return (rest().insOrd(d)).cons(first()); } LIST_PURE sortIns () { LIST_PURE l; while (!void()) { l = l.insOrd(first()); LISTA::rest();} return l; } LIST_PURE interclas (LIST_PURE o, LIST_PURE a) { if (!(o.void() || a.void())) return (o.first() <= a.first()) ? (add(o.first ()), interclas(o.rest(), a)) : (add(a.prim()), interclas(o, a.rest())); return o.voida() ? concat(a) : concat(o); } LIST_PURE sortSel ();LIST_PURE sortInterclas ();// exercises }; DATA double (DATA x){return 2*x;} bool even(DATA x){return bool(!(x % 2)); }

83

5.6. Trees Trees tree ::= voidTree | element {tree} i.e., tree = {element1,.,elementn}, nIN* (elementi (initial element, iIN*, in), mIN trees), forming a partition for the defined tree. A tree is an abstract recursive structure; hence, its elements can represent anything. Structural induction (for trees): a true property for subtrees remains true for the tree, being true for the primitive case void tree, or one node tree property is true for the tree. Possible crossing through of binary trees in descendant representation: on levels: stack/ queue, left/ right for the essential iterative preorder variant, essentially iterative because of the misfit to the descendant representation preorder: binaryTree ::= voidBinaryTree | element binaryTree binaryTree inorder: binaryTree::= voidBinaryTree | binaryTree element binaryTree postorder: binaryTree::= voidBinaryTree | binaryTree binaryTree element Properties of trees: 1. parent is a function parent (n) is the root of the tree that contains the subtree rooted in n. 2. for any 2 nodes ! last common predecessor, in descendant representation. Depth induction: (initial step) for 2 levels the root is common predecessor, last and unique; (nn+1) subtrees depth n; if the nodes belong to the same subtree, then they have a last and unique common predecessor induction hypothesis; else, as the subtrees containing the nodes are disjoint sets, the root is the unique last common predecessor. 3. pair of nodes ! path between concatenation of the paths to the last common predecessor node of a tree can be selected as root. 4. if cardinal (nodes) 0, then cardinal (nodes) = cardinal (edges) + 1 structural induction: for a 1-node tree the property is true; supposing its truth for the m subtrees linked to the root by m edges, adding m to the difference between nodes and edges, and the root to the nodes, the property is conserved for the tree. Binary trees: full all levels are full, possibly excepting the last; complete full, last level occupied left to right. // the roots level = 1 1. On the ith level of a binary tree, there are at most 2i-1 nodes ( induction, exercise). 2. The depth d of a full binary tree with n nodes log2n // efficiency of an algorithm of complexity depth of the binary tree representing the problem to solve: n = nodes/ level for a really full binary tree: n = 20 + 21 + ... + 2d-1 = 2d-1, i.e., d = log2 (n+1) for a full binary tree 2d-1-1 < n 2d-1 log2(n+1) d < log2(n+1)+1, i.e., d = log2 (n+1). Tree representations: a) descendant: links (node) each direct descendant (node list of direct descendants) application: Divide et Impera et Intellige strategy/ approach, neglecting the intersections among the autonomous domains considered for problem decomposing; 1st ex: derivation trees for formal context-free languages operation: pre-/ postorder traversing 2nd ex: sorting/ searching binary trees (binary efficiency); operation: inorder traversing 3rd ex: interclass sorting (efficient also for external data structures); operation: interclass ListSort2 ListSort after a trivial partitioning th 4 ex.: quick sorting (most efficient for medium case); operation: trivial composing after partitioning a List using a reference element, so that it remains in-place while sorting, i.e., anterior elements reference < following elements b) ascendant: links (node) parent: application: partition representation (disjoint classes); operations: class , classes c) on levels: links (node) next/ anterior brother application: complete binary tree as priority queue - heap; operations: efficient sorting d) as binary tree: links (node) first direct descendant, node next brother) application: generalizing methods of the binary tree; operations: preorder preorder binary tree, postorder inorder binary tree. Elimination of recursivity (example suggesting a compiler function) Function/ procedure call is compiled assisted by a stack: - push (values of local variables, address of next instruction) + goto begin function/ procedure, implying at return: - pop() address of next instruction and values of local variables + actualizing local variables+ goto address of next instruction. Example: Iterative preorder processing a binary tree descendantly represented
void l: x: void preOrd(LinkType aR) {// eliminating second call if (!aR) goto x; prel(aR; preOrd(aR->l); aR = aR->r; goto l; ;} preOrd(LinkType aR) {{// eliminating first call

84

l: r: s: x:

if (!aR) goto s; process(aR); stack.push(aR); aR = aR ->st; goto st; aR = aR->dr; goto st; // address of next instuction if (stack.empty()) goto x; aR = stack.pop(); goto r; ;}

Structuration: putting aR->r into stack, line r: is eliminated, and goto r changes to goto l:
void preOrd(LinkType aR) { l: while (aR) {process(aR); stack.push(aR->r); aR=aR->l;} if (stack.empty()) goto x; aR = stack.pop(); goto l; x: ;}

There is another cycle, this time with interior test; adding a push of the initial argument, a while cycle appears:.
void preOrd(LinkType aR) { stack.push(aR); while (!stack.empty()) {aR = stack.pop(); while (aR) {process(aR); stack.push(aR->dr); aR=aR->st; } } }

Eliminating the interior cycle by supplimentary push/ pop:


void preOrd(LinkType aR) {stack.push(aR); while (!stack.empty()){aR=stack.pop(); if(aR){process(aR); stack.push(aR->r); stack.push(aR->l);} } }

If the recursive variant contains also the condition that the tree is processed only if unvoid:
void preOrd(LinkType aR) {stack.push(aR); while (!stack.empty()) {aR = stack.pop(); process(aR); if (aR->r) stack.push(aR->r); if (aR->l) stack.push(aR->l);} }

Priority queue is a type of structured objects associated to an order relation, the is shown when extracting an element (prioritary). Stack and queue are examples of such types, according priority to the last, respectively, to the firs arrived in temporal order. Utility for sorting is evident, but heapSort is also efficient.
class PQ { public: PQ(ElementList); void/PQ insert(Elem); void/PQ deletePrioritaryElem(); void/PQ changePrioritaryElem(); void/PQ modifyElemPriority(Order);void/PQ delete(Elem);void/PQ compose(PQ, PQ);};

Heap is a tree representation of priority queue, so that every element is more prioritary than the elements of its subtrees, and the tree has to be most equilibrated possibly for the efficiency of the operations; for binary trees this means that every element is "greater" than its direct descendants, and the tree has to be complete. Example: Binary tree with different crossings through, of that is derived heap to be sorted:
// tree-heap.h typedef void* INF; INF readInf (); void writeInf(INF); unsigned max2 (unsigned, unsigned); unsigned min2 (unsigned, unsigned); bool greater(INF, INF); bool equalInf(INF, INF); struct NODE{INF inf; NODE* l; NODE* r; NODE* par; NODE* ant;}; typedef NODE* IND; IND newNode(INF); void intch(IND, IND); class TREE {protected: IND aR; IND copyTree(IND, IND); IND insEqTree(IND); void lib(IND);unsigned dep(IND) const; bool bTree(IND) const; INF extr(IND); void preord(IND) const; void inord(IND) const; void postord(IND) const; public: TREE (){aR = 0;} TREE (unsigned nNodes); // constructs balanced tree TREE (const TREE&); virtual ~TREE () {elib(aR);} bool void () const {return aR ? false : true;} void insCompl (); void printInord () const; void printPostord () const; void printBF () const; void printDF () const; // breadth/ depth first void printDpi () const; // preord/ inord stack void printD () const; // preorder right stack virtual IND add (IND); unsigned depth () const; bool equal (const TREE&) const; bool equil () const; TREE operator= (const TREE&); IND vAR() const {return aR;} friend INF extract (TREE&); }; class HEAP: public TREE {IND last; public: HEAP (){TREE(); last = 0;} IND add(IND loc); void sort(); void descendSift (IND); void ascendForm (); void ascendFilter (IND); void descendForm (); }; // st-v-tree.h typedef void* DATA; struct ELEMENT {DATA d; ELEMENT* next;}; typedef ELEMENT* LINK; class STACK { protected: LINK sp; public: STACK (){sp = 0;} virtual ~STACK (); bool empty () const {return !sp;} void push (const DATA); DATA pop (); }; // q-v-tree.h class Q : public STACK { protected: LINK last; // sp - first public: Q () { STACK();} // empty - voidQ void push (const DATA); // - putInQ , pop getOfQ }; // arb-heap.cpp

85

#include #include #include #include IND TREE

<iostream.h> "st-v-tree.h" "q-v-tree.h" "tree-heap.h" :: copyTree(IND alt, IND par) {IND new = 0; if (alt) { new=newNode(alt->inf); new->par=par; new->l=copyTree(alt->l, new); new->r=copyTree(alt->r, new);} return new; } IND TREE :: insEqTree(IND root) {if (!root) root = newNode(readInf()); else if (depth(root->l)<=depth(root->r)) root->l = insEqTree(root->l); else root->r = insEqTree(root->r); return root; } TREE :: TREE (unsigned nNodes) {root=0; for (unsigned i = 1; i <= nNodes; i++) root = insEqTree(root); } IND TREE :: add (IND loc){IND new = newNode(readInf()); new->par = loc; if (!loc) root = new; return new; } TREE :: TREE (const TREE& alt) {root = copyTree(alt.vRoot(), 0); } TREE TREE :: operator= (const TREE& alt) { lib(root); root = copyTree(alt.vRoot(), 0); return *this; } void TREE :: insCompl () { // crossing by levels Q q; IND loc = root; if (!root) root = add(loc); else {q.push (loc); while (!q.empty()) {loc = IND (q.pop()); if (loc->l) q.push(loc->l); else {loc->l=add(loc); break;} if (loc->r) q.push(loc->r); else {loc->r=add(loc); break;}}} } INF TREE :: extr (IND loc) { if (loc) {INF tinf = loc->inf; if (loc == root) root = 0; else if (loc == loc->par->l) loc->par->l = 0; else if (loc == loc->par->r) loc->par->r = 0; else cerr << "Error: extract!" <<endl; lib (loc); return tinf;} return 0; } void TREE::lib(IND root){if(root){lib(root->l);lib(root->r);delete root;} } void TREE :: printBF () const { Q q; IND loc = root; if (void() == false) { q.push(loc); while (q.empty() == false) {loc = IND(q.pop()); writeInf(loc->inf); if (loc->l) q.push(loc->l); if (loc->r) q.push(loc->r);}} } void TREE :: printInord () const {inord (root);} void TREE :: inord (IND root) const { if (root) { inord (root->l); writeInf(root->inf); inord (root->r);} } void TREE :: printPostord () const {postord (root);} void TREE :: postord (IND root) const { if (root) { postord (root->l); postord (root->r); writeInf(root->inf);} } void TREE :: printDF () const {preord (root);} void TREE :: preord (IND root) const { if (root) { writeInf(root->inf); preord (root->l); preord (root->r);} } void TREE :: printDpi () const {STACK st; IND loc=root; bool ready=false; if (!void()) do {while (loc){st.push(loc); // writeInf(loc->inf); => preord loc = loc->l;} if (!st.empty()){loc=IND(st.pop()); writeInf(loc->inf); loc=loc->r;} else ready = true; /* inord */ } while (ready == false); } void TREE::printD () const { STACK st; IND loc = root; if (void()== false) {st.push(loc); while (st.empty() == false) { loc = IND(st.pop()); writeInf(loc->inf); if (loc->l) st.push(loc->l); if (loc->r) st.push(loc->r);}} } unsigned TREE :: depth () const {return depth (root); } unsigned TREE :: depth(IND root) const {if (!root) return 0; return (1 + max2 (depth (root->l), depth (root->r))); } bool equalTree(IND root1, IND root2) {return (!(root1 || root2) || ( equalInf(root1->inf, root2->inf) && equalTree(root1->l, root2->l) &&equalTree(root1->r, root2->r) ) ); } bool TREE::equal(const TREE& alt) const {return equalTree(root,alt.vRoot());} bool TREE :: equilArb(IND root) const { if(!root) return true; unsigned dL = depth(root->l); unsigned dR = depth(root->r); return bool(equilArb(root->l) && equilArb(root->r) && ( (max2(dL, dR) - min2(dL, dR)) <= 1 )); } bool TREE :: equil () const {return equilArb(root);} IND HEAP :: add (IND loc){IND new = newNod(readInf()); new->par = loc; new->ant = last; last = new; if (!loc) root = new; return new; } void HEAP :: descendSift (IND node){IND descMax; if (!node->r) descMax = node->l; else if (!node->l) descMax = node->r; else if (greater(node->r->inf, node->l->inf)) descMax = node->r; else descMax = node->l; if (descMax && greater(descMax->inf, node->inf)) { intch(descMax, node); descendSift(descMax);} }

86

void HEAP::ascendForm(){IND pt=last->par; while (pt) {descendSift(pt);pt=pt->ant;}} void HEAP::ascendFilter(IND nod){if((nod!=root)&&greater(nod->inf,nod->par->inf)) {intch(node->par, nod); ascendFilter (nod->par);} } void HEAP:: descendForm(){Q q; IND loc=root; if (!void()) {q.push(loc); while (!q.empty()) {loc = IND(q.pop());ascendFilter(loc); if (loc->l) q.push(loc->l); if (loc->r) q.push(loc->r);}}} void HEAP :: sort() {IND locLast; while (last) {intch(root,last); locLast =last; last = locLast->ant; writeInf(extr(locLast)); descendSift (root);} } //tree-test.cpp #include <iostream.h> #include "tree-heap.h" INF readInf () {char* pc = new char; cin >> *pc; return pc;} void writeInf (INF inf) { char* pc = (char*) inf; cout << *pc; } unsigned max2 (unsigned i, unsigned j){return (i < j) ? j : i;} unsigned min2 (unsigned i, unsigned j){return (i > j) ? j : i;} INF extract (TREE& tree) {return tree.extr (arb.root->l->r); } bool greater(INF inf1,INF inf2){return(*(char*)inf1>*(char*)inf2)?true:false;} bool equalInf(INF inf1,INF inf2){return(*(char*)inf1==*(char*)inf2)?true:false;} IND newNode(INF inf) {IND new = new NODE; new->inf = inf; new->l = new->r = 0; new->par = new->ant = 0; return new; } void intch(IND addr1, IND addr2) {INF t = addr1->inf; addr1->inf = addr2->inf; addr2->inf = t; } void main () { /*HEAP test;*/ unsigned x; cout << "nNodes: "; cin >> x; cout << "Information: "; TREE test(x); TREE copy(test); cout <<"Depth: "<<test.depth()<<endl <<"Equilibrum: "<<int(test.equil())<<endl <<"Equality: "<<int(test.equal(copy))<<endl; TREE copy = test; writeInf(extract(copy)); cout << "Equilibrum: " << int(copy.equil()) << endl << "Equality: " << int(test.equal(copy))<<endl; test.printDF(); cout<<endl; test.ascendForm(); test.printDF(); cout<<endl; test.sort(); cout<<endl; copy.printDF(); test. descendForm (); test.printDF (); cout << endl; test.sort(); test.printDF(); cout<<endl; test.printBF(); cout << endl; test.printInord (); cout << endl; test.printPostord (); cout << endl; writeInf(extract(test)); cout<<endl; test.printDpi(); cout<<endl; test.printD(); cout<<endl; }

Search/ Sort tree : (l) root < (r) The root value is greater/ less than any node value in the left/ righy subtree. Search O(logn) and sort O(n logn) are efficient by a binary strategy, if the tree is balanced; balance can be operated while or after ordered insertion. Next examples present variations on the theme of 2 related operations: insertion sort and searching in a sorted tree, while inserting the information if not found.
1) //arb1soca.cpp; with class friend // or: inheritance #include <iostream.h> typedef char INF; struct NODE {INF inf; NODE* l; NODE* r;}; typedef NODE* LNK; class ABSC { LNK root; // or protected LNK root; // sort-search public: ABSC(){root=0;} ~ABSC() {lib(root);} // or virtual ~ABSC bool void() const {return !bool(root);} LNK newNode (INF); LNK vRoot() const {return root;} void insOrd (LNK, LNK&); // preorder LNK searchIns (INF, LNK&);/*pre-/postorder*/ void print(LNK); // inorder void lib (LNK);/*postorder*/ friend class TEST_ABSC;// or inheritance }; LNK ABSC::newNode(INF inf){LNK new=new NODE;new->inf=inf;new->l=new->r=0;return new;} void ABSC::insOrd (LNK new, LNK& root) { if (root) if (new->inf <= root->inf) insOrd(new, root->l); else insOrd(new, root->r); else root = new; /* insertion */ } LNK ABSC :: searchIns (INF inf, LNK& root) {if (root) if (inf < root->inf) return searchIns (inf, root->l); else if (inf > root->inf) return searchIns (inf, root->r); else return root;// found else {root=new NODE; root->inf=inf; root->l=root->r=0; return root;}// insertion } void ABSC::print(LNK root){if(root){print(root->l);cout.put(root->inf);print(root->r);} } void ABSC::lib(LNK root) {if(root){lib(root->l);lib(root->r); delete root;} } class TEST_ABSC // or : public ABSC {public: TEST_ABSC(){} /*or {ABSC();*/ ~TEST_ABSC(){} void testIns (); void testSearch(); }; void TEST_ABSC::testIns() { char x = ','; ABSC tree; while (x != '.') { cin.get(x); tree.insOrd(tree.newNode(x), tree.root) ;}// or without tree - inheritance tree.print(tree.root); cout << endl; } void TEST_ABSC::testSearch() {char x=','; ABSC tree; while (x != '.') {// or inheritance cin.get(x); cout.put(tree.searchIns(x,tree.root)->inf);} tree.print(tree.root); cout<<endl; } void main () { TEST_ABSC test; char x; test.testIns(); test.testSearch(); }

87

2) //arb2soca.h; sorting and searching added to class ABSC; ... class ABSC { LNK root; // sort-search public: ABSC (){root = 0;} ~ABSC () {lib(root);} bool void () const {return root ? false : true;} LNK vRoot () const {return root;} LNK newNode (INF); void insOrd (LNK, LNK&);/* preorder */ LNK searchIns (INF, LNK&);// pre-/postorder void print(LNK); /* inorder */ void lib (LNK);// postorder void delNode(); void sortIns (); void sortSearch (); }; // arb2soca.cpp #include "arb2soca.h" LNK ABSC::newNode(INF inf){...} void ABSC::insOrd(LNK new, LNK& root){...} LNK ABSC::searchIns(INF inf, LNK& root){...} void ABSC::print(LNK root){...} void delMin(LNK& root){...} void ABSC::delNode(){...} void ABSC::sortIns(){char x=','; while(x!='.'){ cin.get(x); insOrd(newNode(x),root);} print(root); cout<<endl; } void ABSC :: sortSearch () { char x = ','; while (x != '.') { cin.get(x); cout.put (searchIns(x, root)->inf);} print(root); cout<<endl;} //arb2test.cpp #include "arb2soca.h" void main () {char x; {ABSC test; test.sortIns();} ABSC test; test.sortSearch();} //or without anterior {}and the new declaration with test.lib(); 3) //arb3soca.h; with string as I/O information ... class ABSC { LNK root; // sort-search public: ... INF extre(LNK); // ABSC INF ABSC void sortIns(char* s); void sortCaut(char* s); }; // arb3soca.cpp #include "arb3soca.h" LNK ABSC :: newNode (INF inf) {...} void ABSC :: print(LNK root) {...} void ABSC :: insOrd (LNK new, LNK& root) {...} LNK ABSC :: searchIns (INF inf, LNK& root) {...} //root supposed nonzero, deletes the node with minimal information of the subtree indicated by root, returning minInf INF delMin(LNK root) { INF tinf = root->inf; if (!root->l) if (!root->r) delete root; else {root->inf=root->r->inf; LNK tlnk=root->r; root->l=root->r->l; root->r=root->r->r; delete tlnk;} else tinf = delMin(root->l); return tinf; } INF ABSC::extrNode(LNK root) {INF tinf=root->inf; //root supposed nonzero if (!root->r) if (!root->l) delete root; else {root->inf=root->l->inf; LNK tlnk=root->l; root->l=root->l->l; root->r=root->l->r; delete tlnk; } else root->inf = delMin(root->r); return tinf; } void ABSC::sortIns(char* s){while(*s!='.')insOrd(newNode(*s++),root); print(root);} void ABSC::sortSearch(char* s){while(*s!='.')root=searchIns(*s++,root);print(root);} //arb3test.cpp #include "arb3soca.h" main () { unsigned i = 0; char* s; do cin.get(s[i]); while (s[i++] != '.'); {ABSC test; test.sortIns(s); cout<<endl; cout<<test.extrNode(test.vRoot()); test.print(test.vRoot());} // test.lib (); ABSC test; test.sortSearch(s); cout << endl; return 0; }

Examples exercises: The defined operations can be included in a class AB (+defined those only declared):
struct NODE {INF inf; NODE* l; NODE* r;}; typedef NODE* IND; nNodes AB IN unsigned nNodes(IND root){return root ? 1+nNodes(root->l)+nNodes(root->r) : 0;} nOnLevelAB (IN ) IN IN // call: nOnLev (root, 1, lev) unsigned nOnLev (IND root, unsigned crtLev, unsigned lev) { if (!root) return 0; if (crtLev == lev) return 1; return nOnLev(root->l,crtLev+1,lev)+nOnLev(root->r,crtLev+1,lev);} nLeaves AB IN unsigned nLeaves (IND root) {unsigned nL; if (!root) return 0; nL = nLeaves (root->st) + nLeaves (root->dr); return nL ? nL : 1;} levelNode AB NODE ( IN) IN call: levNode (root, node, 1) unsigned levNode(IND root, IND node, unsigned crtLev) { if (!root) return 0; if (root == node) return crtLev; return levNode (root->l, node, crtLev+1) + levNode (root->r, node, crtLev+1);} degreeAB IN unsigned degree (IND root) {if(!root) return 0; if (root->l&&root->r) return 2; return max2(1, max2(degree (root->l), degree (root->r)); } degenerated AB bool, degenerated (AB) = (degree(AB) = 1) degenToList AB {-1, 0, 1}, -1 // degenerated to left or void tree 0 /* is not degenerated in one direction or void tree */ 1 // degenerated to right or void tree;

88

int degenToList(IND root, int deg) { // call: degenToList (root, 0) if (!root) return deg; if (root->l && root->r) return 0; if (root->r) return degenToList(root->r, 1); return degenToList(root->l, -1); } completeAB bool, fullAB bool bool full(IND root){bool p=true; unsigned crtLev=1; unsigned lev=depth (root); while(p && (crtLev < lev)) { p = (nOnLev(root, crtLev, lev) == power(2, crtLev-1)); crtLev++;} return p;} nrAppear AB INF IN unsigned nAppear(IND root, INF inf) { if (!root) return 0; return equalInf(inf, root->inf) ? 1+nAppear(root->l, inf)+nAppear(root->r, inf) : nAppear(root->l, inf)+nAppear(root->r, inf } window AB AB IND window(IND root) {if (root) { IND t = window (root->l); l = window (root->l); r = t;} return root; } extrFirstLevels AB IN ( IN) AB // call: extrFirstLevels (root, k, 1) IND extrFirstLev(IND root, unsigned maxLev, unsigned lev) { if (!root) return root; if (lev == maxLev) { lib(root->l); lib(root->r); root->l = 0; root->r = 0; } else { root->l = extrFirstLev (root->l, maxLev, lev+1); root->r = extrFirstLev (root->r, maxLev, lev+1); } return root;} equalStruct AB AB bool // (neglects inf) bool equalStruct(IND root1, IND root2) { if (!root1 && !root2) return true; if (!root1 || !root2) return false; return equalStruct(root->st) && equalStruct(root->dr);}

5.7 Graphs A general graphical representation of object sets structured by the relations between objects leads to the concept of graph: G = (V, E), V- vertices ( objects), E - edges/ arcs ( relations) not/ oriented graph. The graph is oriented or not respective to the pair of vertices representing ordered or not ordered relations. A sequence of adjacent edges/ arcs is called chain/ path, and when closed - cycle/ circuit. In a connex graph, any pair of nodes is related by at least one chain/ path. To links, priorities can be associated weights network. A (n oriented) tree can be nonrecursively defined as a connex (not-)oriented cycle-/ circuit-less graph. Operations: crossing, union, intersection, Cartesian product, extract, shortcut. Crossing: visit for some reason of all the vertices that can be attained from an initial one. E.g., a Boolean vector is used to mark the visited vertices (not oriented connex graphs). Representation: a) Adjacency matrix: A V V bool/ Weight (E) Inf (V). // not diagonal diagonal values Elements outside the diagonal reflect the existence of edges/ arcs (possibly also the associated weights), and on the diagonal can be stored information associated to the vertices. The matrix can be vectorially implemented, or, if sparse, i.e., contains few nonzero elements (representing by 0 the lack of edge/ arc between two vertices), by bidirectional linked list.

7 BF: DF: D: 1, 2, 3, 4, 5, 6, 7, 8. 1, 2, 5, 3, 7, 6, 4, 8. 1, 4, 6, 8, 7, 5, 3, 2.

89

b) Adjacency list: lines of the adjacency matrix are implemented using linked lists; representing columns the same way bidirectional-linked list (adjacency multilist). c) Incidence matrix de: I V R {-1, 0, +1} - arc gets out of/ does not intersect/ inputs into the node. Adjacency matrix of the graph can be sparse, e.g., analog structural representation of electronic circuits: | 1 2 3 4 5 6 7 8 1| * 1 1 1 0 0 0 0 2| 1 * 0 0 1 0 0 0 3| 1 0 * 0 1 0 0 0 4| 1 0 0 * 0 1 0 0 5| 0 1 1 0 * 0 1 0 6| 0 0 0 1 0 * 1 1 7| 0 0 0 0 1 1 * 0 8| 0 0 0 0 0 1 0 * Depth-First: recursive: void GRAPH::DF (NODE root) { // NODE visit[0..nNod] contains only false visit[root] == true; process (root); forall (NODE neighbor root.neighbors()) if (!visit[neighbor] DF(neighbor); } D: BF replacing q with stack; analogously, other types of priority queues can be used. Breadth-First: uses a queue (initially empty): void GRAPH::BF (NODE root) { // NODE visit[0..nNod] are only false visit[root] == true; process (root); q.put(root); while (!q.void()) { root = q.get(); forall (NODE neighbor root. neighbors ()) if (!visit[neighbor] { visit[neighbor] == true; process (neighbor); q.put (neighbor); }}} Determining connex components: void GRAPH::connexComp(List& Nodes) {forall (NODE nodeNodes) if (!visit[node]) DF/BF/D(node);} Traveling Salesman Problem (extended example)
#include <iostream.h> #include <stdlib.h> typedef void* Inf; typedef unsigned Diff; struct Imp {Diff effort; unsigned step; unsigned node; Imp* nxt;}; const unsigned maxNod = 17; struct Per {Diff effort; unsigned node;}; class Graph { // complete graph weighted imp/diff non-/oriented - symmetric/nonsymmetric adjacency matrix unsigned dim; Diff minEff; //-Imp impPath <- nodal information unsigned vis[maxNod]; unsigned ch[maxNod]; unsigned dsa[maxNod]; Inf adMat [maxNod][maxNod];// arc weights (path difficulty) - diag (node importance) void delList(Inf first); void intch(unsigned k, unsigned n){unsigned t=ch[k];ch[k]=ch[n];ch[n]=t;} Diff effortPath (); void genPerm (unsigned); Per nxtNode (Per); Diff TSPgreedy(Per unsigned,unsigned,unsigned);//minimum spanning list ->min span tree void insNxt (unsigned, Diff, unsigned, unsigned); unsigned visit (unsigned, unsigned); void path (Diff, unsigned); public: Graph (); // not oriented - symmetric adjacency matrix Graph (unsigned); // oriented (dimension) virtual ~Graph (); // needs delete information void operator= (const Graph&); void write (Diff, unsigned); Diff TSPopt(); Diff minTSPgreedy(); Diff TSPdynprog(unsigned,unsigned,unsigned); Diff TSPbkt(); Dif TSPbab(); Dif TSPdii (); // exercises }; Graph::Graph () {Diff* pDiff; minEff = 0; cout<<"dim<=", maxNod, ":"; cin>>dim; for (unsigned i=1; i<=dim; i++) { vis[i]=dsa[i]=0; ch[i]=i; for (unsigned j=1; j<=dim; j++){if (i<j) { pDiff = new Diff; *pDiff = rand()%(dim); adMat[i][j] = pDif;} else if (i>j) { pDif = new Dif; *pDiff = *(Diff*) adMat[j][i]; adMat[i][j] = pDiff;} else adMat [i][j] = 0;}}} Graph::Graph (unsigned d) {dim = d; Diff* pDiff; minEff = 0; for (unsigned i=1;i<=dim;i++) { vis[i]=dsa[i]=0; ch[i]=i; for (unsigned j=1;j<=dim;j++){ if (i!=j) {pDiff = new Diff; *pDiff =rand()%(dim); adMat[i][j]=pDiff;} else adMat[i][j] = 0; } } }

90

Graph::~Graph (){for (unsigned i=1;i<=dim;i++) delList(adMat[i][i]);} Diff Graph::TSPopt(){minEff=dim*dim; genPerm(dim); write(minEff,1); return minEff;} void Graph::operator= (const Graph& alt) { minEff = 0; dim = alt.dim; for (unsigned i=1;i<=dim;i++) { vis[i] = ch[i] = dsa[i] = 0; for (unsigned j=1;j<=dim;j++) adMat [i][j] == alt.adMat [i][j];}} void Graph::delList(Inf prim){Imp* q; Imp* p=(Imp*)prim; while (p){q=p;p=p->nxt;delete q;} } Dif Graph::effortPath () {Diff effort = *(Diff*)adMat [ch[dim]][ch[1]]; for (unsigned i=1; i<dim; i++) effort += *(Diff*)adMat [ch[i]][ch[i+1]]; return effort ; } void Graph::genPerm (unsigned n) {if (!n) {Diff effort = effortPath (); if (effort < minEff ) {minEff =effort; for (unsigned i=1; i<=dim; i++) vis[i] = ch[i];} } else for (unsigned k=n;k;k--) {intch(k,n); genPerm(n-1); intch(n,k);}} Per Graph::nxtNode (Per per) {Diff effort, e; unsigned nghb = 1; while ((nghb<dim)&&vis[nghb]) nghb++; if (!vis[nghb]) effort =*(Diff*)adMat [per.node][nghb]; for (unsigned v = nghb+1; v<=dim; v++) if (!vis[v]) { e = *(Diff*)adMat [per.node][v]; if (e<effort ) {nghb=v; effort =e;}} per.effort += effort ; per.node = nghb; return per; } Diff Graph::TSPgreedy (Per per, unsigned in, unsigned step) { unsigned crt=per.node; ch[step]=crt; if (step<dim) { vis[crt]=step; per=nxtNode(per); per.effort =TSPgreedy(per,in,++step);} else {per.effort += *(Diff*)adMat [crt][in]; for (unsigned i=1;i<=dim;i++) vis[i]=ch[i]; write(per.effort ,0);} return per.effort ; } Diff Graph::minTSPgreedy (){Per per; per.effort = 0; per.node = 1; minEff = TSPgreedy(per,1,1); for (unsigned in=2; in<=dim; in++) { for (unsigned i=1;i<=dim;i++) vis[i]=0; per.node=in; unsigned eff = TSPgreedy(per,in,1); if (eff<minEff ) minEff =eff;} return minEff; } void Graph::write (Diff effort, unsigned all){ if (all) { for (unsigned i=1;i<=dim;i++) {for (unsigned j=1;j<=dim;j++) if (i!=j) cout<<*(Diff*)adMat [i][j]<<' '; else cout<<'0'<<' '; cout<<endl;}}cout<<"Path "<<(all?"optimal: ":"minimal: "); for (unsigned i=1;i<=dim;i++) cout<<vis[i]<<' '; cout<<vis[1]<<" Effort:"<<effort <<endl; } void Graph::insNxt (unsigned crtNode, Diff eff, unsigned step, unsigned nxtNode) { Imp* pImp = (Imp*)adMat [crtNode][crtNode]; while (pImp&&pImp->nxt) pImp=pImp->nxt; Imp*p=new Imp; p->effort =eff; p->step=step+1; p->node=nxtNode; p->nxt=0; if (pImp) pImp->nxt = p; else adMat [crtNode][crtNode] = p; } unsigned Graph::visit (unsigned node, unsigned step){ unsigned v=0; unsigned i=1; while(!v&&(i<step))v=(vis[i++]==node); return v;} void Graph::path (Diff eff, unsigned in) {Imp* pImp = (Imp*)adMat [in][in]; while (pImp->nxt) pImp = pImp->nxt; unsigned step=2; unsigned crtNode = vis[step] = pImp->node; Diff effc = eff - (*(unsigned*)adMat [in][crtNode]); for (step=3;step<=dim;step++){pImp = (Imp*)adMat [crtNode][crtNode]; if(pImp) while (pImp->nxt&& ((pImp->step!=step)||(pImp->effort !=effc)|| visit(pImp->node,step))) pImp = pImp->nxt; // Imp* p = pImp; delete p; vis[step] = pImp->node; effc -= (*(Diff*)adMat [crtNode][pImp->node]); crtNode=pImp->node; if (step==dim) efc -= (*(Diff*)adMat [crtNode][in]);} if (efc) cout<< " incorrect returned path! "; } Diff Graph::TSPdynprog (unsigned in, unsigned crt,unsigned n){Diff ef; if (n==dim) {ef = *(Diff*)adMat [crt][in]; return ef;} ef=dim*dim; ch[crt]=n; dsa[n]=crt; vis[1]=in; for (unsigned k=1;k<=dim;k++) if (!ch[k]) { Diff effort = *(Diff*)adMat [crt][k] + TSPdynprog (in,k,n+1); if (effort < ef) { ef = effort ; insNxt(crt,ef,n,k);} for (unsigned i=n+1;i<=dim;i++) ch[dsa[i]]=0;} if (n==1) path(ef,in); return ef; } void main() {unsigned d; do {cout<<"dim<=17:"; cin>>d; Graph g(d);/*Graph g;*/ Graph h = g; g.TSPopt(); g=h; cout<<"minimal greedy path:" <<g.minTSPgreedy()<<endl; for(unsigned i=1;i<=d;i++){g=h;g.write(g.TSPprd(i,i,1),0);}}while(d-1); }

91

A* algorithm (extended example): search (estimation function h, cost function g) Paths graph.A* (Nodes S; Estimations h) LIST OPEN, CLOSED, candidates; typedef Nodes[Nodes] Paths; Paths PRED; Nodes x, y, z; typedef double[Nodes] Functions; Functions F, G; boolean found:=false; OPEN:={S}; CLOSED:={}; G[S]:=0; PRED[S]:=nil; while (OPEN <> {}) and not found do candidates := OPEN.F_min; x := candidates.extr_if _possible_goal; OPEN.del(x); CLOSED.ins(x); if x.goal then found:= true else foreach y in x.succesors do if not y in OPEN+CLOSED then G[y] := G[x] + x.dist(y); F[y] := G[y] + h(y); PRED[y] := x; OPEN.ins(y) else z:=PRED[y]; temp:=F[y]-(G[z]+z.dist(y))+(G[x]+x.dist(y)); if temp < F[y] then G[y] := G[y] - F[y] + temp; F[y] := temp; PRED[y] := x; if y in CLOSED then OPEN.ins(y); CLOSED.del(y) fi fi fi fi od; if found then return(PRED.inv) else return(nil) fi end. Power of recurrence
unsigned sr(unsigned k, unsigned n) {unsigned sum=0; if (n) for (unsigned i=1; i<=k; i++) sum += sr(i,n-1); else sum++; return sum;} unsigned s (unsigned k,unsigned n) {unsigned sum=0; for (unsigned i1=1; i1<=k; i1++) for (unsigned i2=i1; i2<=k; i2++) for (unsigned i3=i2; i3<=k; i3++) for (unsigned i4=i3; i4<=k; i4++) sum++; return sum;} #include <iostream.h> void main(){unsigned k=1;unsigned n; while(k){cin>>k>>n; cout<<sr(k,n)<<', '<<s(k,n) <<endl;} } Gcd (Euclid, Nichomachus) int gcd_r(int m, int n){if (!n) return(m); return(gcd_r(n,m%n));} int gcd_i(int m, int n){int r;while(n){r=m%n; m=n; n=r;}return m;} int gcd_d(int m, int n) {if (m==n) return(m); return(m > n ? gcd_d(m-n,n):gcd_d(m,n-m));} Complex numbers enum Bool {false, true}; class C { double r, i; public: C (double a = 0, double b = 0) {r = a; i = b;} C (const C&alt){r=alt.r; i=alt.i;} void operator= (const C&alt){r=alt.r;i=alt.i;} virtual ~C () {} //delete &r; delete &i; double re () const {return r;} double im () const {return i;} Bool zero () const {return r || i ? false : true;} Bool operator==(const C alt)const{return(r==alt.r)&&(i==alt.i)?true:false;} Bool operator!=(const C alt)const{if(*this==alt)return false; return true;} C plus (C alt) const {alt.r += r; alt.i += i; return alt;} C operator+ (C alt) const {alt.r += r; alt.i += i; return alt;} /*friend*/ C operator* (const C&); void operator*= (const C&); void read(); void print()const; }; #include <iostream.h> inline C C::operator* (const C& alt) {cout<<'a'<<endl; return C (r*alt.r - i*alt.i, r*alt.i + i*alt.r);} inline void C::operator*= (const C& alt) { *this = C(r*alt.r-i*alt.i, r*alt.i+i*alt.r); } C operator* (const C& a, const C& b) {cout<<'b'<<endl; return C(a.re()*b.re()-a.im()*b.im(), a.re()*b.im()+a.im()*b.re());} void C::read(){cin>>r>>i;} void C::print()const{cout<<r <<','<<i<<endl;} void print(const C z) {cout << z.re() << ',' << z.im() << endl;} void main() {C s,z; print(s); C p(1,1); print(p); C altp(1,1); do{z.read(); s=s+z; s.print(); p=p*z; p.print(); altp*=z; altp.print();} while (z!=C(0));} Square root // generalization: nIN - root double eps; double x=1; double abs(double x){return (x>=0)? x: -x;} double radrec (double xn) {double xn1=xn; xn=(xn1+x/xn1)/2; if (abs(xn - xn1)<eps) return xn; return radrec(xn);} double rad() {double xn; double xn1=x; do {xn=xn1; xn1=(xn+x/xn)/2;} while (abs(xn1-xn)>eps); return xn;} or double abs(double); double abs (double x) {return (x >= 0) ? x : -x;} double radrec (double x, double eps, double xn) { double xn1 = xn; xn = (xn1 + x/xn1)/2; if (abs(xn - xn1)< eps) return xn; return radrec(x, eps, xn);} double rad (double x, double eps) {double xn; double xn1=1; do { xn=xn1; xn1=(xn+x/xn)/2;} while (abs(xn1-xn)>eps); return xn; }

92

5.8 Summary C++ Primary data types ::= void| bool| char| unsigned| int| float| double| enum TypeName {elemName, .., elemName}; /* if not explicitly initialized, elements values are the first natural numbers */ Constructed data types: Pointer type ::= Type* // pointers Vector type ::= Type[] // equivalent to Type*, except parameter transmission Reference type ::= Type& // alternative names Data structure ::= struct TypeName {Type componentName; ..}; Class ::= class TypeName:access ClassNameopt{access: Type elementName; .. }; access ::= private | protected | public elementName ::= componentName | methodName :access ClassNameopt //derivation from another class (can be generic) Generic class::= template<class Type> class TypeName:access ClassNameopt{ access: Type elementName; ..}; Declarations: Before being used in the program, an identifier has to be declared (defining its type); its validity domain extends to the end of the block {}, if declaration is a function, a class, or a name domain local object; else a global object results, which validity domain extends to the end of the file containing the declaration; to be useful at execution, it needs to be defined, i.e., put in correspondence with an object.; declaration ::= specifyieropt Type declarator initializatoropt specifyier::= typedef virtual static extern friend inline register declarator ::= name operator-declarationopt operator-declaration: * & [] () *const pointer reference vector function constant _pointer unary operator: prefixed ( * & *const ) or postfixed links tighter - ( [] ()) Expressions: result of subexpressions by operating with operators: resolution of the validity domain :: (access global domain or class domain) element selection . -> //of object, respectively, with pointer to object function domain, type conversion () object size sizeof type size sizeof () index [] address & unreference * generation new destruction delete assignment = can be combined to bit-logical or arithmetical operations op= relational == != < > <= >= logical ! && || bit-logical ~ << >> & | ^ arithmetical + - * / % incrementation ++ decrementation -- (pre-/ postfix) conditional bool-expr? expr : expr sequencing , Instructions: declaration { instruction_listopt} expressionopt ; if (condition) instruction if (condition) instruction else instruction switch (condition) instruction case constant_expression: instruction default: instruction break; continue; while (condition) instruction do instruction while (condition); for(initialization _ expression_for condition opt ; expressionopt) instruction condition::= expression | Type declarator = expression /*commentary*/ // line commentary instruction_list::= instruction | instruction instruction_listopt Computer architecture Implementation of the functions that contribute to computer realization (with 4 operations and variables) occasions to exemplify the main constructions in C+, helping to: understand the programs functioning; understand the structuring in autonomous significant functions, by decomposing into cpp files interconnected header files; at compilation their are include into a project; understand the simplified functions of compilers: lexical analyzer combined here with input processor, parser (syntactical analyzer), symbol table (here, hash table), error management (here it also constructs the final result of the program - main function); 93

understand the management of the heterogeneous sets that lack complete previous information, by hash tables that combine in this example the vectorial to the linked representation; add arithmetical, trigonometrical, algebraical, analytical operations; modify the representation of the symbol table; object-orient the program.

// calcmain.cpp - line commentary #include <iostream.h> // standard header file containing I/O classes #include "error.h" // header file for the current project #include "lex.h" // header files corresponding to projects modules #include "syn.h" #include "table.h" #include <string.h> // standard header file for sign sequences int main(){// " main program" manages the activity of the program insert ("pi")->value = 3.1415926535897932385; // assignment to indicated variables insert ("e")->value = 2.7182818284590452354; while (1) { get_token(); // appearantly infinite cycle, hence with output on the route if (crt_tok == END) break; // conditioned output of the cycle if (crt_tok == PRINT) continue; // jump to next iteration cout << expr() << endl; // output to object with standard operator } // output from the domain indicated by curly brackets, where local/ global data where processed return no_of_errors; // subprograms are functionally noted; procedures have void codomain; } main returns a value that can be further utilized in the system // lex.h enum token_value {NAME, NUMBER, END = '.', LP = '(', RP = ')', PLUS = '+', MINUS = '-', MUL = '*', DIV = '/', PRINT = ';', ASSIGN = '='}; // enumeration type extern token_value crt_tok; // variable declaration; a declaration can be multiple extern double number_value; // extern char name_string[256]; // a definition has to be unique in the domain token_value get_token (); // function declaration // lex.cpp #include <iostream.h> #include <ctype.h> // standard header for character operations #include "error.h" #include "lex.h" token_value crt_tok; double number_value; // double precision real number; otherwise, float char name_string[256]; // vector with 257 characters including name_string[0]) do {if (!cin.get(ch)) return crt_tok = END; // cycle with finally test } while (ch != '\n' && isspace(ch)); // character for end of line is abbreviated endl switch (ch) { // conditioned instruction case ';': case endl: return crt_tok = PRINT; case '*': case '/': case '+': case '-': case '(': case ')': case '=': return crt_tok = token_value(ch); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': cin.putback(ch); cin >> number_value; return crt_tok = NUMBER; default: if (isalpha(ch)) {char* p = name_string; *p++ = ch; while (cin.get(ch) && isalnum(ch)) *p++ = ch; cin.putback(ch); *p = 0; return crt_tok = NAME;} error("bad token"); return crt_tok=PRINT; }} // syn.h double expr (); double term (); double prim (); // syn.cpp #include "error.h" #include "lex.h" #include "syn.h" #include "table.h" double prim (){ switch (crt_tok) { case NUMBER: get_token(); return number_value; case NAME: if (get_token()==ASSIGN) {name* n = insert (name_string); get_token (); n->value = expr (); return n->value;} return look (name_string)->value; case MINUS: get_token(); return -prim(); case LP: {get_token(); double e=expr(); if (crt_tok!=RP) return error(")expected"); get_token(); return e;} case END: return 1; default: return error ("primary expected"); } } double term (){double left=prim (); for (;;)// cycle ended by return switch (crt_tok) {case MUL: get_token (); left *= prim (); break; case DIV: {get_token(); double d=prim(); if(!d)return error("divide by 0"); left /= d; break;} default: return left;} } double expr () { double left = term ();

94

for (;;) switch (crt_tok) {case PLUS: get_token (); left += term (); break; case MINUS: get_token (); left -= term(); break; default: return left;}} // table.h struct name {char* string; name* next; double value; }; name* look (const char* p, int ins = 0); inline name* insert (const char* s) {return look(s,1);} // table.cpp #include "error.h" #include <string.h> #include "table.h" const TBLSZ = 23; name* table[TBLSZ]; name* look (const char* p, int ins){int ii=0; const char* pp = p; // pointer to constant character, initialized while (*pp) ii = ii << 1 ^ *pp++; // cycle with initial test - string is 0 on the last position if (ii < 0) ii = -ii; ii %= TBLSZ; for (name* n = table[ii]; n; n = n->next) if (strcmp(p, n->string) == 0) return n; if (ins==0) error ("name not found"); name* nn = new name; // follows generation of object type name + string copy nn->string = new char[strlen(p)+1]; strcpy(nn->string,p); nn->value = 1; nn->next = table[ii]; table[ii] = nn; return nn; } // error.h extern int no_of_errors; // integer; naturals are unsigned int double error (const char* s); // parameter is pointer to constant character // error.cpp #include <iostream.h> #include "error.h" int no_of_errors; double error(const char* s){cerr<<"error: "<<s<<endl; no_of_errors++; return 1;}

5.9 Structuring modes Usual programming languages offer as structuring modes of complex objects from simpler ones: parallelism (Cartesian product), alternative (disjoint union), and hierarchy (passing to the set of parts/ binary functions defined on a set). Thus can be defined application-adequate types, starting from the basic types existent in the language. Standard structuring modes can be formalized by stronger schemes (separation, dependence, induction, comprehension), being both mathematically interesting, as implementable in languages for intelligent simulation. Structuring principles (enumeration, induction/recursion, abstraction) guide the modes. Standard structuring modes Function spaces (general, total, partial) General functions GFS: f(S T) x (xS fxT) // associative to right Operations defined for the basic types belong to spaces of general functions. Theorem: a) S any type: cond Bool S S S b) Boolean operations can be implemented by conditional: andBool Bool Bool, and xy = cond x (cond ytf) f; not Bool Bool, not x = cond xft; or Bool Bool Bool, or xy = cond xt (cond ytf) Extensional equality f T g : xT (fx=gx) Composition T, S, R any types; fTS, gSR, g o f = x.g (fx) Theorem: T, S, R, U - any types a) I T T b) If f T S, g S R then g o f T R c) If f T S, g S R, h R U then (h o g) o f = h o (g o f) d) IF f T S then f o I T f, I o f T f Total functions TFS: f(S =t>T) xS ((fx) fxT) If xS then f(S =t>T) f (axiom L2), therefore can not belong to any space of total functions. Partial functions PFS: f(S =p>T) xS ((fx) fxT) // x((x)) (L2, L5, L6); therefore is a member of any space of partial functions. Cartesian product New constants related to pairs (and n-tuples) are introduced in the language: Constructors: (*,*) (pair), l (left component), r (right component) Axioms: l (x,y) = x; r (x,y) = y; (x,y) ; (l ) (r ) The standard mode to represent functions of more arguments is to select arguments sequentially; e.g., x.(y.fxy); an alternative scheme is offered by the pair-constructor: (Haskell)Curry C1to2 = fxy. f (x,y) - transforms a 1-argument function into a 2-arguments one; functions C2to1 = fz. f (lz) (rz) - transforms a 2-arguments function into a 1-argument one. Pair abstraction (x,y).t = C2to1(xy.t). Extending the idea of pair n-tuples, n>0, are obtained. compni (component i) n-tuples Constructors: tuplen , Axioms: compni (tuplen x1... xn) = xi , 1 i n (tuplen x1... xn) ; (compni ) 95

Curry functions: C1ton = f x1... xn. f (tuplen x1... xn), Cnto1 = f z. f (compn1 z) ... (compnn z) Notation: tuplen x1... xn = (x1, ... , xn) n-tuple abstraction (tuplen x1... xn).t = Cnto1 ( x1... xn.t) Cartesian product CP: z ST (z = (lz, rz) lzS rzT) ) n-dimensional Cartesian product can be defined by n-tuple or by repeated binary Cartesian product; isomorphic types result: CPn: zS1...Sn (z = tuplen (compn1z) ... (compnnz) (compn1z)S1.. compnnz)Sn) If S1 = .. = Sn notation: Sn. or z S1 (S2 (...(Sn-1Sn) ..)) Isomorphic types S, T isomorphic types if fST f 'TS (f o f 'TIfof 'SI Theorem: a) S T U isomorphic to S (T U) b) S T U isomorphic to S (T U) . Considering passing to general functions space and Cartesian product fundamental structuring modes (disjunctive union is reducible to cartesian product), the quotient class of types relative to isomorphism admits as operations these structuring modes: passing to functions has a multiplicative behavior, noncommutative, as nonassociative, while Cartesian product behaviors additively, commutatively, and associatively. Theorem (distributivity to ) S, T, R-types. (RS) (RT) isomorphic to R(ST). Disjunctive union (implementation): xST - (xBoolST ((x = (t,s,) s) (x = (f, , d) d ))) Structuring schemes A mathematical constructive type theory formalizes the structuring principles: enumeration, induction, abstraction, into structuring schemes defining a hierarchy of de concrete types, autonomous hierarchies of abstract types; mathematical structuring schemes generalize the structuring modes of programming. Separation (subtypes) S, T - types Inclusion: ST x (xS xT); preorder relation; structuring modes are monotonous, except the domain of the function space that is antimonotonous, relative to inclusion; more: T=t>S TS T=p>S. Extensional equivalence: S T (T S S T); equivalence relation with compatible structuring modes. Separation of strong decidability: T S is strongly decidable relative to S: fS=t>Bool (xS(fx=txT)) Relative strong decidability is transmitted to cartesian product, therefore to , yet not to relations on functions. The general scheme to obtain subtypes is separation of elements by a specific difference, represented as wwf. {xT: } // [x] means x is free variable in //: is often replaced by | Separation bounds the variables, analogous to quantifying or abstraction . Separation axiom SP: z{xT: } (zT [z /x]) Theorem: a) {xT: }T b) (ST ()) ({xS : }{xT : }) c) (ST ()) ({xS : }{xT : }) (Domain, image): For fS =p> T Dom (f) = {xS: fx} Im (f) = {yT : xS (fx fx = y)} Theorem: a) (ST) {fS=p>T : xS(fxT)} b) (S =t> T) {f S T : xS(fx)} Only the way to form the space of partial functions needs to be axiomatic. Dependence (parametric types) Families of types parameterized by a free variable are defined (, bound). Generally, T[x], i.e., x is free variable in T. For example: Dependent product: DP: f ( x S.T) x (xS fxT) Function space S T is a particular case when x is not free in T. Examples: 1) Dynamic arrays over type T: IN[n]={yIN: y<n}; A[n]=(IN[n] T) xIN.A [x] type of dynamic arrays. 2) left function, e.g., quickSort, from a number list l and a number nl, returns a sublist of the numbers n: left ( x {yList[IN]| y [ ]}.({zIN| member zx = t} List [IN]) ). Dependent sum: DS: z ( x S.T) (z = (lz, rz) lzS rz(T [lz/x])) Cartesian product ST represents the particular case when x is not free in T. E.g.: variant records: z( xBool. (cond x T1T2)); lists with privileged element: z( xList[IN].(IN [len x])). Structural inductive types. Extensions A structural inductive type SI [T,C] is generated by: 1. Two families: T finite collection of types; C finite collection of distinct constructors with associated signatures arities m 0, and types of their arguments T or SI [T,C]); basic elements correspond to constructors having only arguments in T. 2. Closure axiom: CLO: [c1] . . . [cn]; closure condition [ci] corresponds to constructor ci in C: x1T1 . . . xmTm ((ci x1 . . . xm) SI [T,C]), Tk is T or SI [T,C] 3. Structural induction principle: IND: (' [c1] .. ' [cn]) ySI [T,C] ([y]); conserving condition ' [ci] associated to constructor ci of C, the form is: (x1T1..xmTm) ([y1] .. [yr] [cix1 . . . xm]), y1,.., yr xj for that T j = SI [T,C]. The common scheme to define inductive types can be utilized in a programming language to define types, e.g., enumerated types, particular inductive types, cartesian products, disjunctive unions, however not function spaces, abstraction being a more general principle/ procedure than induction. 96

Example for cartesian product: S, T - types CP [T,S]:

Theorem: CP [T, S] T S . Dem:[zTS] (closure) z = (lz, rz)CP [T,S] (i) CP [T,S]TS [zCP[T,S]] (induction) zTS (i) TSCP [T,S] [x]= xTS: [uT, vS] (u, v)TS # Generalization of strong decidability transmition theorems for equality of component types to structured types: Theorem: Ti subtype of T equality strong decidable SI [T,C] equality strong decidable; witness function on SI [T,C]: f (ci x1...xk) (cj u1...uk) = (ci = cj and f1x1u1 and...and fk xk uk and f y1 v1 and...and f yr vr), fi is witness function for type Ti, y1,..,yr are those xi, and v1,..,vr those vi, for that Ti = SI [T,C]. Dem: (induction) x, ySI [T,C] (x = y f xy = t) [x] = (ySI [T,C] (f xy = t x = y) (subinduction) [y] = (f xy = t x = y) # // avoids the need to strengthen the hypotheses This theorem exemplifies the proof of recursive function properties defined for structural inductive types. Particular cases of structural induction reveal that often the induction hypotheses have to be strengthened. Well-founded induction A generalization of the structural inductive types bases on well-order relations RTT: IND is extended to IND-B: xT (y ((x, y)R [y]) [x]) xT ([x]) // the proof of a propertys validity on T reduces to showing it is transmitted to an element from predecessors. Comprehension (comprehensive types) More general separation of types eliminates the restriction of belonging to a certain type implicit call to the idea of universal type: x: (bounding operation); generally, [x]. Axiom that governs type definition is: COMP: z{x : } [z /x]. Comprehensive types are hierarchically constructed, bottom-up from the basic types, using in abstraction and structuring applied to already defined types. Separation needs no axiom, as: {xS: } = {x: xS } Universally type : = {x : x = x} // all by terms expressible objects, and all types are its subtypes Theory of concrete types is defined by axiom COMP, as all other structuring modes are definable by comprehension: S T = {z: z = (lz, rz) lzS rzT} S =p>T = {f: x (xS (fx) fxT} x S.T = {f: x(xS fxT)} x S.T= {z: z = (lz, rz) lzS rz(T [lz/x])} SI [T, C] = {z: ( [c1]. . . [cn] ( [c1]. . . [cn] y SI [T,C] ( [y]))} Theory is consistent being much weaker than standard set theory (ZF), since it is typed, i.e., terms and types are represented by distinct syntactical categories; its expressivity is greater than concrete programming needs. Useful types are completely described, i.e., do not imply in definition disjunctions or existential quantification. Enumerated types can be defined inductively, e.g., Bool: CL-Bool: t Bool f Bool IND-Bool: [t] [f] xBool ([x]) Negative wff does not contain or . (, - free). Class of negative wwf is nwwf. Axiom: NCOMP: z {x: } [z /x], unde nfbf is sufficient to cover the programming structures, therefore important in developing programs as collateral effect of constructive proof of correctness for specifications.

/CLO: uT vS ((u,v)CP [T,S]) / IND: uT vS ([(u, v)]) xCP [T,S] ([x])

97

Anda mungkin juga menyukai