Professional Documents
Culture Documents
Abhiram G. Ranade
Abhiram G. Ranade
Do not distribute
Contents
Prefa e
0.1
0.2
0.3
0.4
Graphi
s . . . . . . . . . . . . . . .
First day/rst month blues . . . . .
Obje
t oriented programming . . .
Fitting the book into a
urri
ulum
.
.
.
.
1 Introdu tion
1.1
1.2
1.3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1.4
1.5
1.6
1.7
1.8
1.9
A simple program . . . . . . . . . . .
1.1.1 Exe
uting the program . . . .
Remarks . . . . . . . . . . . . . . . .
1.2.1 Exe
ution order . . . . . . . .
Repeating a blo
k of
ommands . . .
1.3.1 Drawing any regular polygon
1.3.2 Repeat within a repeat . . . .
Some useful turtle
ommands . . . .
Numeri
al fun
tions . . . . . . . . . .
Comments . . . . . . . . . . . . . . .
Computation without graphi
s . . . .
Con
luding Remarks . . . . . . . . .
1.8.1 Graphi
s . . . . . . . . . . . .
1.8.2 A note regarding the exer
ises
Exer
ises . . . . . . . . . . . . . . . .
.
.
.
.
2.1
2.2
2.3
14
14
15
17
17
18
19
20
21
21
22
22
24
24
24
26
27
27
28
29
29
31
31
33
34
35
35
36
36
38
38
38
39
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
40
40
40
41
41
41
42
44
44
44
45
46
46
49
49
50
51
52
52
53
54
55
56
57
57
57
58
59
60
62
62
63
63
64
64
65
65
67
67
71
4.3
4.4
4.5
4.6
4.2.1 Testing . . . . . . . . . . . . . . .
4.2.2 Corre
tness Proof . . . . . . . . .
4.2.3 Invariants . . . . . . . . . . . . .
Debugging . . . . . . . . . . . . . . . . .
Comments in the
ode . . . . . . . . . .
Con
luding remarks . . . . . . . . . . . .
4.5.1 A note on programming exer
ises
Exer
ises . . . . . . . . . . . . . . . . . .
5 Simple pp graphi s
5.1
5.2
5.3
5.4
5.5
5.6
5.7
5.8
5.9
Overview . . . . . . . . . . . . . .
5.1.1 y axis goes downward! . .
Multiple Turtles . . . . . . . . . .
Other shapes besides turtles . . .
5.3.1 Cir
les . . . . . . . . . . .
5.3.2 Re
tangles . . . . . . . . .
5.3.3 Lines . . . . . . . . . . . .
5.3.4 Text . . . . . . . . . . . .
Commands allowed on shapes . .
5.4.1 Rotation in radians . . . .
5.4.2 Tra
king a shape . . . . .
5.4.3 Imprinting on the
anvas .
5.4.4 Resetting a shape . . . . .
Cli
king on the
anvas . . . . . .
Proje
tile Motion . . . . . . . . .
Best t straight line . . . . . . . .
Con
luding Remarks . . . . . . .
Exer
ises . . . . . . . . . . . . . .
6.1
6.2
6.3
6.4
6.5
6.6
6.7
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
The If statement . . . . . . . . . . . . . . . . .
Blo
ks . . . . . . . . . . . . . . . . . . . . . . .
Other forms of the if statement . . . . . . . . .
A dierent turtle
ontroller . . . . . . . . . . . .
6.4.1 \Buttons" on the
anvas . . . . . . . . .
The swit
h statement . . . . . . . . . . . . . .
Conditional Expressions . . . . . . . . . . . . .
Logi
al Data . . . . . . . . . . . . . . . . . . . .
6.7.1 Reasoning about logi
al data . . . . . .
6.7.2 Determining whether a number is prime
6.8 Remarks . . . . . . . . . . . . . . . . . . . . . .
6.9 Exer
ises . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
75
75
76
76
77
77
78
78
80
80
81
81
82
82
82
82
83
83
84
84
84
85
85
86
87
88
88
91
91
95
95
98
99
100
102
102
103
104
106
107
7 Loops
9 Fun tions
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
110
110
112
113
115
116
117
118
120
120
120
121
121
122
122
123
124
124
125
126
126
126
126
129
129
131
131
132
133
134
136
138
139
139
142
142
144
145
145
147
147
148
149
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
151
152
154
154
154
155
155
156
157
158
159
160
161
162
164
165
166
167
167
168
171
172
173
175
177
177
180
180
185
186
186
186
187
187
189
190
190
191
191
191
192
193
193
11.4
11.5
11.6
11.7
11.8
11.9
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
12.1
12.2
12.3
12.4
12.5
12.6
12.7
12.8
12.9
13 Arrays
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
194
194
194
196
197
197
200
200
200
201
202
203
204
206
206
208
208
208
210
210
210
211
211
212
213
213
214
215
215
216
217
217
218
218
218
220
220
221
223
226
227
228
228
13.3.3 [ as an operator . . . . .
13.4 Fun
tion Calls involving arrays .
13.4.1 Examples . . . . . . . . .
13.4.2 Summary . . . . . . . . .
13.5 Sele
tion sort . . . . . . . . . . .
13.5.1 Estimate of time taken . .
13.6 Representing Polynomials . . . .
13.7 Array Length and
onst values .
13.7.1 Why
onst de
larations? .
13.7.2 What we use in this book
13.8 Con
luding remarks . . . . . . . .
13.9 Exer
ises . . . . . . . . . . . . . .
14 More on arrays
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
15 Stru tures
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
230
231
232
233
233
234
235
236
237
238
238
238
243
244
244
245
246
248
249
250
251
252
253
253
254
255
257
257
258
258
261
264
264
267
267
268
268
269
269
271
271
271
273
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
17.1
17.2
17.3
17.4
17.5
.
.
.
.
.
.
.
.
.
.
.
.
274
277
277
277
278
279
279
279
281
282
284
284
285
285
286
287
287
288
289
290
291
292
292
293
293
294
294
296
296
296
297
297
298
298
299
299
301
301
304
305
307
308
309
18.1 Events . . . . . . . . . . . . . . .
18.1.1 Event obje
ts . . . . . . .
18.1.2 Waiting for events . . . .
18.2 Che
king for events . . . . . . . .
18.2.1 Mouse button press events
18.2.2 Mouse drag events . . . .
18.2.3 Key press events . . . . .
18.3 A drawing program . . . . . . . .
18.4 A rudimentary Snake game . . .
18.4.1 Spe
i
ation . . . . . . . .
18.4.2 Classes . . . . . . . . . . .
18.4.3 User intera
tion . . . . . .
18.5 Exer
ises . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
311
311
311
312
312
312
312
313
313
313
314
314
314
316
317
318
319
320
320
321
321
322
322
323
324
324
325
326
326
326
327
327
329
332
332
334
334
334
335
335
336
336
10
21.1 Graphs . . . . . . . . . . . . . . . . . . . .
21.1.1 Adja
en
y lists . . . . . . . . . . .
21.1.2 Extensions . . . . . . . . . . . . . .
21.1.3 Array indi
es rather than pointers .
21.1.4 Edges represented expli
itly . . . .
21.2 Adja
en
y matrix representation . . . . . .
21.3 Surng on the internet . . . . . . . . . . .
21.4 Cir
uits . . . . . . . . . . . . . . . . . . .
21.5 Edge list representation . . . . . . . . . . .
21.6 Auxiliary data stru
tures . . . . . . . . . .
21.7 Remarks . . . . . . . . . . . . . . . . . . .
21.8 Exer
ises . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
336
337
338
339
339
339
340
341
343
345
345
347
348
348
349
349
350
350
353
353
354
355
355
356
356
357
359
362
362
362
362
364
365
365
366
368
370
371
375
375
375
376
377
11
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
23 Inheritan e
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
378
380
380
382
382
382
386
387
387
388
389
390
392
393
393
393
393
395
396
397
399
400
401
401
402
402
405
406
406
409
410
411
414
414
415
415
417
417
419
420
420
421
422
12
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
26 Simulation of an airport
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
423
426
427
427
429
429
431
433
435
438
439
439
440
440
441
441
441
442
442
442
443
443
445
449
450
451
454
454
456
457
457
457
457
458
459
459
459
A Installing Simple pp
460
461
13
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
465
465
468
468
469
C Libraries
470
472
473
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
473
473
474
474
474
474
475
475
476
477
479
H Lambda expressions
480
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
481
481
481
482
482
Prefa e
This book presents an introdu
tion to programming using the language C++. No prior
knowledge of programming is expe
ted, however the book is oriented towards a reader who
has nished about 12 years of s
hooling, preferably in the S
ien
e stream.
The book presents a fairly
omprehensive introdu
tion to the C++ language, in
luding
some ex
iting additions to it in the latest standard. However, the goal of the book is to
tea
h programming, whi
h is dierent from merely learning the dierent statements in any
programming language. Programming, like any skill, must be integrated with the other
knowledge and skills that the learner has. Also, programming is a liberating skill whi
h
an
hange your world view. Programming enables you to experiment/play with the math or
physi
s that you have learned. You have been told that planets must move around the sun;
but if you know programming you
an write a program to
he
k if this
laim might be true.
In general, programming should en
ourage you to make a model of the world around you
and experiment with it on a
omputer. Obviously, this requires a
ertain experien
e and
knowledge about the world. It is our thesis that 12 years of s
hooling in the S
ien
e stream
an indeed provide su
h knowledge. In any
ase, the book has been written not only to tea
h
programming to our target readership, but also to
hallenge and provoke them to using it in
ex
iting ways.
This book does not emphasize any spe
i
programming methodology. Instead, we have
tried to present ideas in order of intelle
tual simpli
ity as well as simpli
ity of programming
syntax. The general presentation style is: \Here is a problem whi
h we would like to solve
but we
annot using the programming primitives we have learned so far; so let us learn this
new primitive". Obje
t oriented programming is
learly important, but an attempt is made
to let it evolve as a programming need. We dis
uss this more in Se
tion 0.3.
This book is expe
ted to be used along with a pa
kage, simple
pp, developed for use
in the book. Appendix A
ontains instru
tions for installing simple
pp and the s
ript that
an be used to
ompile user programs that use simple
pp. This pa
kage
ontains (a) a set
of graphi
s
lasses whi
h
an be used to draw and manipulate simple geometri
shapes on
the s
reen, and (b) some prepro
essor ma
ros whi
h help in smoothing out the introdu
tion
of C++ in the initial period. We feel that the use of simple
pp
an
onsiderably help in
learning C++. This is dis
ussed in greater detail below.
We
on
lude this prefa
e by suggesting how this book
an be tted into a
urri
ulum.
0.1 Graphi
s
I hear and I forget. I see and I remember. I do and I understand.
14
15
{Confu
ius
Traditionally, introdu
tory programming uses the domain of numbers or text to illustrate
programming problems and programming strategies. However, a large part of the human
experien
e deals with pi
tures and motion. This is espe
ially true for our target reader.
Humans have evolved to have a good sense of geometry and geography, and are experts
at seeing patterns in pi
tures and also planning motion. If this expertise
an be brought
into a
tion while learning programming, it
an make for a more mature intera
tion with the
omputer. It is for this reason that simple
pp was primarily developed.
Our pa
kage simple
pp
ontains a
olle
tion of graphi
s
lasses whi
h allow simple geometri
al shapes to be drawn and manipulated on the s
reen. Ea
h shape on the s
reen
an
be
ommanded to move or rotate or s
ale as desired. Taking inspiration from the
hildren's
programming language Logo, ea
h shape also has a pen, whi
h may be used to tra
e a
urve
as the shape moves. The graphi
s
lasses enable several
omputational a
tivities su
h as
drawing interesting
urves and patterns and performing animations together with
omputations su
h as
ollision dete
tion. These a
tivities are
hallenging and intuitive at the same
time.
The graphi
s
lasses are used right from the rst
hapter. The introdu
tory
hapter
begins with a program to draw polygons. The program statements
ommand a turtle
holding a pen to tra
es the lines of the polygon. An immediate benet is that this simple
exer
ise makes the imperative aspe
t of programming and notions su
h as
ontrol
ow very
obvious. A more important realization, even from this very elementary session is the need to
re
ognize patterns in the pi
ture being drawn. A pattern in the pi
ture often translates to
an appropriate programming pattern, say iteration or re
ursion. Identifying and expressing
patterns is a fundamental a
tivity in programming. These
onsiderations
an be brought to
the fore very easily.
As you read along, you will see that graphi
s is useful for explaining many
on
epts, from
variable s
oping and parameter passing to inheritan
e based design. Graphi
al fa
ilities make
several traditional examples (e.g. tting lines to points, or simulations) very vivid. Finally,
graphi
s is a lot of fun, a fa
tor not to be overlooked. After all, edu
ators worldwide are
on
erned about dwindling student attention and how to attra
t students to a
ademi
s.
1
C++, like many professional programming languages, is not easy to introdu
e to novi
es.
Many introdutory programming books begin with a simple program that prints the message \hello world". On the fa
e of it, this is a very natural beginning. However, even a
simple program su
h as this appears
ompli
ated in C++ be
ause it must be en
ased in a
fun
tion, main, having a return type int. The notion of a return type is
learly inappropriate
to explain on the very rst day. Other
on
epts su
h as namespa
es are even more daunting. The only possibility is to tell the students, \dont worry about these, you must write
these mantras whose meaning you will understand later". This doesnt seem pedagogi
ally
satisfa
tory.
1
16
After the student has somehow negotiated through int main and namespa
es, there is
typi
ally a long preparatory period in whi
h substantial basi
material su
h as data types
and
ontrol stru
tures has to be learnt, until any interesting program
an be written. Psy
hologi
ally and logisti
ally, this \slow" period is a problem. Psy
hologi
ally, a preparatory
period without too mu
h intelle
tual
hallenge
an be viewed by the student as boring, whi
h
is a bad initial impression for the subje
t. Se
ond, in most
ourse oerings, students tend
to have weekly le
ture hours and weekly programming pra
ti
e hours. In the initial weeks,
students are fresh and rearing to go. It is disappointing to them if there is nothing ex
iting
to be done, not to mention the waste of time.
To
ounter these problems the following features have been in
luded in simple
pp.
Instead of the main program being spe
ied inside a fun
tion main returning int, a
prepro
essor ma
ro main program is dened (it expands to int main()). The main program
an be written as
main_program{
body
}
Further, on
e the student loads in the simple
pp pa
kage using #in
lude <simple
pp>,
nothing additional needs to be loaded, nor using dire
tives given. The simple
pp pa
kage
itself loads other header les su
h as iostream and issues the using dire
tives. These
\training wheels" are taken o when fun
tions et
. are explained (Chapter 9).
A se
ond \language extension" is the in
lusion of a \repeat" statement. This statement
has the form
repeat(
ount){body}
and it
auses the body to be exe
uted as many times as the value of the expression
ount.
This is also implemented using prepro
essor ma
ros and it expands into a for statement.
We believe that the repeat statement is very easy to learn, given a good
ontext. Indeed,
it is introdu
ed in Chapter 1, where instead of using a separate statement to draw ea
h edge
of a polygon, a single line drawing statement inside a repeat statement su
es. In fa
t the
turtle-based graphi
al drawing examples are
ompelling enough that students do not have
any di
ulty in understanding nested repeat statements either.
In the se
ond and third
hapters, there is a dis
ussion of
omputer hardware, data representation and data types. These topi
s are important, but are not amenable to good
programming exer
ises. For this period, the repeat statement together with the notions introdu
ed in the rst
hapter
an be used very fruitfully to generate relevant and interesting
programming exer
ises.
In addition, simple
pp
ontains some mis
ellaneous fa
ilities, a fun
tion to generate
random numbers in a range, and
lasses for managing event driven simulations. The
ode
for all this is of
ourse provided, and the student
an look at the
ode when he or she is
ready to do so.
17
The dominant paradigm in modern programming pra
ti
e is
learly the obje
t oriented
paradigm. As a result, there have been approa
hes to edu
ation whi
h attempt to introdu
e
lasses and obje
ts from \day 1". But even the proponents of these approa
hes have noted
that
lasses and obje
ts are di
ult to tea
h from the rst day for a number of reasons. For
example, for very simple programs, organizing programs into
lasses might be very arti
ial
and verbose. Expe
ting a student to a
tually develop
lasses very early requires understanding a fun
tion abstra
tion (for developing member fun
tions/methods) even before
ontrol
stru
tures are understood. This
an appear unmotivated and overwhelming.
Our dis
ussion of obje
t oriented programming
an be
onsidered to begin in Chapter 5:
reating a graphi
al shape on the s
reen requires
reating an obje
t of a graphi
s
lass.
However, in the initial
hapters, it is only ne
essary to use
lasses, not build new
lasses.
Thus shapes
an be
reated, and member fun
tions invoked on them to move them around
et
.
Classes are presented as an evolution of the stru
t notion of the programming language
C. The major dis
ussion of
lasses in
luding the modern motivations happens in Chapter 16,
however member fun
tions are introdu
ed in Se
tion 15.5. Inheritan
e is presented in Chapter 23. Chapter 24 presents inheritan
e based design. It
ontains a detailed example in
whi
h a program developed earlier, without inheritan
e, is redeveloped, but this time using
inheritan
e. This vividly shows how inheritan
e
an help in writing reusable, extensible
ode. A brief des
ription of the design of simple
pp graphi
s system is also given, along
with an extension to handle
omposite obje
ts.
The book
an be used for either a one semester
ourse or a two semester sequen
e.
For a one semester
ourse, the re
ommended syllabus is Chapters 1 through Chapter 16,
Chapter 19, Chapter 23. Many of these
hapters
ontain multiple examples of the same
on
ept, all of these need not be \
overed" in
lass. In addition, Chapter 20 should be
dis
ussed, at least at a high level.
A two semester sequen
e
an
over Chapters 1 through Chapter 16 in the rst semester,
going over them
arefully and
onsidering at length aspe
ts su
h as proving
orre
tness of
programs. The se
ond semester
ould
over the remaining
hapters. In a two semester
ourse, it would be appropriate to introdu
e some of the modern ideas su
h as referen
e
ounting pointers (Appendix B).
Chapter 1
Introdu
tion
A
omputer is one of the most remarkable ma
hines invented by man. Most other ma
hines
have a very narrow purpose. A wat
h shows time, a
amera takes pi
tures, a tru
k
arries
goods from one point to another, an ele
tron mi
ros
ope shows magnied views of very small
obje
ts. Some of these ma
hines are mu
h larger than a
omputer, and many mu
h more
expensive, but a
omputer is mu
h, mu
h more
omplex and interesting in the kind of uses
it
an be put to. Indeed, many of these ma
hines, from a wat
h to an ele
tron mi
ros
ope
typi
ally might
ontain a
omputer inside them, performing some of the most vital fun
tions
of ea
h ma
hine. The goal of this book is to explain how a
omputer
an possibly be used
for so many purposes, and many more.
Viewed one way, a
omputer is simply an ele
tri
al
ir
uit; a giant,
omplex ele
tri
al
ir
uit, but a
ir
uit nevertheless. In prin
iple, it is possible to make
omputers without the
use of ele
tri
ity { indeed there have been designs of
omputers based on using me
hani
al
gears, or
uidi
s devi
es. But all that is mostly of histori
al importan
e. For pra
ti
al
purposes, today, it is ne to regard a
omputer as an ele
tri
al
ir
uit. Parts of this
ir
uit
are
apable of re
eiving data from the external world, remembering it so that it
an be
reprodu
ed later, pro
essing it, and sending the results ba
k to the external world. By data
we
ould mean dierent things. For example, it
ould mean some numbers you type from the
keyboard of a
omputer. Or it
ould mean ele
tri
al signals a
omputer
an re
eive from a
sensor whi
h senses temperature, pressure, light intensity and so on. The word pro
ess might
mean something as simple as
al
ulating the average of the sequen
e of numbers you type
from the keyboard. It
ould also mean something mu
h more
omplex: e.g. determining
whether the signals re
eived from a light sensor indi
ate that there is some movement in
the vi
inity of the sensor. Finally, by \send data to the external world" we might mean
something as simple as printing the
al
ulated average on the s
reen of your
omputer so
that you
an read it. Or we
ould mean a
tivating a beeper
onne
ted to your
omputer if
the movement dete
ted is deemed suspi
ious. Exa
tly whi
h parts of the
ir
uit are a
tive
at what time is de
ided by a program fed to the
omputer.
It is the program whi
h distinguishes a
omputer from most other ma
hines; by installing
dierent programs the same
omputer
an be made to behave in dramati
ally dierent ways.
How to develop these programs is the subje
t of this book. In this
hapter, we will begin
1
Also it is appropriate to think of our own brain as a
omputer made out of biologi
al material, i.e.
neurons or neural
ells.
1
18
19
wait(5);
If you exe
ute this program on your
omputer, it will rst open a window. Then a small
triangle whi
h we
all a turtle will appear in the window. Then the turtle will move and
draw lines as it moves. The turtle will draw a square and then stop. After that, the window
will vanish, and the program will end. Shortly we will des
ribe how to exe
ute the program.
2
Our turtle is meant to mimi the turtle in the Logo programming language.
20
First we will tell you why the program does all that it does, and this will help you modify
the program to make it do something else if you wish.
The rst line #in
lude <simple
pp> de
lares that the program makes use of some fa
ilities
alled simple
pp in addition to what is provided by the C++ programming language.
The next line, main programf, says that what follows is the main program. The main
program itself is
ontained in the bra
es f g following the text main program.
The line following that, turtleSim()
auses a window with a triangle at its
enter to
be opened on the s
reen. The triangle represents our turtle, and the s
reen the ground on
whi
h it
an move. Initially, the turtle points in the East dire
tion. The turtle is equipped
with a pen, whi
h
an either be raised or lowered to tou
h the ground. If the pen is lowered,
then it draws on the ground as the turtle moves. Initially, the pen of the turtle is lowered,
and it is ready to draw.
The next line forward(100)
auses the turtle to move forward by the amount given
in the parentheses, (). The amount is to be given in pixels. As you might perhaps know,
your s
reen is really an array of small dots, ea
h of whi
h
an be of any
olour. Typi
al
s
reens have an array of about 1000 1000 dots. Ea
h dot is
alled a pixel. So the
ommand
forward(100)
auses the turtle to go forward in the
urrent dire
tion it is pointing by about
a tenth of the s
reen size. Sin
e the pen was down, this
auses a line to be drawn.
The
ommand left(90)
auses the turtle to turn left by 90 degrees. Other numbers
ould also be spe
ied instead of 90. After this, the next
ommand is forward(100), whi
h
auses the turtle to move forward by 100 pixels. Sin
e the turtle is fa
ing north this time, the
line is drawn northward. This
ompletes the se
ond side of the square. The next left(90)
ommand
auses the turtle to turn again. The following forward(100) draws the third side.
Then the turtle turns on
e more be
ause of the third left(90)
ommand, and the fourth
forward(100) nally draws the fourth side and
ompletes the square.
After this the line wait(5)
auses the program to do nothing for 5 se
onds. This is the
time you have to admire the work of the turtle! After exe
uting this line, the program halts.
Perhaps you are puzzled by the () following the
ommand turtleSim. The explanation
is simple. A
ommand in C++ will typi
ally require additional information to do its work,
e.g. for the forward
ommand, you need to spe
ify a number denoting how far to move. It
just so happens that turtleSim
an do its work without additional information. Hen
e we
need to simply write (). Later you will see that there
an be
ommands whi
h will need
more than one pie
es of information, in this
ase we simply put the pie
es inside () separated
by
ommas.
3
21
the GNU C++
ompiler, whi
h must be present on your
omputer (See Se
tion A). In a
UNIX shell you
an
ompile a le by typing s++ followed by the name of the le. In this
ase
you would type s++ square.
pp. As a result of this another le is produ
ed, whi
h
ontains
the program in a form that is ready to exe
ute. On UNIX, this le is typi
ally
alled a.out.
This le
an be exe
uted by typing its name to the shell
% a.out
You may be required to type ./a.out be
ause of some quirks of UNIX. Or you may be able
to exe
ute by double
li
king its i
on. When the program is thus exe
uted, you should see
a window
ome up, with the turtle whi
h then draws the square.
1.2 Remarks
if you wish. This
exibility is meant to enable you to write su
h that the program is easy to
understand. Indeed, we have put empty lines in the program so as to help ourselves while
reading it. Thus the
ommands whi
h a
tually draw the square are separated from those
that open and
lose the windows. Another important idea is to indent, i.e. put leading spa
es
before lines that are part of main program. This is again done to make it visually apparent
what is a part of the main program and what is not. As you might observe, indentation is
also used in normal writing in English.
1.2.1 Exe
ution order
There is another important similarity between programs and text written in a natural language su
h as English. A paragraph is expe
ted to be read from left to right, top to bottom.
So is a program. By default a
omputer exe
utes the
ommands left to right, top to bottom.
But just as you have dire
tives in magazines or newspaper su
h as \Please
ontinue from
page 13,
olumn 4", the order in whi
h the
ommands of a program are exe
uted
an be
hanged. We see an example next.
22
At this point you should be able to write a program to draw any regular polygon, say a
de
agon. You need to know how mu
h to turn at ea
h step. The amount by whi
h you turn
equals the exterior angle of the polygon. But we know from Eu
lidean Geometry that the
exterior angles of a polygon add up to 360 degrees. A de
agon has 10 exterior angles, and
hen
e after drawing ea
h side you must turn by 360=10 = 36 degree. So to draw a de
agon
of side length 100, we repeat the forward(100) and right(36)
ommands 10 times. This
works, but you may get bored writing down the same
ommand several times. Indeed, you
dont need to do that. Here is what you would write instead.
#in
lude <simple
pp>
main_program{
turtleSim();
repeat(10){
forward(100);
left(36);
}
wait(5);
}
This program, when exe
uted, will draw a de
agon. The new statement in this is the
repeat. Its general form is
repeat(
ount){
statements
}
In this,
ount
ould be any number. The statements
ould be any sequen
e of statements
whi
h would be exe
uted as many times as the expression
ount, in the given order. The
statements are said to
onstitute the body of the repeat statement. Ea
h exe
ution of the
body is said to be an iteration. Only after the body of the loop is exe
uted as many times
as the value of
ount, do we exe
ute the statement following the repeat statement.
So in this
ase the sequen
e forward(100); left(36); is exe
uted 10 times, drawing
all 10 edges of the de
agon. Only after that do we get to the statement wait(5);
1.3.1 Drawing any regular polygon
Our next program when exe
uted, asks the user to type in how many sides the polygon
should have, and then draws the required polygon.
#in
lude <simple
pp>
main_program{
int nsides;
out << "Type in the number of sides: ";
in >> nsides;
23
turtleSim();
repeat(nsides){
forward(50);
left(360.0/nsides);
}
wait(5);
This program has a number of new ideas. The rst statement in the main program
is int nsides; whi
h does several things. The rst word int is short for \integer", and
it asks that a region be reserved in memory in whi
h integer values will be stored during
exe
ution. Se
ond, it also gives a name to this region: nsides. Finally it de
lares that from
now on, whenever the programmer uses the name nsides it should be
onsidered to refer
to this region. It is
ustomary to say that nsides is a variable, whose value is stored in the
asso
iated region of memory. This statement is said to dene the variable nsides. As many
variables as you want
an be dened, either by giving separate denition statements, or by
writing out the names with
ommas in between. For example, int nsides, length; would
dene two variables, the rst
alled nsides, the se
ond length. We will learn more about
names and variables in Chapter 3.
The next new statement is relatively simple.
out is a name that refers to the
omputer
s
reen. It is
ustomary to pronoun
e the
in
out (and
in in the next statement) as \see".
The sequen
e of
hara
ters << denotes the operation of writing something on the s
reen.
What gets written is to be spe
ied after the <<. So the statement in our program will
display the message
Type in the number of sides:
on the s
reen. Of
ourse, you may put in a dierent message in your program, and that will
get displayed.
In the statement after that,
in >> nsides;, the name
in refers to the keyboard. It
asks the
omputer to wait until the user types in something from the keyboard, and whatever
is typed is pla
ed into the (region asso
iated with) the variable nsides. The user must type
in an integer value followed by typing the return key. The value typed in gets pla
ed in
nsides.
After the
in >> nsides; statement is exe
uted, the
omputer exe
utes the repeat
statement. Exe
uting a repeat statement is nothing but exe
uting its body as many times
as spe
ied. In this
ase, the
omputer is asked to exe
ute the body nsides times. So
if the user had typed in 15 in response to the message asking for the number of sides to
be typed, then the variable nsides would have got the value 15, and the loop body would
be exe
uted 15 times. The loop body
onsists of the two statements forward(100) and
left(360.0/nsides). Noti
e that instead of dire
tly giving the number of degrees to turn,
we have given an expression. This is allowed! The
omputer will evaluate the expression,
and use that value. Thus in this
ase the
omputer will divide 360.0 by the value of the
variable nsides, and the result is the turning angle. Thus, if nsides is 15, the turning angle
will be 24. So it should be
lear that in this
ase a 15 sided polygon would be drawn.
24
repeat(10){
out << "Type in the number of sides: ";
in >> nsides;
repeat(nsides){
forward(50);
left(360.0/nsides);
}
}
wait(5);
The key new idea in this program is the appearan
e of a repeat statement inside another
repeat statement. How does a
omputer exe
ute this? Its rule is simple: to exe
ute a
repeat statement, it just exe
utes the body as many times as spe
ied. In ea
h iteration
of the outer repeat statement there will one exe
ution of the inner repeat statement. But
one exe
ution of the inner repeat
ould have several iterations. Thus, in this
ase a single
iteration of the outer repeat will
ause the user to be asked for the number of sides, after the
user types in the number, the required number of edges will be drawn by the inner repeat
statement. After that, the next iteration of the outer repeat would begin, for a total of 10
iterations. Thus a total of 10 polygons would be drawn, one on top of another.
The
ommands you have seen so far for
ontrolling the turtle will enable you to draw several
interesting gures. However you will noti
e that it is
umbersome to draw some simple
25
gures. For example, if you wish to draw an iso
eles right angled triangle, then you will
need to take square roots { and we havent said how to do that. Say you want to draw
a simple right angled triangle with side lengths in the proportion 3:4:5. To spe
ify the
angles would require a trigonometri
al
ulation. We now provide
ommands for these and
some
ommon operations that you might need. You may wonder, how does a
omputer
al
ulate the value of the sine of an angle, or the square root of a number? The answers to
these questions will
ome later. For now you
an just use the following
ommands without
worrying about how the
al
ulation a
tually happens.
Let us start with square roots. If you want to nd the square root of a number x, then the
ommand for that is sqrt. You simply write sqrt(x) in your program and during exe
ution,
the square root of x will be
al
ulated, and will be used in pla
e of the
ommand. So for
example, here is how you
an draw an iso
eles right angled triangle.
forward(100);
left(90);
forward(100);
left(135);
forward(100*sqrt(2));
The
ommands for
omputing trigonometri
ratios are sine,
osine and tangent. Ea
h
of these take a single argument: the angle in degrees. So for example, writing tangent(45)
will be as good as writing 1.
The
ommands for inverse trigonometri
ratios are ar
sine, ar
osine and ar
tan.
These will take a single number as an argument and will return an angle (in degrees). For
example, ar
osine(0.5) will be 60 as expe
ted. These
ommands return the angle in the
range -90 to +90. An important additional
ommand is ar
tan2. This needs two arguments,
y and x respe
tively. Writing ar
tan2(y,x) will return the inverse tangent of y/x in the full
range, -180 to +180. This
an be done by looking at the signs of y and x, information whi
h
would be lost if the argument had simply been y/x. Thus ar
tan2(1,-1) would be 135,
while ar
tan2(-1,1) would be -45, and ar
tan(-1/1)=ar
tan(1/-1)=ar
tan(-1) would
also be -45.
Now to do a triangle with side lengths 75, 100, 125 you may simply exe
ute the following.
forward(75);
left(90);
forward(100);
left(ar
tan2(75,-100));
forward(124);
As you might guess, we
an put expressions into arguments of
ommands, and put the
ommands themselves into other expressions and so on.
Some other useful
ommands that are also provided:
1. exp, log, log10 : These return respe
tively for argument x the value of ex (where e
is Euler's number, the base of the natural logarithm), the natural logarithm and the
logarithm to base 10.
2. pow : This takes 2 arguments, pow(x,y) returns xy .
3.
26
respe
tively return the sine,
osine, and tangent of an angle, but it
must be spe
ied in radians.
4. asin, a
os, atan2 respe
tively return the ar
sine, ar
osine and ar
tangent, in radians. The
ommand atan2 takes 2 arguments like the
ommand ar
tangent2 dis
ussed
above.
C++ also has
ommands sin,
os, tan whi
h return the trigonometri
ratios given the
angle in radians. And for inverse trigonometri
ratios we have the
ommands asin, a
os,
atan, atan2 whi
h return the angle in radians.
The name PI
an be used in your programs to denote , the ratio of the
ir
umferen
e
of a
ir
le to its diameter.
sin,
os, tan
1.6 Comments
The primary fun
tion of a program is to get exe
uted. Clearly, it should exe
ute
orre
tly
and do whatever it is expe
ted to do.
However, a program should be written so that it is easy to understand, when programmers
read it. The reason is simple. One programmer may write a program, whi
h another
programmer may need to modify. In su
h
ases the se
ond programmer must be able to
understand why the program was written in the manner it was written. This pro
ess
an
be aided if the original programmer writes additional notes to explain the tri
ky ideas in
the program. For this purpose, C++ allows you to insert
omments in your program. A
omment is text that is not meant to be exe
uted, but is meant solely for humans who might
read the program.
A
omment
an be written in two ways. At any point on a line, you may pla
e two slash
hara
ters, //, and then the text following the // to the end of the line be
omes a
omment.
Alternatively, anything following the /*
hara
ters be
omes a
omment, and this
omment
an span over several lines, ending only with the appearan
e of the
hara
ters */.
It is
ustomary to put
omments at the beginning mentioning the author of the program
and stating what the program does. Subsequently, wherever something non-obvious is being
done in the program, it is
onsidered polite to explain using a
omment.
Here is our polygon drawing program written the way it should be written.
#in
lude <simple
pp>
/* Program to draw a regular polygon with as many sides as the user wants.
Author: Abhiram Ranade
Date:
18 Feb 2013.
*/
main_program{
int nsides;
out << "Type in the number of sides: ";
in >> nsides;
turtleSim();
27
repeat(nsides){
forward(50);
// Ea
h side will have length 50 pixels.
left(360.0/nsides); // Be
ause sum(exterior angles of a polygon) = 360.
}
wait(5);
Although we began this introdu
tion with a pi
ture drawing program, every program you
write need not
ontain any drawing. Here is a program that does not draw anything, but
merely reads a number from the keyboard, and prints out its
ube.
main_program{
int n;
out <<"Type the number you want
ubed: ";
in >> n;
out << n*n*n << endl;
}
The
al
ulation of n is similar to what you have seen earlier. Indeed, the general rule is that
whatever mathemati
al operations you want performed
an be expressed by writing them
out as you do normally. We dis
uss the exa
t rules later, in Se
tion 3.2.
3
Although it may not seem like it, in this
hapter you have already learned a lot.
First, you have some idea of what a
omputer program is and how it exe
utes: starting
at the top and moving down one statement at a time going towards the bottom. If there are
repeat statements, the program exe
utes the body of the loop several times; the program
is said to loop through the body for the required number of iterations.
You have learned the notion of a variable, i.e. a region of memory into whi
h you
an
store a value, whi
h
an later be used while performing
omputations.
You have also learned several
ommands using whi
h you
an draw, do
al
ulations (e.g.
take square root). Later on we will see how to ourselves build new
ommands.
Finally, a very important point
on
erns observing the patterns in whatever you are doing.
When we draw a polygon, we repeat the same a
tion several times. This is a pattern that
we
an mirror in our program by using the repeat statement. By using a repeat statement
we
an keep our program
ompa
t; indeed we may be drawing a polygon with 100 sides, but
our program only has a few statements. You will see other ways of
apturing patterns in
your programs later. In general this is a very important idea.
At this point you should also see why the notation used to write programs is
alled a
language. A spoken language is very
exible and general. It has a grammati
al stru
ture, e.g.
there is a subje
t, verb, and obje
t; or there
an be
lauses, whi
h
an themselves
ontain
subje
ts, verbs, obje
ts and other
lauses. And so long as the stru
ture is respe
ted, you
28
Do not worry if you have forgotten some of this; we will refresh your memory when needed.
29
Also remember the adage \A pi
ture is worth a thousand words.". Indeed, it is very
useful if you
an show the result of your
omputation through a pi
ture. Also, in a lot of
appli
ations it is useful if you
an provide input to the program by drawing a pi
ture or
li
king on the s
reen, rather than by typing in numbers. In general, and espe
ially for
omputation on mobile phones and tablet
omputers, the areas of data visualization and
graphi
al user interfa
es are be
oming very important, and our pi
ture drawing exer
ises
will give you a taste of these areas.
And nally, drawing pi
tures is fun.
1.8.2 A note regarding the exer
ises
Programming is not a spe
tator sport. To really understand programming, you must write
many, many programs yourself. That is when you will dis
over whether you have truly
understood what is said in the book. To this end, we have provided many exer
ises at the
end of ea
h
hapter, whi
h you should assidously solve.
Another important suggestion: while reading many times you may nd yourself asking,
\What if we write this program dierently". While the author will not be present to answer
your questions, there is an easy way to nd out { write it dierently and run it on your
omputer! This is the best way to learn.
In all the problems related to drawing, you are expe
ted to identify the patterns/repetitions
in what is asked, and use repeat statements to write a
on
ise program as possible. You
should also avoid ex
essive movement of the turtle and tra
ing over what has already been
drawn.
1. Modify the program given in the text so that it asks for the side length of the polygon
to be drawn in addition to asking for the number of sides.
2. Draw a sequen
e of 10 squares, one to the left of another.
3. Draw a
hessboard, i.e. a square of side length say 80 divided into 64 squares ea
h of
sidelength 10.
4. If you draw a polygon with a large number of sides, say 100, then it will look essentially
like a
ir
le. In fa
t this is how
ir
les are drawn: as a many sided polygon. Use this
idea to draw the numeral 8 { two
ir
les pla
ed tangentially one above the other.
5. A pentagram is a ve pointed star, drawn without lifting the pen. Spe
i
ally, let
A,B,C,D,E be 5 equidistant points on a
ir
le, then this is the gure A{C{E{B{D{A.
Draw this.
6. Draw a seven pointed star in the same spirit as above. Note however that there are
more than one possible stars. An easy way to gure out the turning angle: how many
times does the turtle turn around itself as it draws?
30
7. We wrote \360.0" in our program rather than just \360". There is a reason for this
whi
h we will dis
uss later. But you
ould have some fun guring it out. Rewrite
the program using just \360" and see what happens. A more dire
t way is to put
in statements
out << 360/11;
out << 360.0/11; and see what is printed on the
s
reen. This is an important idea: if you are
urious about \what would happen if I
wrote ... instead of ...?" { you should simply try it out!
8. Read in the lengths of the sides of a triangle and draw the triangle. You will need to
know and use trigonometry for solving this.
9. When you hold a set of
ards in your hand, you usually arrange them fanned out. Say
you start with
ards sta
ked one on top of the other. Then you rotate the ith
ard
from the top by an amount proportional to i (say 10i degrees to the left) around the
bottom left
orner. Now, we
an see the top
ard
ompletely, but the other
ards are
seen only partially. In parti
ular, only a triangular portion of ea
h
ard is seen, with
the top left
orner being at the apex of ea
h triangle. This is the gure that you are
to draw. (a) Draw it assuming the
ards are transparent. (b) Draw it assuming the
ards are opaque. For this some trigonometri
al
ulation will be ne
essary. In both
ases, use repeat statements to keep your program small as possible.
10. Draw a pattern
onsisting of 7
ir
les of equal radius: one in the
enter and 6 around
it, ea
h outer
ir
le tou
hing the
entral
ir
le and two others. Try to write a program
whi
h minimizes turtle movement. Your program statements should be
hosen to
exploit the symmetry in the pattern.
11. Draw the pi
ture shown in Figure 1.1. As you
an see, the pi
ture has 36 repetitions of
a basi
pattern. Your program should be able to take a number n as input, and draw
a pi
tures having n repetitions. Make sure that the lines and the ar
s in the pattern
onne
t smoothly.
Chapter 2
A bird's eye view
Chapter 1 provided a hands-on introdu
tion to
omputers and programming. This
hapter
is also introdu
tory. In the rst part we will give a high level overview of the pro
ess of
problem solving using
omputers. In the se
ond part we will give a high level overview of
omputer hardware. This ba
kground will be useful when we dig into details of programming
from the next
hapter.
The rst step in solving any problem on a
omputer is to formulate it as a problem on
numbers. On
e this is done, you try to gure out what operations you need to perform and
in what order, to solve the numeri
al problem. In this step, you
an pretend, if you wish,
that you are solving the problem manually using pen
il and paper. A sequen
e of operations
that is guaranteed to solve the problem, des
ribed pre
isely, is
alled an algorithm. On
e
you have an algorithm, it must be expressed in a language su
h as C++, when
e it be
omes
a program whi
h
an exe
ute on a
omputer. We dis
uss all this in Se
tion 2.1.
In the se
ond part of the
hapter we
onsider questions su
h as how
omputers perform
al
ulations and how numbers are stored and pro
essed in them. In Se
tion 2.2 we dis
uss the
basi
features of the
ir
uits used in
omputers. In Se
tion 2.3 we dis
uss in detail dierent
formats used for representing numbers inside
omputers. In Se
tion 2.4 we dis
uss the
overall organization of a
omputer. Then we
onsider how individual parts work. We dis
uss
the
on
ept of a program stored in memory, and other
on
epts relevant to programming
su
h as the
on
ept of an address. We
on
lude by dis
ussing what it means to
ompile a
C++ program. The
hapter
ontains a lot of detail whi
h is given only for the purpose of
illustrating the
on
epts. It is not to be taken literally, or remembered.
As dis
ussed above, the rst step in solving a problem using a
omputer is to express it
as a problem on numbers. This is easy for several real life problems whi
h are represented
numeri
ally to begin with. Commer
e requires us to keep tra
k of pri
es and prots and
apital and salaries, and
learly this requires numbers and substantial
omputation on those
numbers. Numbers are also obviously needed to represent quantities su
h as temperature,
length, mass, for
e, voltage,
on
entration of
hemi
als. So it would seem that problems
involving su
h quantities will be naturally formulated using numbers. However, it is not
lear
that this holds for all real life entities. For example,
an we express pi
tures or language
31
32
0
0
0
1
1
1
1
0
0
0
(a)
0
0
1
0
0
0
0
1
0
0
0
1
0
0
1
0
0
0
1
0
1
0
0
0
0
0
1
0
0
1
1
0
0
0
0
0
1
0
0
1
(b)
1
0
0
0
0
0
1
0
0
1
0
1
0
0
1
0
0
0
1
0
0
1
0
0
0
0
0
0
1
0
0
0
1
1
0
0
1
1
0
0
0
0
0
0
1
1
0
0
0
0
(c)
This is not to say that all physi
al phenomenon related to the weather are well understood. In fa
t,
many simple things are not understood, e.g. how pre
isely do rain drops form. However, we understand
enough (through the hard work of several s
ientists) to make predi
tions with some
onden
e. All this is
1
33
34
23
46
69
92
115
138
161
184
207
1406
23
32358
23
93
92
158
138
20
Here is the key fa
t: the
ir
uits in a
omputer are designed su
h that for pra
ti
al purposes,
we
an pretend that numbers
ow through the wires in the
ir
uit, or get stored in the
devi
es in the
ir
uit. Su
h
ir
uits are
alled digital
ir
uits. We only dis
uss digital
ir
uits
in this
hapter. In digital
ir
uits, at any time instant, we
an think of ea
h individual wire
as
arrying a single number, or to be more pre
ise, either the number 0 or the number
1. Likewise, there are devi
es, that are
apable of performing a storage fun
tion (most
ommonly
apa
itors), and ea
h su
h individual devi
e
an also store the number 0 or the
number 1 at any time.
We brie
y explain how this illusion is
reated. But you
an ignore this paragraph if you
wish, it is not needed for understanding the rest of the book. As you may know,
urrent
ows
through the wires in an ele
tri
al
ir
uit (just as water
ows through pipes), and wires are
asso
iated with voltages (ele
tri
al equivalent of water pressure). The idea for representing
numbers in
ir
uits is simple: if a wire is at a
ertain designated high voltage (say higher
than 1 volt) then we will say that the number 1 is being
arried on it. If the wire is at a
ertain designated low voltage (say smaller than 0.2 volts), then we will say that the wire is
arrying the number 0. Note further that the
ir
uits are designed so that the wires never
arry voltages in the range 0.2 volts to 1 volt, and so there is never any ambiguity. Thus we
an pretend that wires in the
ir
uit are
arrying around numbers. Further note that if you
store ele
tri
al
harge on a
apa
itor, the
harge does not dissipate qui
kly; in this sense the
apa
itor remembers that
harge. To make the
apa
itor remember a 0, we simply drain o
harge from it. This will happens if we
onne
t the
apa
itor to our designated low voltage.
If on the other hand, we
onne
t our
apa
itor to a high voltage, a large amount of
harge
35
gets stored on it; this represents the number 1. For the rest of the book, we will not worry
about
harges and voltages. Instead we will only talk about
apa
itors and wires holding
and
arrying the numbers 0 or 1.
Of
ourse, we will want to store or
ommuni
ate numbers besides 0 and 1. We will see
how to do this in Se
tion 2.3. On
e we have numbers represented, it is possible to design
ir
uits whi
h
an perform arithmeti
on them. This is
onsidered in Se
tion 2.6.
36
00000000000000000000000000011001
Note that if we de
ide to use an n bit long binary representation, the maximum value that
an be represented is 2n 1 (sequen
e of n 1s). Thus we must be sure that the number we wish
to represent is not larger. As an example, sin
e we know that telephone numbers (in India)
are at most 8 digits long, and sin
e the largest possible 8 digit number 99999999 < 2 1,
we
an use n = 32 to represent telephone numbers.
32
32
This only means that there is no standard, built-in me
hanism for representing numbers outside the
range. However, you will be able to design your own me
hanisms if you wish, as you are asked to in
Exer
ise 21 of Chapter 13.
3 You may nd it
onvenient to rst
onvert 232 and 25 to binary, and then subtra
t.
2
37
are represented using the so-
alled
oating point representations. Usually,
oating point
representations use bit strings of length 32 or 64.
In the s
ienti
world real numbers are typi
ally written using the so
alled s
ienti
notation, in the form: f 10q , where the signi
and f typi
ally has a magnitude between
1 and 10, and the exponent q is a positive or negative integer. For example the mass of an
ele
tron is 9:109382 10 kilograms, or Avogadro's number is 6:022 10 .
On a
omputer, real numbers are represented using a binary analogue of the s
ienti
notation. So to represent Avogadro's number, we rst express it in binary. This is not hard
to do: it is
1:11111110001010101111111 2
Note that this is approximate, and
orre
t only to 3 de
imal digits. But then, 6:022 10
was only
orre
t to 3 digits anyway. The exponent 1001110 in de
imal is 78. Thus the
number when written out fully will have 78 bits. We
ould use 78 bits to represent the
number, however, it seems unne
essary. Usually we will not need that mu
h pre
ision in our
al
ulations. A better alternative, is to represent ea
h number in two parts: one part being
the signi
and, and the other being the exponent.
For example, we
ould use 8 bits to represent the exponent, and 24 bits to represent the
signi
and, so that the number is neatly tted into a single 32 bit word! This turns out
to be essentially the method of
hoi
e on modern
omputers. You might ask why use an
8-24 split of the 32 bits and why not 10-22? The answer to this is: experien
e. For many
al
ulations it appears that an exponent of 8 bits is adequate, while 24 bits of pre
ision in
the signi
and is needed. There are s
hemes that use a 64 bit double word as well and the
split here is 11-53, again based on experien
e.
Note that the signi
and as well as the exponent
an be both positive or negative. One
simple way to deal with this is to use a sign-magnitude representation, i.e. dedi
ate one bit
from ea
h eld for the sign. Note that we don't need to expli
itly store the de
imal point (or
we should say, binary point!) { it is always after the rst bit of the signi
and. Assuming
that the exponent is stored in the more signi
ant part or the word, Avogadro's number
would then be represented as:
0; 1001110; 0; 11111111000101010111111
Two points to be noted: (a) we have put
ommas after the sign bit of the exponent, the
exponent itself, and the sign bit of the signi
and, only so it is easy to read. There are no
ommas in memory. (b) Only the most signi
ant 23 bits of the signi
and are taken. This
requires throwing out less signi
ant bits (what happened in this example), but you might
even have to pad the signi
and with 0s if it happens to be smaller than 23 bits.
As another example,
onsider representing 12:3125. This is -1100.0101 in binary, i.e.
1:1000101 2 . Noting that our number is negative and our exponent is positive, the
representation would be
0; 0000011; 1; 110001010000000000000000
Again the
ommas are added only for ease of reading.
31
23
1001110
23
In the s
ienti
notation, the position of the de
imal point within the signi
and depends upon the value
of the exponent. Hen
e the name
oating point.
4
38
The exa
t format in whi
h real numbers are represented on modern
omputer hardware
and in C++ is the IEEE Floating Point Standard. It is mu
h more
ompli
ated, but has
more features, some of whi
h we will dis
uss later.
The memory of a modern
omputer may
ontain a huge number of basi
memory elements,
say 2 . These are usually
apa
itors as dis
ussed earlier. Ea
h basi
memory element is
apable of storing 1 bit. The number of bits that
an be stored is dened to be the
apa
ity,
or the size, of the memory. More
ommonly, the memory size is measured in bytes, where a
byte is simply 8 bits. Thus a memory with 2
apa
itors has size 2 bits or 2 bytes, or 4
Gigabytes.
35
35
35
32
2.5.1 Addresses
Ea
h byte (group of 8 elementary memory devi
es, say
apa
itors) in the memory is asso
iated with a unique label, or address. If a memory has N bytes, then the addresses
an start
at 0 and end at N 1 (Figure 2.3). Note that while in day to day life we would have used
39
Output
Control
Arithmetic unit
Input 1
Byte 0
Byte 1
Byte 2
.
.
.
.
.
.
.
.
.
input 2
Keyboard
Address Port
Network
Memory
Monitor
Data Port
Disk
Byte N1
Read control
Write control
Control Unit
40
Thus the number y will be stored in byte x. Byte x will
ontinue to hold the number
y until another write operation is performed on byte x.
2. Reading data from byte x: For this, you pla
e the address x, on the address port. Then
you pla
e a 1 on the read
ontrol port. The 1 on the read
ontrol port signals the
memory to sense the data stored in byte x of the memory and pla
e it on the data
port. On
e data appears on the data port, it
an be moved from there to where it is
needed.
What we des
ribed above is a byte-oriented memory. More
ommon are word-oriented memories. In these, when we supply an address, the word starting at the given address is sent
ba
k or written to. In byte-oriented memories, the data port will
onsist of 8 wires, be
ause
8 bits need to be
ommuni
ated. In word-oriented memories, the data port will likewise have
to have 32 wires. Similarly for half-words and double word oriented memories.
How many wires do we need in the address port? Let us take our 2 byte memory as an
example. In this memory, the addresses range from 0 to 2 1. Thus the largest address
onsists of 32
onse
utive 1s. Hen
e the address port will have to have 32 wires, in order
that we may spe
ify any possible address. In general, if the memory has N bytes, then we
will need to have log N wires in the address port.
32
32
The arithmeti
unit has
ir
uits using whi
h it is possible to perform basi
arithmeti
operations, i.e. addition, subtra
tion, multipli
ation, division, for numbers in all formats des
ribed
earlier, unsigned and signed integers, and
oating. It re
eives the operands through two ports
named Input1 and Input2 and the result of the operation is pla
ed on the port named Output, see Figure 2.3. What operation is to be performed depends upon the value supplied
on the Control port. The arithmeti
unit
an also
onvert numbers from one representation
to another, e.g. given a number represented as an integer on one of the inputs, its representation in the
oating format (exponent and signi
and)
an be produ
ed on the output
port.
You may think that the arithmeti
unit must
onsist of many very
ompli
ated
ir
uits.
That is indeed true. However, for the purpose of programming, we don't need to know how
the
ir
uits are to be designed, it is su
ient to know what they
an do.
The input-output devi
es are
onsidered to be peripherals, and the rest of the
omputer the
\main
omputer".
2.7.1 Keyboard
The simplest input devi
e is a keyboard. A
ode number is assigned to ea
h key on the
keyboard. When a key is pressed, the
orresponding
ode number is sent to the main
41
omputer. The
ontrol unit de
ides what is to be done with the re
eived
ode number; for
example it might just get stored in the memory.
2.7.2 Display
A
omputer terminal s
reen or display is a fairly
omplex devi
e. You probably know that a
display is made up of pixels whi
h are arranged in a grid, say 1024 rows and 1024
olumns.
Ea
h pixel
an be made to show the
olour you desire. By showing appropriate
olours, you
an display pi
tures, or letters, or the turtle from Chapter 1. The display hardware de
ides
what
olour to show by
onsulting a small amount of memory asso
iated with ea
h pixel.
The amount of memory depends on the sophisti
ation of the display. For a simple bla
k and
white display, it is enough to spe
ify whether the pixel is to appear white or bla
k. So a
single bit of memory is enough. You may also have displays whi
h
an show dierent levels
of brightness: k bits of memory will be able to store numbers between 0 and 2k 1 and hen
e
that many levels of brightness, or gray levels. In
olour displays we need to simultaneously
store the red, green, blue
omponents at ea
h pixel, and so presumably even more bits are
needed. Indeed high quality
olour displays might use as many as 24 bits of memory for ea
h
pixel. To display an image, all we need to do is to store appropriate values in the memory
asso
iated with ea
h pixel in the s
reen. If we have 24 bits of memory per pixel, then be
ause
there are 1024 1024 = 2 pixels, we will need a memory with addresses between 0 and
2 1, ea
h
ell of the memory
onsisting of 24 bits. A reasonable
orresponden
e is used
to relate the pixels and addresses in memory: the
olour information for the pixel (i; j ) i.e.
the pixel in row i and
olumn j (with 0 i; j < 1024) is stored in address 1024i + j of the
memory. When the
ir
uitry of the s
reen needs to display the
olour at pixel (i; j ) it pi
ks
up the
olour information from address 1024i + j of the memory. If you wish to
hange the
image, it su
es to
hanges the data in the memory. So in some ways, the display
an be
treated very mu
h like another memory. The main
omputer
an a
ess the display memory,
but it should not be
onfused with the main memory of the
omputer.
20
20
2.7.3 Disks
Devi
es su
h as disks
an also be thought of as storing data at
ertain addresses, however,
the addresses no longer refer to spe
i
apa
itors in the
ir
uitry, but spe
i
lo
ations on
the magneti
surfa
e. The magneti
surfa
e
an be magnetized in dierent dire
tions: the
dire
tion indi
ates whether a 0 or a 1 is stored there.
Opti
al
ompa
t disks also fun
tion in a similar manner. The surfa
e of an opti
al
ompa
t disk has elevations and dips whi
h
an be dete
ted by shining a laser on them.
Whether a
ertain region of the disk stores a 0 or it stores a 1 is determined by the pattern
of elevations and dips in that region.
2.7.4 Remarks
There is a lot of innovation and ingenuity in designing peripheral devi
es. This is of
ourse
outside the s
ope of this book.
42
However, the fundamental ideas should be noted: (a)
ommuni
ation between the main
omputer and the peripheral devi
e happens by sending numbers, (2) information is stored
as bits, by designating some physi
al property to determine whether a 0 or a 1 is stored (3)
if several bits are stored, there will be a notion of address using whi
h we
an refer to some
sele
ted bit or group of bits.
As the name implies, the Control Unit
ontrols the other parts of a
omputer. The
ontrol
unit
ontains
omplex
ir
uitry. But it is possible to understand its fun
tion at a high level.
When you want to solve a problem, you would like the
omputer to perform a
ertain
sequen
e of operations, depending upon the algorithm you have de
ided for the problem.
To take a simple example, suppose you want to
ompute the
ube of a number. Then you
would like the number to be read from the keyboard and put into some lo
ation in memory,
say the word at address 100. After that you would like the arithmeti
unit to multiply the
number at the lo
ation by itself, two times and then put the result at some other lo
ation,
say lo
ation 104. Finally, you would like the number at lo
ation 104 to be displayed on the
s
reen. If you want to solve some other problem, then a dierent sequen
e of operations
would have to be performed.
The
ontrol unit
an get the other parts of the
omputer to perform whatever a
tion you
may desire, in
luding the a
tions noted above. However, you must somehow tell the
ontrol
unit what you want. How do you do this?
Here is the outline of the answer: you store the sequen
e of a
tions you want performed
in the memory of the
omputer, and then ask the
ontrol unit to perform those a
tions, in
the order you have stored them. Many details need to be explained: (a) how do we store
a
tion sequen
es in memory, (b)
an we design
ir
uits using whi
h the
ontrol unit
an
\understand" what is stored in memory and perform the required a
tion. The answer to
part (b) is Yes, the
ontrol unit indeed
ontains the required
ir
uits, but any explanation
of these
ir
uits is outside the s
ope of this book.
A high level answer to part (a) is as follows. It follows the basi
idea that on a
omputer,
everything is represented using numbers, in
luding a
tions that you want the
omputer
to perform! The
omputer designer provides you with a numeri
al
oding system
alled
ma
hine language using whi
h you
an tell the
ontrol unit what you want. As an example,
the
omputer designer may de
ide that (a) the sequen
e of numbers of the form 53; x (for any
x) means the
ontrol unit should perform the a
tions needed to read a real number from the
keyboard and put it into the memory lo
ation x, (b) sequen
es of the form 57; x; y; z mean
the
ontrol unit should perform the a
tions needed to multiply the real numbers in lo
ations
x; y and store the result in lo
ation z , (
) sequen
es of the form 63; x mean the
ontrol unit
should perform the a
tions needed to display the real number in lo
ation x on the s
reen,
and (d) the sequen
e
onsisting of just the number 99 means the
ontrol unit should shut
down the
omputer. Ea
h su
h sequen
e of numbers is
alled a ma
hine instru
tion, and a
sequen
e of ma
hine instru
tions is
alled a ma
hine language program. Thus, the sequen
e
of numbers 53, 100, 57, 100, 100, 104, 57, 100, 104, 104, 63, 104, 99. would represent a
ma
hine language program
onsisting of the instru
tions:
43
53, 100. This would read in a real number from the keyboard.
57, 100, 100, 104. This would multiply the number in memory lo
ations 100 with itself
and put the result in lo
ation 104. Thus the square of the number will get pla
ed in
lo
ation 104. Note that after this instru
tion exe
utes we will have the original number
and its square in lo
ations 100 and 104 respe
tively.
57, 100, 104, 104. This would multiply the number in memory lo
ations 100 and
104 and put the result in lo
ation 104. Thus the number and its square would get
multiplied, and the result, the
ube, would get pla
ed in lo
ation 104.
63,104. This would print out the
ube on the s
reen.
99. The program would stop.
Me
hanisms would be provided using whi
h you
ould store this sequen
e of instru
tions in
memory, say from lo
ation 0, and then ask the
ontrol unit to start exe
uting the program
starting at lo
ation 0.
We made up the format of the instru
tions to
reate the example program given above.
No real
omputer has the given instru
tions. Note further that there is no reason why we
hose the pattern 57; x; y; z to denote an instru
tion that multiplies numbers. The designer
ould have
hosen another number instead of 57. The point is that the designer would have
to
reate some set of instru
tions, and the
omputation you want would have to be expressed
using those instru
tions. Many, many dierent instru
tions will be needed, to express all the
dierent basi
fun
tions a
omputer
an perform. Here is an example. Suppose you wish to
have integer numbers in lo
ations x; y and wish to pla
e their produ
t in lo
ation z, then the
instru
tion 57; x; y; z would not work be
ause it would interpret the bit pattern in lo
ations
x; y as a real number (Se
tion 2.3.3),
ompute the produ
t and store the resulting real
number in lo
ation z. So the designer would have to provide another type of instru
tion, say
58; x; y; z for multiplying integers, and say also 59; x; y; z for multiplying unsigned integers.
Here is another example. We spe
ied above that the
ontrol unit exe
utes the rst
instru
tion in our program, then the next, and then the next and so on. But what if we
want to exe
ute some instru
tions several times, say be
ause we have a repeat statement?
To do this
onveniently, the designer will usually provide a
onvenient ma
hine instru
tion.
Thus our designer may designate that some other sequen
e, say 73; x means that
ontrol unit
should start exe
uting instru
tions from lo
ation x. So
onsider the program 53, 100, 57,
100, 100, 104, 57, 100, 104, 104, 63, 104, 73, 0, 99. Suppose the program is stored starting
at lo
ation 0. Now after exe
uting 73, 0, the
ontrol would not exe
ute the next instru
tion
in memory, 99, as it usually does. But instead 73,0 asks it to exe
ute the next instru
tion
starting at lo
ation 0. Thus, it would
ause the instru
tion 53, 100 to be exe
uted again
and so on. Thus another number would be read, and its
ube would be displayed. And this
would happen again, and again, ad innitum. More
ommonly, you will want a sequen
e
of instru
tions to be repeated some nite number of times, the ma
hine language will also
ontain instru
tions for that purpose. We will omit the details of this.
44
When the earliest
omputers were built, they
ould be used only by writing ma
hine language
programs. Indeed, you had to de
ide where in memory you would store your data, look up the
omputer manual and determine what instru
tion would perform the a
tions you wanted,
and then write out the sequen
e of numbers that would
onstitute the ma
hine language
program. Then the ma
hine language program would have to be loaded into the
omputer
memory, and then you
ould exe
ute the program. As you might guess, this whole pro
ess
is very tiring and error prone.
Fortunately, today, programs
an be written in the style seen in Chapter 1, and what will
be dis
ussed in the rest of the book. We do not think about what instru
tions to use, nor
the address in memory where to store the number of sides of the polygon we wish to draw.
Instead, we use familiar mathemati
al expressions to denote operations we want performed.
We give names to regions of memory and store data in them by referring to those names.
The
omputer, of
ourse, really only \understands" instru
tion
odes and memory addresses,
and does not understand mathemati
al notation or the names we give to parts of memory.
So how does our ni
e looking program a
tually exe
ute on a
omputer?
Clearly, the ni
e looking programs we write must rst be translated into ma
hine language
instru
tions whi
h the
omputer does understand. This is done by a program
alled a
ompiler, whi
h fortunately has been written by someone already! The program s++ that
you used in the last
hapter is a C++
ompiler, whi
h takes a C++ program (e.g. the
one from Se
tion 1.7) and generates the le (e.g. a.out) whi
h
ontains a ma
hine language
program like what we dis
ussed in Se
tion 2.8. When you type
a.out
from the
ommand line or
li
k on the program i
on, the
ontent of the le a.out gets loaded
into the memory, and then what is loaded starts getting exe
uted.
The rst important point made in this
hapter is that for solving any problem, you rst
need to express it as a problem on numbers. In some
ases this is easy, whereas in some
other
ases, we had to produ
e some kind of a
oding s
heme. We also dened the notion of
algorithms, and gave examples of algorithms that are learned in primary s
hool.
45
The se
ond important point was that numbers
an be pro
essed using appropriately
designed
ir
uits. We
an think of numbers as physi
al
ommodities whi
h take up spa
e
as they are stored and when they are moved. You
an even (metaphori
ally!)
onsider that
number have mass, be
ause energy is needed to move them around in a
omputer! We then
saw, at a high level, how parts of a
omputer fun
tion. We also saw, how su
h
ir
uits
an
be
ontrolled using programs, and that these programs are sequen
es of instru
tions, ea
h
of whi
h is also represented using numbers! Finally, we dis
ussed notions su
h as an address
and the
orresponden
e between ma
hine language and C++.
We
on
lude with the remark that our des
ription of
omputer hardware in this
hapter
is very simple-minded, and that real hardware is mu
h more elaborate.
All the exer
ises below are meant to be only paper and pen
il exer
ise.
1. How would you represent a position in a
hess game? Or you
an answer this question
for any board game you are familiar with.
2. Make sure you are able to
onvert numbers from de
imal to binary and vi
e versa. You
may not be familiar with
onverting fra
tions. For this simply note that a 1 in the
ith position after the (binary) point, has pla
e value 2 . Thus 0.1 in binary is just
half, 0.01 is just one fourth. So now you should be able to de
ide whether the rst bit
after the point should be one or not, by
omparing the fra
tional part to half. The
remaining bits
an be de
ided by extending the idea. You will not need to
onvert
fra
tions in this book, however, it will be useful to be able to
onvert intgers routinely.
So pra
ti
e with dierent examples.
3. How many dierent numbers are represented in the sign magnitude representation on
n = 3 bits? Make a table showing what bit pattern represents whi
h number.
4. How many dierent numbers are represented in the n bit 2's
omplement representation? Make a table showing what bit pattern represents whi
h number.
5. Suppose you want to draw a \+" symbol at the
enter of a 1024 1024 display.
Suppose the display will show a pixel white if you store a 1 at the
orresponding
memory lo
ation. Suppose the \+" is 100 pixels tall and wide, and 2 pixels thi
k. In
whi
h s
reen memory lo
ations would you store 1s?
1
Chapter 3
Numbers
In this
hapter we will see C++ statements for pro
essing numbers. By \pro
essing numbers"
we mean a
tions su
h as reading numbers from the keyboard, storing them in memory,
performing arithmeti
or other operations on them, and writing them onto the s
reen. We
have already seen some examples in Chapter 1. In this
hapter, we will build up on that and
state everything more formally and more generally.
As mentioned in Chapter 2, textual data is also represented numeri
ally on a
omputer:
ea
h
hara
ter is represented by its numeri
al ASCII
ode. Text pro
essing turns out to be
a minor variation of numeri
pro
essing. Logi
al data is also represented numeri
ally. We
onsider these topi
s brie
y, they are
onsidered at length in Se
tion 14.1 and Se
tion 6.7
respe
tively.
Using the repeat statement and what we learn in this
hapter we will be able to write
some interesting programs. We see some of these at the end.
A region of memory allo
ated for holding a single pie
e of data (for now a single number),
is
alled a variable. C++ allows you to
reate a variable, i.e. allo
ate the memory, and give
it a name. The name is to be used to refer to the variable in the rest of the program. A
variable
an be
reated by writing the following in your program.
data-type variable-name;
In this, data-type must be a data-type sele
ted from the rst
olumn of Table 3.1, and
variable-name a name
hosen as per Se
tion 3.1.1. The term data-type is used to denote
the type of values expe
ted to be stored in the variable (
olumn 4), and the size of memory
allo
ated (
olumn 3).
You have already seen some examples, e.g. in Se
tion 1.3.1 we wrote:
int nsides;
We said then that this would
reate a variable
apable of storing integers. From Table 3.1 you
now also know that typi
ally the variable will use 4 bytes of memory, and will store positive
and negative numbers. As dis
ussed in Se
tion 2.3.2 su
h numbers are typi
ally represented
46
47
-128 to 127
0 to 255
-32768 to 32767
0 to 65535
-2147483648 to 2147483647
0 to 4294967295
-2147483648 to 2147483647
0 to 4294967295
9223372036854775808 to
9223372036854775807
0 to 18446744073709551615
false (0) or true (1)
Positive or negative. About 7
digits of pre
ision. Magnitude
in the range 1:17549 10 to
3:4028 10
Positive or negative. About 15
digits of pre
ision. Magnitude
in the range 2:22507 10
to 1:7977 10
Positive or negative. About 18
digits of pre
ision. Magnitude
in the range 3:3621 10 to
1:18973 10
# Bytes
Allo
ated
(Indi
ative)
1
2
4
4
8
1
4
Use
for
storing
Chara
ters or
small integers.
Medium size
integers.
Standard size
integers.
Storing longer
integers.
Even longer
integers.
Logi
al values.
Real numbers.
38
38
double
12
High
pre
ision and very
high range real
numbers.
308
308
long double
4932
4932
48
in the two's
omplement representation, and if so the numbers in the range -2147483648 to
2147483647
an be stored.
C++ provides the types signed
har, short int, long int, and long long int
for storing (positive or negative) integers. Variables of these respe
tive types will use amount
of memory as given in Table 3.1 and will be able to store values in
orrespondingly larger
or smaller range. In all su
h
ases, very likely the two's
omplement representation of Se
tion 2.3.2 is used.
If you know that you will only store non-negative integers in a
ertain variable, you may
hoose one of the unsigned types. For example, you may write:
unsigned int telephoneNumber;
This will
reate a variable
alled telephoneNumber, using 4 bytes, and the values will be
stored using the binary representation, as dis
ussed in Se
tion 2.3.1. The types unsigned
har, unsigned short, unsigned long and unsigned long long are also used for storing non-negative integers. These will respe
tively use dierent amount of memory and allow
orrespondingly smaller or larger ranges.
The following will
reate a variable
alled temperature for storing real numbers.
double temperature;
The
reated variable will be 8 bytes long. It will typi
ally use the IEEE Floating point
standard as dis
ussed in Se
tion 2.3.3. The type name double is short for \double pre
ision",
in
omparison to the type float whi
h uses 4 bytes and is
onsidered \single pre
ision".
The rst 9 types in Table 3.1 are said to be integral types, and the last 3, floating
types.
It should be noted that the size shown for ea
h data-type is only indi
ative. The C++
language standard only requires that the sizes of
har, short, int, long, long long to
be in non-de
reasing order. Likewise, the sizes of float, double, long double are also
expe
ted to be non-de
reasing. The exa
t sizes are may vary from one
ompiler to another
but
an be determined as dis
ussed in Se
tion 3.1.6.
The
har types are most
ommonly used for storing text, as we will see later. In su
h
uses it is
ustomary to omit the qualiers signed or unsigned and write:
har firstLetterOfName;
This will
reate a 1 byte variable, of type either unsigned
har or signed
har. One of
these types will be
hosen by the
ompiler. Note that if you are using
har to store text,
the exa
t
hoi
e does not matter be
ause the ASCII
ode is uses only the range 0 to 127
whi
h is present in either the signed or the unsigned version. If you use the
har type to
store integers (that happen to lie in a small range) then it is best to spe
ify whether you
want the signed or the unsigned type.
The type bool is primarily used to store logi
al values, as will be seen in Se
tion 6.7.
The phrase value of a variable is used to refer to the value stored in the variable. So the
stored telephone number (after it is stored, and we will say how to do this) will be the value
of the variable telephone number.
We nally note that you
an dene several variables in a single statement if they have
the same type, by writing:
data-type variable-name1, variable-name2, ... variable-namek;
49
// in degrees entigrade.
This statement denes 2 variables, of whi
h the rst one, p, is initialized to 10239. No initial
value is spe
ied for q, whi
h means that some unknown value will be present in it. The
number \10239" as it appears in the
ode above is said to
onstitute an integer literal, i.e. it
is to be interpreted literally as given. Any integer number with or without a sign
onstitutes
an integer literal. The words false and true are literals whi
h stand for the values 0 and
1. So for bool variables, it is re
ommended that you write initializations using these, e.g.
bool penIsDown = true;
50
rather than writing bool penIsDown = 1; whi
h would mean the same thing but would be
less suggestive. For
onvenien
e in dealing with
har data, any
hara
ter en
losed in a pair
of single quotes is an integer literal that represents the ASCII value of the en
losed
hara
ter.
Thus you may write
har letter_a = 'a';
This would store the
ode, 97, for the letter 'a' in the variable letter a. You
ould also
have written
har letter a = 97; but writing 'a' is preferred, be
ause it is easier to
understand. In general, we may write a
hara
ter between a pair of single quotes, and that
would denote the ASCII value of the
hara
ter. Chara
ters su
h as the newline (produ
ed
when you press the \enter" key), or the tab,
an be denoted by spe
ial notation, respe
tively
as 'nn' and 'nt'. Note that literals su
h as 'nn' and 'a' really represent an integer value. So
we
an in fa
t write
int q = 'a';
23
31
This statement denes 4 variables, the se
ond, third and fourth are respe
tively initialized
to 1.5, 6:022 10 and 9:10938188 10 . The variable w is not initialized.
23
31
On
e a name is de
lared
onst, you
annot
hange it later. The
ompiler will
omplain if
do attempt to
hange it.
1
The number of mole ules in a mole of any substan e, e.g. number of arbon atoms in 12 gm of arbon.
51
Simply put: when this statement is exe
uted, the
omputer will wait for us to type a value
onsistent with the type of pqr. That value will then be pla
ed in pqr.
The exa
t exe
ution pro
ess for the statement is a bit
ompli
ated. First, the statement
ignores any whitespa
e
hara
ters that you may type before you type in the value
onsistent
with the type of pqr. The term whitespa
e is used to
olle
tively refer to several
hara
ters
in
luding the spa
e
hara
ter (' '), the tab
hara
ter ('nt'), and the newline
hara
ter ('nn').
In addition, the verti
al tab ('nv'), the formfeed
hara
ter ('nf') and the
arriage return ('nr')
are also
onsidered whitespa
e. These three
hara
ters are now only of histori
al interest.
The rst non whitespa
e
hara
ter you type is
onsidered to be the start of the value
you wish to give for pqr. You may type several non whitespa
e
hara
ters as value if
appropriate. After typing the desired value you must type a whitespa
e
hara
ter (often
newline) to signify that you have nished typing the value that you wanted. Let us
onsider
an example. Suppose pqr has type int, then if you exe
ute the above statement, and type
123 56
the spa
es that you type at the beginning will be ignored, the value 123 will be stored into
pqr. This is be
ause the spa
e following 123 will serve as a delimiter. The 56 will be used
for a subsequent read statement, if any. Note further that the value you type will not be
re
eived by your program unless you type a newline after typing the value. Thus to pla
e
123 into pqr in response to the statement above, you must type a newline either immediately
following 123 or following 56.
If pqr was of any of the
oating types, then a literal of that type would be expe
ted.
Thus we
ould have typed in 6.022e23 or 1.5. If pqr was of type bool you may only type
0 or 1.
Reading into a
har variable
You may not perhaps expe
t what happens when you exe
ute
har xyz;
in >> xyz;
In this
ase the initial whitespa
es that you type if any will be ignored, as dis
ussed above.
Any non-whitespa
e value is
onsidered appropriate for the type
har, so the rst su
h value
will be a
epted. The ASCII value of the rst non-whitespa
e
hara
ter that you type will
be pla
ed into xyz. Note that if you type 1, then xyz will be
ome 49. This is be
ause the
ASCII value of the
hara
ter '1' is 49. If you type the letter a, then xyz would get the value
97.
52
If you wish to read values into several variables, you
an express it in a single statement.
in >> pqr >> xyz;
3.1.5 Printing
If you print a variable rst of type bool,
short, int
or long, writing
its value will be printed. A minus sign will be printed if the value is negative. The nal endl
will
ause a newline to follow.
If you print a
oating type variable, then C++ will print it in what it
onsiders to be
the best looking form: as a de
imal fra
tion or in the s
ienti
format.
Printing a
har variable
This will
ause that
hara
ter whose ASCII value is in xyz to be printed. Thus in this
ase
the letter a will be printed. Following that a newline will be printed, be
ause of the endl at
the end of the statement.
Printing several values
The two previous statements above
an be
ombined into a single statement if you wish.
out << rst << endl << xyz << endl;
53
where variable is the name of a variable, and expression is an expression as des
ribed
above. Here is an example.
int x=2,y=3,p=4,q=5,r;
r = x*y + p*q;
This will
ause r to get the value of the spe
ied expression. Using the values given for the
other variables, the expression is simply 2*3+4*5, i.e. 26. Thus r will get the value 26.
We
ould also print out the value of the expression by writing
out << x*y+p*q << endl;
Note that when you use a variable in an expression, you must have assigned it a value
already, say by initializing it at the time of dening it, or by reading a value into it from
the keyboard, or in a previous assignment statement. If this is not done, the variable will
54
still
ontain some value, only you dont know what value. If an unknown value is used in a
omputation, the result will of
ourse be unpredi
table in general.
Note that the operator = is used somewhat dierently in C++ than in mathemati
s. In
mathemati
s a statement r = x*y + p*q; asserts that the left hand side and right hand
side are equal. In C++ however, it is a
ommand to evaluate the expression on the right
and put the resulting value into the variable named on the left. After the assignment the
values of the expressions on either side of the = operator are indeed equal if we
onsider r
on the left hand side to be a trivial expression.
Note however, that we
annot write x*y + p*q = r; be
ause we require the left hand
side to be a variable, into whi
h the value of the expression on the right hand side must be
stored.
The rule des
ribed above makes it perfe
tly natural to write a statement su
h as:
p = p + 1;
This is meaningless in mathemati
s; in C++ however it just says: evaluate the expression on
the right hand side and put the resulting value into the variable named on the left. Assuming
p is as in the
ode fragment given earlier, its value is 4. Thus in this
ase the value 4+1=5
would be put in p. Note that a statement su
h as p + 1 = p; is illegal { the left hand side
p + 1 does not denote a variable.
3.2.1 Integer division and the modulo operator %
In C++, when one integer value is divided by another, the result is dened to also be the
largest integer no larger than the quotient. Thus if you write
int m=100, n=7, p, q;
p = m/n;
q = 35/200;
the variables p and q would respe
tively get the values 14 and 0. In other words, we only
get the integer part of the quotient.
If you wish to get the remainder resulting when one integer divides another you use the
% operator. Thus the expression m % n evaluates to the remainder of m when divided by n,
where m,n must have an integral type. The operator % has the same pre
eden
e as *, /.
Here is a
ode fragment that reads in a duration given in se
onds and prints out the
equivalent duration in hours, minutes, and se
onds.
out <<"Give the duration in se
onds: ";
int duration;
in >> duration;
int hours, minutes, se
onds;
hours = duration/3600;
minutes = (duration - hours*3600)/60;
se
onds = duration % 60;
out <<"Hours: "<< hours <<", Minutes: "
<< minutes <<", Se
onds: "<< se
onds << endl;
55
If you run this
ode, and type 5000 when asked, you would get the following output as
expe
ted:
Hours: 1, Minutes: 23, Se
onds: 20
3.2.2 Subtleties
The assignment statement is somewhat tri
ky. The rst point
on
erns the
oating point
representations. Both, float and double are impre
ise representations, where the signi
and
is
orre
t only to a xed number of bits. So if an arithmeti
operation ae
ts less signi
ant
bits, then the operation will have no ee
t. As an example,
onsider the following
ode.
float w, y=1.5, avogadro=6.022E23 ;
w = avogadro + y;
What is the value of w? Suppose for a moment that we pre
isely
al
ulate the sum avogadro
+ y. The sum will be
602200000000000000000001:5
We will have a problem when we try to store this into a float type variable. This is be
ause
a float type variable
an only stores signi
ands of 24 bits, or about 7 digits. So in order
to store, we would treat everything beyond the most signi
ant 7 digits as 0. If so we would
get
602200000000000000000000
This loss of digits is
alled round-o error. After the round o, this
an now t in a float,
be
ause it
an be written exa
tly as 6.022E23. Net ee
t of the addition: nothing! The
variable w gets the value avogadro even though you assigned it the value avogadro + 1.5.
This example shows the inherent problem in adding a very small float value to a very large
float value.
Some subtleties arise when we perform an arithmeti
operation in whi
h the operands
have dierent types, or even simply if you store one type of number into a variable of another
type. C++ allows su
h operations, and
ould be said to perform su
h a
tions reasonably
well. However, it is worth knowing what exa
tly happens.
Suppose we assign an int expression to a float variable, C++ will rst
onvert the
expression into the
oating point format. An int variable will have 31 bits of pre
ision
ex
luding the sign, whereas a float variable only has 24 bits or so. So essentially some
pre
ision
ould be lost. There
ould be loss of pre
ision also if we assign a float expression
to an int variable. Consider:
float y = 6.6;
int x = y;
The value 6.6 is not integral, so C++ tries to do the best it
an: it keeps the integer part. At
the end, x will equal 6. Basi
ally, when a
oating value is to be stored into an integer, C++
uses trun
ation, i.e. the fra
tional part is dropped. You might want the assigned value to
be the
losest integer. This you
an obtain for yourself by adding 0.5 before the assignment.
Thus if you write x=y+0.5;, then x would be
ome 7, the integer
losest to y. Note that some
56
pre
ision
ould be lost when you store a value from a double (53 bits of pre
ision) into a
oat (24 bits of pre
ision). Over
ow is also possible, as dis
ussed later.
When we perform an arithmeti
operation on operands of the same type the result is also
omputed to be of the same type. If your program asks to perform arithmeti
operations on
operands of dierent types, then the operands are rst
onverted by C++ so that they have
the same type. The rules for this are fairly natural. C++ always
onverts less expressive
types to more expressive ones, where unsigned integral types are deemed less expressive than
signed integral types, whi
h in turn are deemed less expressive than the
oating types. If the
two types dier in size, then the smaller is
onverted to have a larger size. As an example,
suppose we have an arithmeti
expression var1 op var2, where var1 is int and var2 is
float. Then var1 will be
onverted to float, and the result will also be float. If var1,
var2 are long, int, then var2 will be
onverted to long. If the operands are of type
float, long long then both will be
onverted to double, and so on. After the expression
is evaluated, it may either itself form an operand in a bigger expression, or it might have to
be stored into a variable. In both
ases, there may have to be a further type
onversion.
Literals also have a type asso
iated with them. An integer literal like 35 is
onsidered to
be of type int, and a
oating literal like 100.0 is by default
onsidered to be of type double.
You
an spe
ify literals of spe
i
types by atta
hing the suxes L,F,U whi
h respe
tively
stand for long,
oat, unsigned. Thus if you write 100LU, it will be interpreted as a literal of
type long unsigned, having the value 100.
It is important to be
areful with division.
int x=100, w;
float y,z;
y = 360/x;
z = 360.0/x;
w = 360.0/x;
As per the rules stated, 360/x will be evaluated to have an integer value sin
e both operands
are integer. Thus the exa
t quotient 3.6 will be trun
ated to give 3. This value will be stored
(after
onversion to the
oating point format) into y. In the next statement, 360.0/x the
rst operand is double, hen
e the result will be evaluated as a double, i.e. 3.6. This value
will be stored in z. In the nal statement, the value of the expression will indeed be 3.6,
however be
ause w is of type int, there will have to be a type
onversion, and as a result
the value stored in w will be just 3.
Note nally that if the dividend and the divisor are of integral types, and the divisor
is 0, then an error will be reported when su
h an operation happens during exe
ution, and
the program will stop with a message. Something dierent happens for
oating types, as
dis
ussed in Se
tion 3.2.4.
3.2.3 Over
ow and arithmeti
errors
For ea
h numeri
al data type, we have mentioned a
ertain largest possible and smallest
possible value that
an be represented. While performing
al
ulations, the results
an go
outside this allowed range. In this
ase, what exa
tly happens is handled dierently for
dierent types.
57
For the unsigned data types, the rule is that arithmeti
is performed modulo 2n, where
n is the number of bits used. So for example if you add up two short numbers, both
65535, then the result will be (65535+65535) mod 65536 = 65534, where you may note that
2 = 65536.
For signed integer types, the language does not spe
ify what must happen. In other
words, you as a programmer must be
areful to ensure that the numbers stay within range.
16
or
(T2) exp
This latter form is a lega
y from the C language. The type
onversion rules as des
ribed
earlier apply, e.g. int(6.4) would evaluate to the integer value 6.
3.2.6 Assignment expression
It turns out that C++ allows you to write the following
ode.
58
int x,y,z;
x = y = z = 1;
This will end up assigning 1 to all the variables. This has a systemati
explanation as follows.
Any assignment, say z = 1, is also an expression in C++. Not only is the assignment
made, but the expression stands for the value that got assigned. Further, the asso
iativity of
= is right-to-left, i.e. given an expression x = y = z = 1, the rightmost assignment operator
is evaluated rst. This is dierent from the other operators you have seen so far, su
h as the
arithmeti
operators, in whi
h the evaluation order is left to right. Thus, the our statement
x = y = z = 1; is really to be read as
x = (y = (z = 1));
Now the expression inside the innermost parentheses, z = 1 is required to be evaluated rst.
This not only puts the value 1 into z, but itself evaluates to 1. Now the statement ee
tively
be
omes
x = (y = 1);
3.3 Examples
We
onsider some simple examples of using the data-types and assignment statements. These
do not in
lude the bool type whi
h is
onsidered in Se
tion 6.7.
Here is a program that reads in the temperature in Centigrade and prints out the equivalent temperature in Fahrenheit.
main_program{
double
entigrade, fahrenheit;
out << "Give temperature in Centigrade: ";
in >>
entigrade;
Note that the operator + is exe
uted last be
ause it has lower pre
eden
e than * and /. The
operator * exe
utes before / be
ause it appears to the left. Note we
ould have written 9
instead of 9.0. This is be
ause that while multiplying
entigrade, it would get
onverted
to a double value anyway, sin
e
entigrade is double. Similarly we
ould have written 5
and 32 instead of 5.0 and 32.0. But what we have written is preferable be
ause it makes it
very
lear that we are engaging in
oating point arithmeti
.
In the next program you are expe
ted to type in any lower
ase letter, and it prints out
the same letter in the upper
ase.
59
main_program{
har small,
apital;
out << "Type in any lower
ase letter: ";
in >> small;
apital = small + 'A' - 'a';
}
When the statement
in >> small; exe
utes, the ASCII value of the letter typed in by the
user is pla
ed in small. Suppose as an example that the user typed in the letter q. Then
its ASCII value, 'q' is pla
ed in small. This value happens to be 113. To understand the
next statement, we need to note an important property of the ASCII
odes.
The lower
ase letters a-z have
onse
utive ASCII
odes. The upper
ase letters A-Z also
have
onse
utive ASCII
odes. From this it follows that for all letters, the dieren
e between
the ASCII
ode of the upper
ase version and the lower
ase version is the same. Further,
be
ause 'A' and 'a' denote the integers representing the ASCII
odes of the respe
tive letters,
'A'-'a' merely gives the numeri
al dieren
e between the ASCII
odes of upper
ase and lower
ase of the letter a. But this dieren
e is the same for all letters. Hen
e given the ASCII
ode value for any lower
ase letter, we
an add to it 'A' - 'a', and this will give us the ASCII
ode of the
orresponding upper
ase letter. So this value gets pla
ed in
apital, whi
h
when printed out displays the a
tual upper
ase letter.
To
omplete the example, note that the ASCII
ode of 'A' is 65. Thus 'A'-'a' is -32.
Sin
e small
ontains 113,
apital would get 113 - 32, i.e. 81. This is indeed the ASCII
ode
of Q as required.
Note that the digits '0', '1', '2', : : :, '9' also have
onse
utive ASCII
odes.
What do you think happens on exe
uting the following pie
e of
ode?
main_program{
turtleSim();
int i = 1;
repeat(10){
forward(i*10); right(90);
forward(i*10); right(90);
i = i + 1;
}
wait(5);
}
Imagine that you are the
omputer and exe
ute the
ode one statement at a time. Write
down the values of dierent variables as you go along, and draw the lines tra
ed by the turtle
60
as it moves. You will probably be able to gure out by exe
uting 2-3 iterations. It is strongly
re
ommended that you do this before reading the explanation given next.
In the rst iteration of the repeat, i will have the value 1, and this value will in
rease
by 1 at the end of ea
h iteration. The turtle goes forward 10*i, i.e. a larger distan
e in ea
h
iteration. As you will see, the turtle will tra
e a \spiral" made of straight lines.
We next see another
ommon but important intera
tion of the assignment statement and
the repeat statement. Consider the following problem. We want to read some numbers,
from the keyboard, and print their average. For this, we need to rst nd their sum. This
an be done as follows.
main_program{
int
ount;
out << "How many numbers: ";
in >>
ount;
float num,sum=0;
repeat(
ount){
out << "Give the next number: ";
in >> num;
sum = sum + num;
}
The statement sum = sum + num; is exe
uted in ea
h iteration, and before it is exe
uted,
the next number has been read into num. Thus in ea
h iteration the number read is added
into sum. Thus in the end sum will indeed
ontain the sum of all the numbers given by the
user.
3.4.1 Programming Idioms
There are two important programming idioms used in the programs of the previous se
tion.
The rst idiom is what we might
all the sequen
e generation idiom. Note the value of
the variable i in the rst program. It started o as 1, and then be
ame 2, then 3, and so
on. As you
an see, by
hanging the starting value for i and adding a dierent number to
i inside the loop instead of 1, we
ould make i take the values of any arithmeti
sequen
e
(Exer
ise 7). By
hanging the operator to * instead of +, we
ould make the values form a
geometri
sequen
e if we wished.
The se
ond idiom is what we might
all the a
umulation idiom. This was seen in the
se
ond program. The variable sum was initialized to zero, and then the number read in ea
h
iteration was added to the variable sum. The variable sum was thus used to a
umulate the
values read in ea
h iteration. Stating this dierently, suppose the number of numbers read
61
is n, and suppose the values read were v ; : : : ; vn. Then after the exe
ution of the loop in
the se
ond program the variable sum has the value:
0 + v + v + + vn
Here we have written 0+ expli
itly to emphasize that the value
al
ulated a
tually also
depends on the value to whi
h sum was initialized, and that happened to be zero, but it is
a
hoi
e we made.
You might wonder whether this idea only works for addition or might work for other
operators as well. For example C++ has the
ommand max, where max(a,b) gives the
maximum of the values of the expressions a,b. Will using max help us
ompute the value
of the maximum of the values read? In other words, what would happen if we dened a
variable maximum and wrote
1
instead of sum = sum + num;? For simpli
ity, assuming n = 4 and also assuming that
maximum is initialized to 0 just as sum was, the value taken by maximum at the end of the
repeat will be:
max(max(max(max(0; v ); v ); v ); v )
Will this return the maximum of v ; v ; v ; v ? As you
an see this will happen only if
at least one of the numbers is positive. If all numbers are negative, then this will return
0, whi
h is not the maximum. Before we abandon this approa
h as useless, note that we
a
tually have a
hoi
e in de
iding how to initialize maximum. Clearly, we should initialize it
to as small a number as possible, so that the values vi
annot be even smaller. We know
from Se
tion 3.1.6 that it su
es to
hoose -numeri
limits<float>::max(). Thus our
initialization be
omes:
1
62
}
out << "Maximum is: " << maximum << endl;
This program does not behave identi
ally to the program sket
hed earlier, i.e. obtained by
initializing maximum to - numeri
limits<float>::max(). The exer
ises ask you to say
when the programs might dier, and whi
h one you prefer under what
ir
umstan
es.
3.4.2 Combining sequen
e generation and a
umulation
Often we need to
ombine the sequen
e generation and a
umulation idioms.
Suppose we want to
ompute n fa
torial, written as n!, whi
h is just short hand for the
produ
t n (n 1) (n 2) 2 1. How
an we do this?
The key point to note is that we merely need to take the produ
t of the sequen
e of
numbers 1; 2; : : : ; n 1; n, and this is a sequen
e that we
an generate. But if we
an generate
a sequen
e, then we
an easily take its produ
t, i.e. a
umulate it using the multipli
ation
operator.
main_program{
int n, fa
=1, i=1;
in >> n;
repeat(n){
fa
= fa
* i;
i = i + 1;
}
out << fa
<< endl;
// sequen
e a
umulation
// sequen
e generation
In the above program, if you ignore the statement fa
= fa
* i;, then we merely have
our sequen
e generation program, with the sequen
e 1 to n being generated in the values
taken by the variable i. However, the value generated in ea
h iteration is being multiplied
into the variable fa
whi
h was initialized to 1. Hen
e in the end the variable fa
ontains
the produ
t of the numbers from 1 to n, i.e. n! as we wanted. Note that the program also
works for n=0, sin
e 0! is dened to be 1.
The idioms of a
umulation and sequen
e generation are very useful, and very, very
ommonly used. They are used so
ommonly that they will be
ome se
ond nature soon. We
see a more involved example in Chapter 4.
63
whi
h merely means C = C + 1;, where C is any variable. This usage is very useful.
For
ompleteness, we des
ribe some additional, possibly
onfusing, feature of the ++
operator. Turns out that for a variable C, C++ is also an expression. It stands for the value
that C had before 1 was added to it. Thus if you wrote
int x = 2, y;
y = x++;
after exe
ution y would be 2 and x would be 3. We re
ommend that you avoid a statement
su
h as y = x++; and instead write it as the less
onfusing y = x; x++;. It is worth noting
that in the modern era programming is often done by teams, and so your
ode will be read
by others. So it is good to write in a manner that is easy to understand qui
kly.
The operator ++ written after a variable is said to be the (unary) post-in
rement operator. You may also write ++C, whi
h is the unary pre-in
rement operator operating on the
variable C. This also
auses C to in
rease by 1. ++C also has a value as an expression: ex
ept
the value is the new value of C. Thus if you wrote
int x = 2, y;
y = ++x;
both x,y would get the value 3. Again this will usually be better written as ++x; y = x;
(or for that matter as x++; y = x;) be
ause it is less
onfusing.
Likewise, C--; means C = C - 1;. This is also a very useful operator, and is
alled the
post de
rement operator. As an expression C-- has the value that C had before the de
rementation. Analogously, you have the pre-de
rement operator with all similar properties.
Again, it is re
ommended that you use the expression forms sparingly.
3.5.2 Compound assignment operators
The a
umulation idiom
ommonly needs the statement vname = vname + expr;, where
vname is a variable name, and expr an expression. This
an be written in short as:
vname += expr;
The phrase vname += expr is also an expression and has as its value the value that got
assigned. Analogously C++ has operators *=, and -=, /=. These operators are
olle
tively
alled the
ompound assignment operators.
The expression forms of the operator += and others are also quite
rypti
and hen
e
onfusing. It is re
ommended that you use these expression forms sparingly.
64
It turns out that most C++ programmers would write the average
omputation program
from Se
tion 3.4 slightly dierently, as follows.
main_program{
int
ount;
out << "How many numbers: ";
in >>
ount;
float sum=0;
repeat(
ount){
out << "Give the next number: ";
float num;
// defined inside the loop
in >> num;
sum = sum + num;
}
As you
an see, the only dieren
e is that the variable num is dened inside the loop rather
than outside. We rst explain how the variable denition is exe
uted in the new program.
As you might guess, the variable indeed gets
reated when
ontrol rea
hes the denition
statement. From the time of
reation, the variable is available to the program, until the time
the
ontrol rea
hes the end of the loop body in the
urrent iteration. In other words, the
variable is destroyed when the
ontrol rea
hes the end of the body! Thus, in ea
h iteration
of the loop, the variable is
reated and destroyed. Of
ourse, destroying a variable is only
notional, the
omputer merely assumes that the memory that was given is now available for
use. It should also be noted that the variable
annot be used outside the repeat loop, or
before its denition inside the loop.
Experien
ed programmers prefer to write the average
omputation
ode in the new style,
be
ause in this the denition of num is pla
ed
lose to its use. Pla
ing denitions
lose to
the use makes it easier to read the program, espe
ially if it has many variables and the loop
bodies are large.
Next we will state the general rules for all this. First we need the notion of a blo
k.
3.6.1 Blo
k
The region of the program from an opening bra
e, f, to the
orresponding
losing bra
e,
g, is
alled a blo
k. Thus the entire program forms a blo
k, and the body of a repeat also
forms a blo
k, whi
h is
ontained inside the blo
k
onsisting of the entire program. If there
is a repeat inside a repeat, then the blo
k
orresponding to the body of the former is
ontained inside the blo
k asso
iated with the latter. As you
an see, two blo
ks must either
65
be
ompletely disjoint, or one of them must be
ompletely
ontained in the other. It is also
useful to dene the parent blo
k of a variable denition: it is the innnermost blo
k in whi
h
the variable is dened.
3.6.2 General prin
iple 1
Now, we
an restate more formally what we stated earlier. When
ontrol rea
hes a variable
denition, the
orresponding variable is
reated. The variable is destroyed when the
ontrol
leaves the parent blo
k of the denition. The variable is potentially available for use in the
region of the program starting at the point of its denition, and going to the end of its parent
blo
k. This region of the program is
alled the s
ope of the denition.
We have already dis
ussed how this prin
iple applies to the variable num of the program
given above.
The prin
iple also applies to the variable sum in the program. Its parent blo
k is the main
program itself, and indeed, the entire portion of the program from the point of its denition
to the end of the program
an refer to the variable sum.
3.6.3 General prin
iple 2
The prin
iples in giving names to variables and using the names, are somewhat similar to
the way in whi
h we give names to human beings.
Let us rst dis
uss how we name human beings. Ideally, you might think that we should
insist that all human beings be given dierent names. But of
ourse, this does not happen.
It is perfe
tly possible that there exist two families in Mumbai both of whi
h name their son
Raju. In that
ase whenever a referen
e is made to \Raju" in either family, it is deemed to
refer to the son in that family. There is no
onfusion. Noti
e however, that usually the same
name is not given to two
hildren in the same family.
As another example,
onsider the name Manmohan. In most families in India, the name
would be
onsidered to be referring to the Prime Minister of India. Suppose now that a
ertain family de
ides to name their son \Manmohan". In this family, after the birth of the
son, if anyone speaks of Manmohan, it would probably be
onsidered as referring to the son.
You
ould say that the son \overshadows" the Prime Minister in this family.
Variable naming in C++ is almost as
exible as naming of human beings, in
luding the
idea of shadowing. The analogue of the family is a blo
k of the program.
In a C++ program, it is possible to use the same name in several variable denitions.
However, it is ne
essary that the denitions have dierent parent blo
ks. Even if there are
many variable denitions for the same name, the rules for
reating and destroying variables
remain the same. A variable is
reated when the denition is en
ountered during exe
ution,
and is destroyed when its parent blo
k is exited. Or alternately, a variable is
reated when
ontrol enters the s
ope of the denition and is destroyed when
ontrol leaves the s
ope of
the denition. Suppose now that the
ontrol has entered the s
ope of a
ertain denition
that
reates a variable Q. As the exe
ution pro
eeds, but before the variable is destroyed,
suppose we have another denition, also of variable Q. Now a se
ond variable named Q will
be
reated and while
ontrol is inside the s
ope of the se
ond denition, the se
ond variable
will shadow the rst variable. In other words, inside the s
ope of the se
ond denition the
66
name Q will not refer to the rst variable. It will instead refer to the se
ond variable { unless
that of
ourse is shadowed by a third denition of Q.
Here are some examples.
main_program{
int sum=0;
repeat(5){
int num;
in >> num;
sum += num;
}
out << sum << endl;
int prod=1;
repeat(5){
int num;
in >> num;
prod *= num;
}
out << prod << endl;
}
// statement 1
// statement 2
In this
ase the referen
es to num in the rst loop are in the s
ope of the denition in
statement 1 (and of no other denition), and hen
e refer to the variable
reated in statement
1. Similarly the referen
es to num in the se
ond loop are in the s
ope of the denition in
statement 2 (and of no other denition), and hen
e refer to the variable
reated in statement
2. This is what you would intuitively expe
t, and indeed the program will
ompute the sum
of the rst 5 numbers that it reads, and the produ
t of the next 5.
Here is an example of a program in whi
h there is shadowing.
main_program{
int p=10;
repeat(3){
out << p << endl;
int p=5;
out << p << endl;
}
out << p << endl;
}
// statement 3
// statement 4
// statement 5
// statement 6
// statement 7
67
3. At the end of the loop the variable
reated in statement 5 will be destroyed. Thus when
ontrol rea
hed statement 7, the variable
reated in statement 6 will have been destroyed,
and the statement is in the s
ope only of the denition in statement 3. Thus the referen
e
to p in statement 7 will be
onsidered to be to the variable p dened in statement 3. Thus
statement 7 will
ause 10 to be printed. Thus the entire
ode when exe
uted will
ause the
sequen
e of numbers 10, 5, 10, 5, 10, 5, 10 to be printed.
1. What is the value of x after the following statements are exe
uted? (a) x=22/7;
(b) x=22.0/7; (
) x=6.022E23 + 1 - 6.022E23 (d) x=6.022E23 - 6.022E23 + 1
(e) x=6.022E23 * 6.022E23. Answer for three
ases, when x is dened to be of type
int, float, double. Put these statements in a program, exe
ute and
he
k your
on
lusions.
68
2. For what values of a,b,
will the expressions a+(b+
) and (a+b)+
evaluate to dierent
values?
3. I want to
ompute the value of
= . I have many
hoi
es in
performing this
omputation. I
an
hoose the order in whi
h to perform the multipli
ations and divisions, and I
an
hoose the data type I use for representing the nal
and intermediate results. Here is a program whi
h does it in several ways. Guess whi
h
of these are likely to give the
orre
t answer, nearly the
orre
t answer, or the wrong
answer. Then run the program and
he
k whi
h of your guesses are
orre
t.
100
6
100
99 98 97 96 95
2 3 4 5 6
main_program{
int x = 100 * 99 * 98 * 97 * 96 * 95/ (1 * 2 * 3 * 4 * 5 * 6);
int y = 100/1 * 99/2 * 98/3 * 97/4 * 96/5 * 95/6;
int z = 100/6 * 99/5 * 98/4 * 97/3 * 96/2 * 95/1;
int u = 100.0 * 99 * 98 * 97 * 96 * 95/ (1 * 2 * 3 * 4 * 5 * 6);
int v = 100.0/1 * 99/2 * 98/3 * 97/4 * 96/5 * 95/6;
int w = 100.0/6 * 99/5 * 98/4 * 97/3 * 96/2 * 95/1;
out << x << " " << y << " " << z << endl;
out << u << " " << v << " " << w << endl;
5. Write a program that reads in distan
e d in in
hes and prints it out as v miles, w
furlongs, x yards, y feet, z in
hes. Remember that a mile equals 8 furlongs, a furlong
equals 220 yards, a yard is 3 feet, and a foot is 12 in
hes. So your answer should satisfy
d = (((8v + w) 220 + x) 3 + y ) 12 + z , and further w < 8; x < 220; y < 3; z < 12.
6. What is the state of the
omputer, i.e. what are the values of the dierent variables
and what is on the s
reen, after 4 iterations of the loop of the spiral drawing program of
Se
tion 3.4? Write down your answer without running the program. Then modify the
program so that it prints the values after ea
h iteration and also waits a few se
onds
so you
an see what it has drawn at that point. Run the modied program and
he
k
whether what you wrote down earlier is
orre
t.
69
7. Write a program that prints the arithmeti
sequen
e a; a + d; a + 2d; : : : ; a + nd. Take
a; d; n as input.
8. Write a program that prints out the geometri
sequen
e a; ar; ar ; : : : ; arn, taking a; r; n
as input.
9. Write a program whi
h reads in side, nsquares, q. It should draw nsquares as many
squares, all with the same
enter. The sidelength should in
rease by q starting at side.
Repeat with the modi
ation that the sidelength should in
rease by a fa
tor q.
10. Write a program whi
h prints out the squares of numbers from 11 to 99.
11. What does the following program draw:
2
main_program{
turtlesim();
int i=0;
repeat(30){
left(90);
forward(200*sine(i*10));
forward(-200*sine(i*10));
right(90);
forward(10);
i++;
}
wait(5);
12. The ASCII
odes for the digits 0 through 9 are 48 through 57. Suppose in the third
statement below, the user types in two digits. The ASCII
odes for the digits will then
be pla
ed in p,q. You are to ll in the blanks in the
ode su
h that dig1 gets the value
of the digit in p (not the value of its ASCII
ode), and similarly dig2 should get the
value of the digit in q. Finally, the integer n should
ontain the value of the number
in whi
h p is in the tens pla
e and q in the units pla
e.
har p,q;
int dig1,dig2,n;
in >> p >> q;
// equivalent to
in >> p;
in >> q;
dig1 = ...
dig2 = ...
n = ...
For example, if the user typed '1','2', then p,q will
ontain the values 49,50. At the
end we would like dig1,dig2,n to be respe
tively 1,2,12.
13. Write a program that takes as input the
oordinates of two points in the plane and
prints out the distan
e between them.
70
14. Write the program for
omputing the maximum of numbers as suggested initially in
Se
tion 3.4.1, i.e. the one in whi
h maximum was to be initialized to the value (numeri
limits<float>::max()). Does this program behave identi
ally (i.e. give the
same result for the same inputs) to the program given at the end of the Se
tion 3.4.1?
If you think the programs behave dierently, state the inputs for whi
h the programs
will behave dierently.
15. What does the following program
ompute?
double x;
int n;
in >> x >> n;
repeat(n){
x = x * x;
}
16. Draw a smooth spiral. The spiral should wind around itself in a parallel manner, i.e.
there should be a
ertain point
alled \
enter" su
h that if you draw a line going out
from it, the spiral should interse
t it at equal distan
es as it winds around.
Chapter 4
A program design example
In this
hapter, we will use what we have learned to write a small program. This program
will be more
omplex than all the programs you have seen so far. Indeed, the pro
ess of
writing it will illustrate some important ideas in designing programs.
The problem we
onsider is of nding the value of e, the base of the natural logarithm.
The number e
an be written as the following innite series.
1 + 1 + 1 + 1 ++ 1
e = nlim
!1 0! 1! 2! 3!
n!
It turns out that the terms of the series de
rease very fast, so that you get a good approximate
value by evaluating the series to a few terms.
The rst step in writing a program is to write down a spe
i
ation, by whi
h we mean
a pre
ise des
ription of what is the input to the problem, and are the outputs, and what
it means for the output generated by the program to be
orre
t. Next
omes the step of
designing the program itself. After that, you typi
ally test the program, i.e.
ompile and
run it on some inputs to see if it works
orre
tly. It might so happen, that the program
makes a mistake, in whi
h
ase you need to go ba
k and try to nd what went wrong. This
step is often
alled debugging, where a bug is a
ommon euphemism for programming errors.
In addition to testing the program, you may formally reason for yourself that your program
is
orre
t.
We
onsider these steps next.
As mentioned above, the spe
i
ation for a program states
learly what the input and the
output of the program will be. For our program to
ompute e, what should the input be?
A natural possibility is to ask the user to state how mu
h of the series should be summed.
Input to e
omputation program: Integer n, where n 0.
Output from e
omputation program: 1=0! + 1=1! + : : : + 1=n!.
You may have thought that the spe
i
ation for our program is \obvious". However,
note that the input n
ould have been interpreted as the number of terms to whi
h the series
71
72
should be summed, in whi
h
ase the output would have to be 1=0!+1=1!+ : : : +1=(n 1)!.
So there appear to be two \obvious" ways of spe
ifying the input. This may often happen.
In su
h
ases it doesnt really matter what we
hoose, so long as we
learly state what we
have
hosen. A se
ond point to be noted is that we have made a remark about the input
being required to be non-negative. In professional programs, you are expe
ted to
he
k rst
whether the valid inputs are spe
ied by the user. In this small example we will ignore this
issue, but it is a point you should note. It is a good idea to tell the user of the program what
is a valid input and what isnt.
4.1.1 Examples
\Wait a minute", I would say. \Is there a parti
ular example of this
general problem?"
Ri
hard Feynman, Surely you'r joking Mr. Feynman
It is good to write down some examples of the spe
i
ation, i.e. for some spe
i
input
values what the output values ought to be. For our program, you may write: For input 0,
the output should be 1. For input 1, the output should be 2, for input 2, the output should
be 2.5. Writing down examples for
es you to
he
k that you are being alert while writing
the spe
i
ation. Indeed, you may write the spe
i
ation as above, but in your mind might
still be thinking that n denotes the number of terms to be added. When you make up an
example, your
onfusion will vanish.
Also, when your program is written, these examples
an be used to test it.
The rst, extremely important, idea in designing programs is to think about how you would
solve the problem using a paper and pen
il, without
omputers. On
e you are
lear in your
mind how to solve a problem using paper and pen
il, it often su
es to mimi
the solution
on a
omputer.
Quite likely, you have already tried to solve the problem using paper and pen
il, if you
tried to
onstru
t examples as suggested in Se
tion 4.1.1. You probably
omputed the terms
of the series, and added them together as you went along. It is probably a good idea to
imagine yourself doing the
al
ulation for a large value of n, say n = 10. In this pro
ess,
you will perhaps see that there is a general pattern, and you might also see how to do the
al
ulation e
iently. In parti
ular, suppose you have just
al
ulated the value of the term
1=3!, and then you go to the next term, 1=4!. Cal
ulating 1=4! involves dividing 1 by the
numbers from 1 to 4, but of these divisions, you just did the divisions from 1 to 3 when you
al
ulated 1=3!. So you
an get the value of the term 1=4! simply by dividing 1=3! by 4. So
to
al
ulate any term 1=t!, you do just one additional division: you take the term 1=(t 1)!
that you previously
omputed, and divide that further by t.
Next you need to gure out if there a repetitive pattern in your
al
ulations. If you nd
that you are performing similar steps repeatedly, you
ould perhaps put those steps in a
repeat statement. Indeed, there is a pattern. The pro
ess of
al
ulating the term 1=t! is
very similar to the pro
ess of
omputing 1=(t 1)!. So it would seem that you should indeed
73
have a repeat loop. We want to
al
ulate the sum 1=0! + 1=1! + : : : + 1=n!, whi
h has n + 1
terms, so it needs n additions. So presumably we will use n iterations of a repeat loop. And
our goal will be that we should have 1=0! + 1=1! + : : : + 1=t!
al
ulated after t iterations of
the loop. Thus in the tth iteration we will
al
ulate 1=t!, whi
h we will then add to the sum.
The next step is to de
ide what variables we need in the program. This step is a bit
tri
ky. When you imagine yourself solving a problem using a paper and pen
il, you just
keep on doing the additions or multipli
ations (or divisions in this
ase) using more paper
as ne
essary. You may have written a lot of numbers on the paper as you worked, but that
doesnt mean you need a separate variable for holding ea
h number that you might have
written. The key question to ask is: what data do we need at the beginning of the tth
iteration in order to perform the work that we planned for the iteration? We need a variable
to hold ea
h su
h pie
e of data.
Clearly, we need to remember the sum of the series
al
ulated so far. Thus we should have
a variable result in whi
h the sum
omputed so far will be held. This variable should be of
a
oating type. It is
ustomary to use high pre
ision, and so we will use the type double.
Further, we said that to
al
ulate 1=t! we need the value 1=(t 1)! whi
h we
al
ulated in the
previous iteration. So we will have a variable term in whi
h we will expe
t to hold the value
1=(t 1)! at the beginning of the tth iteration. Finally in the tth iteration we need to divide
by t to get the new term value that needs to be added to result. In other words, we need
to know whi
h iteration just nished. So we will use a variable i whi
h will hold the value
t during the tth iteration. What we have de
ided about the program
an be summarized as
the following sket
h.
main_program{
int n;
in >> n;
int i = ...;
// we fill in the blanks later.
double result = ..., term = ...;
repeat(n){
// Need
ode to
al
ulate 1/0!+1/1!+...+1/t! in the tth iteration.
// At the beginning of the tth iteration
// i = t, term = 1/(t-1)!, result = 1/0!+1/1!+...+1/(t-1)!
}
out << result << endl;
Our plan, as stated in the
omment, is a
tually enough for us to
omplete the program.
Imagine exe
uting the program and entering the loop for the rst time. In our plan, this
orresponds to t = 1. So we want the variable i to be 1. Thus we must initialize it to 1
when we
reate it. Se
ond, we want term to have the value 1=0!, sin
e t 1 = 0. Thus we
need to initialize term also to 1 = 1=0!. Likewise we also need to initialize result to 1.
Next we de
ide pre
isely what we need to do inside the loop. Imagine we are exe
uting
the tth iteration of the loop. Our idea was to have result get the value 1=0! + : : : + 1=t! in
the tth iteration. Assuming everything has gone a
ording to our plan so far, we will have
the values 1=0! + : : : + 1=(t 1)! in result and 1=(t 1)! in term. So we should have the
following statement inside the loop:
74
/***************************************************
Program to
al
ulate e.
Cal
ulates 1/0! + 1/1! + ... + 1/n!, for input n.
n >= 0.
This will leave in result the value that we planned. Is this enough? No, in order to sti
k
to our plan, in the t + 1th iteration, we will need to have the value t + 1 in the variable i,
and the value 1=t! in the variable term. This
an be a
heived by writing inside the loop:
term = term/i;
i = i + 1;
The
omplete program is given in Figure 4.1. Note that we
ould have written the three
statements inside the loop as result += term/i; term /= i; i++;. Indeed this is oftern
preferred. Note that the loop body
ould also have been:
term = term/i;
result = result + term;
i = i+1;
In this the new value of result is
al
ulated using the old value of result and the new value
of term. In the version in Figure 4.1, the new values of all variables are
al
ulated from the
old values of all variables. Some may therefore nd the
ode in Figure 4.1 to be simpler.
75
4.2.1 Testing
The next step is to run the program and test if it really works. I
ompiled and ran this
program supplying the values 1,2,3,4 for n, and it did print out the answers 2, 2.5, 2.667,
2.70833. If you did the
al
ulation by hand as suggested earlier, you will realize that these
values are indeed
orre
t. You
ould also try some large value for n, say 10. When I did
this, I got the result 2.71828. Sin
e e is a famous number, you should be able to get its value
from textbooks, and you will see that 2.71828 is the often quoted value.
4.2.2 Corre
tness Proof
Testing is one way to
he
k if your program is
orre
t. However, testing does not really give
you a
omplete guarantee of
orre
tness. You know what the program does for the input
values that you
he
ked; but how
an you be sure that the program will not give a wrong
answer on other values?
One way to be sure is to prove that the program is
orre
t. This is often not pra
ti
al
for large programs. However, a proof
an be written for our small program for
omputing e.
We do this next. It will have some important lessons in general too.
You may be saying at this point, \But our program is obviously
orre
t, after all didn't
we design it so that the variable result has the desired value?" Unfortunately, it isnt so
simple: mistakes
an
reep in at any stage. Let me
onfess that when I rst wrote the
program of Figure 4.1, I forgot to initialize the variable i. As a result, I was getting very
strange answers. Forgetting to initialize a variable is a \silly" mistake, but it is very easy to
make silly mistakes! This is an important humbling lesson that programming tea
hes you.
Note that if our program is doing something serious, say
ontrolling an air
raft in
ight, a
mistake of any kind
an
ause a
rash. So we must learn to avoid even silly mistakes.
So as a
ross
he
k, we will try to anyway prove the
orre
tness of the program after
we have nished writing it. In this proof, we will basi
ally
he
k whether our program is
adhering to our plan, i.e. we
onrm whether the variables indeed take the values we expe
t
them to take. The proof is based on mathemati
al indu
tion. The indu
tion hypothesis is
what we stated as our plan.
Indu
tion Hypothesis: The values of i,term,result on the tth entry to the
loop are respe
tively:
1 +:::+ 1
t; 1=(t 1)!;
0!
(t 1)!
For the base
ase, we
onsider t = 1, i.e. the values on the rst entry. Substituting t = 1 in
the values in the Indu
tion Hypothesis, we see that we want i,term,result to be all 1. But
the
ode before the loop indeed initializes all these variables to 1. Thus we have established
the base
ase. Note that when you do this part of the proof, you will dis
over if you indeed
forgot to initialize any variable.
Next we will assume that the Indu
tion Hypothesis is true on the tth entry, and show
that it must also hold on the t + 1th entry. Thus we need to prove:
76
4.2.3 Invariants
We
ould have
hara
terized the values taken by the variables i, term, result in the
following manner
At the beginning or at the end of any iteration of the repeat loop, let
i; term; result be the values of the variables i, term, result. Then these values
satisfy the following relationships.
term = 1=(i 1)!;
result = 1=0 + 1=1! + + 1=(i 1)!
Noti
e that this statement is independent of whi
h iteration is being
onsidered. Su
h statements are
alled loop invariants, and these are more natural in other
ontexts (Se
tion 7.8.1).
4.3 Debugging
Unless you are one of the lu
ky/
lever few, it is inevitable that the programs you write will
not work on the rst try. You will quite likely forget a semi
olon or make some other mistake
be
ause of whi
h the
ompiler will
omplain. The
ompiler will usually state the line number
in whi
h the error is present, so generally it will be easy to
orre
t your mistake. But even
after your program
ompiles
orre
tly, it is possible that it will produ
e the wrong answers.
What do you do in that
ase?
77
Clearly you must go over your entire pro
ess of design. Did you get the spe
i
ations
right? Have you forgotten to initialize a variable? After these basi
he
ks, you should
turn to the plan you wrote. In the plan you have essentially written down how you expe
t
the variable values to
hange in the dierent iterations. So
onsider putting down print
statements whi
h print out the values of the variables in ea
h iteration. Then you will have
to
al
ulate by hand if they are as you expe
t them to be. We will see some short
uts for
this later, but basi
ally this is what you need to do.
You may be tempted to say that your program is
orre
t but the
omputer is making a
mistake { but
omputers make mistakes so rarely that this possibility
an be safely ignored.
We have remarked earlier that programs should be written not only so that they
an be
ompiled and exe
uted to solve problems, but also so that they
an be easily understood by
other programmers.
There are several ways to make a program easier to understand. Most of these ways
involve putting in appropriate
omments in
ode. For example, the spe
i
ations should be
written down in the
omments. Another way is to
hoose good names for the variables so
that the names
onvey the purpose. In addition, you
ould write a
omment along with the
denition of the variable.
A very important aid to understandability is explaining the plan for a loop. The plan
should be des
ribed in enough detail, so that it should be possible to understand the progress
made in ea
h iteration towards the nal goal. Later on we will suggest other (related) ways
of explaining loops, e.g. invariants and potential (Se
tion 7.8).
78
Next you need to identify repetitive patterns in the
omputation, de
ide what variables
to use, write down an overall plan and then write the a
tual
ode.
Testing your program is extremely important. We will say more on the subje
t later.
However, for now, try testing on many values. As you
an see, it is useful to work out what
results you expe
t using pen
il and paper, at least for a few
ases.
We also gave an introdu
tion to the pro
ess of proving the
orre
tness of programs.
Proving programs to be
orre
t turns out to be too tedious for large programs. However,
for small programs, proving
orre
tness is very useful, and you will see several examples of
it in the book. When you prove a program, you are basi
ally reasoning about how values
are assigned to the variables in the program so that the program slowly but steadily makes
progress towards
omputing what it needs to. This progress is made pre
ise in the plan (or
invariant as we will dis
uss later on) that we wrote down. Even if you dont bother to prove
your programs
orre
t, we strongly re
ommend that you write the plan for ea
h non-trivial
loop in your program. Just the a
t of writing the plan in detail will help you to get a
orre
t
program. The plan must be pla
ed in the program as
omments. This will also make your
program more understandable to others who might read it. Often, you
an rst write the
plan and then the
ode, as we just did.
Do everything you
an that will in
reases your
onden
e that your program is
orre
t.
Remember, a wrong program is not just useless, it is potentially dangerous.
4.5.1 A note on programming exer
ises
Programming exer
ises form a big part of learning to program. Programming
annot be
learnt just by reading: pra
ti
e is extremely important. So please write as many programs
as possible. Follow the guidelines suggested in this
hapter while writing programs.
( 1)k kr!!
k
In
identally, D(r) is the number of ways in whi
h the numbers 1 through r
an be
arranged in a sequen
e su
h that i is never in the ith position, for all i.
2. Here is an innite produ
t whi
h
an be shown to approa
h 2= as the number of terms
in
reases.
p p p q2 + p2 + p2
2 = 2 2+ 2
2
2
2
Write a program that
omputes the produ
t of the rst n terms, where n is spe
ied as
input. You will need to spe
ify what values your variables take after
t iterations.
q some
p
p
For
this
feel
free
to
write
something
like
\
numerator has value 2 + : : : + 2 with
p appearing t times". Write a proof of
orre
tness.
D(r) =
r
X
=0
79
main_program{
int n, fa
=1, i=2;
double e=1.0;
in >> n;
main_program{
int n, fa
=1, i=1;
double e=1.0;
in >> n;
repeat(n){
e = e + 1.0/fa
;
fa
= fa
* i;
i = i + 1;
}
out << e << endl;
repeat(n){
e = e + 1.0/fa
;
fa
= fa
* i;
i = i + 1;
}
out << e << endl;
(b)
(a)
Figure 4.2: One of these programs is in
orre
t.
3. Write a program to approximately
ompute ex by adding rst 15 terms of the series
ex =
x0
x x x
+
0! 1! + 2! + 3! + : : :
1
4. Write a program that
omputes the value of an nth degree polynomial A(x) = a +
a x + a x + : : : + an xn . Assume that you are given n then the value x, and then the
oe
ients a ; a ; : : : ; an.
5. Evaluate the polynomial, but this time assume that you are given the
oe
ients in
the order an; an ; : : : ; a .
6. Figure 4.2 gives two programs to
ompute e. One of them is in
orre
t. Find whi
h
one. For the
orre
t program, give appropriate invariants and prove its
orre
tness.
7. Write a program whi
h multiplies an n digit number M by a 1 digit number d, where
n
ould be large, e.g. 1000. The input will be given as follows. First the user gives
d, then n and then the digits of M , starting from the least signi
ant to the most
signi
ant. The program must print out the digits of the produ
t one at a time, from
the least signi
ant to the most signi
ant.
The program you write will likely perform about n multipli
ation operations and a
similar number of other operations. There is a more e
ient way of writing this
program, i.e. using fewer operations for multiplying the same numbers M; d. Hint:
Ask the user to give several digits of M at a time.
1
Chapter 5
Simple
pp graphi
s
The graphi
s
ommands we introdu
ed in Chapter 1 are fun, but quite limited. The more
general graphi
s system that we dis
uss in this
hapter has many other features:
Ability to have several turtles on s
reen simultaneously, moving and drawing as desired.
Ability to
reate other shapes, e.g. lines, re
tangles,
ir
les, polygons and text on the
s
reen and move these shapes as desired. The shapes also have pens, so they
an also
draw on the s
reen if needed.
Ability to
hange attributes su
h as
olour, size of the various shapes.
The graphi
s window is asso
iated with a Cartesian
oordinate frame, and the positioning and movement of shapes
an be spe
ied by giving the
oordinates on the s
reen,
rather than always having to be spe
ied relative to the position and the orientation
of the obje
t in question.
Elementary graphi
al input. The user
an
li
k on the graphi
s window and the program
an wait for su
h
li
ks and get the
li
k
oordinates.
After dis
ussing the dierent features of the graphi
s system, we will give two examples.
The rst is somewhat simple: plotting the traje
tory of a proje
tile as it moves under the
in
uen
e of gravity. The se
ond is more involved. In this the user
an
li
k a set of points
on the s
reen, and the program will draw the best t straight line through the points, under
a
ertain measure of goodness.
You will note that it is easier to draw some kinds of pi
tures by making a turtle tra
e
over them. However, for many other kinds, it is more
onvenient to spe
ify the
oordinates
dire
tly. You will see examples of both in the
hapters to
ome.
In a later
hapter, we will present some additional graphi
s related features.
5.1 Overview
To a
ess the more general graphi
s fa
ilities, it is more
onvenient to use the
ommand:
initCanvas();
80
81
rather than turtleSim(). This opens a window, but does not
reate a turtle at its
enter.
Commands
anvas width(),
anvas height() return the width and height of the
anvas
in pixels. You may also invoke the
ommand as
initCanvas(name,w,h)
where name is a quoted string meant to be the name given for the
anvas, and w,h should
indi
ate the width and height you desire for the
anvas window.
5.1.1 y axis goes downward!
A
oordinate system is asso
iated with the
anvas window. You may nd it slightly unusual:
the origin is at the top left
orner, and x
oordinates in
rease rightward, and y
oordinates downward. The
oordinates are measured in pixels. Note however that internally,
simple
pp
onsiders the
oordinates of obje
ts to be real numbers of type double. These
real
oordinates are
onverted to integers only when needed for the purpose of displaying
the obje
ts.
Turtle t1,t2,t3;
This will
reate 3 turtles, respe
tively named t1, t2, t3 at the
enter of the window
reated
using initCanvas(). Yes, the turtles will all be at the
enter, sta
ked one on top of the
other. We next see how we get them untangled.
The basi
idea is: any
ommand you used in Chapter 1 to ae
t the turtle will also work
with these turtles, but you must say whi
h turtle you are ae
ting. For this, you must write
the
ommand following the name of the turtle, the two joined together by a dot: \.". Thus,
to move turtle t1 forward by 100 steps, we merely write:
t1.forward(100);
82
repeat(8){
t1.forward(100);
t2.forward(100);
t3.forward(100);
t1.left(360.0/8);
t2.left(360.0/8);
t3.left(360.0/8);
}
wait(5);
Three other shapes are allowed besides turtles:
ir
les and axis-parallel re
tangles, and
straight line segments. Text is also
onsidered to be a kind of shape. Later in Se
tion 14.2.4
we will dene a polygon shape.
5.3.1 Cir
les
Cir
les
an be
reated by writing:
Cir
le
1(
x,
y,r);
Here,
x,
y,r must be numeri
al expressions whi
h indi
ate the radius of the
ir
le, and
the x and y
oordinates of its
enter. The
reated
ir
le is named
1.
5.3.2 Re
tangles
An axis parallel re
tangle is dened as follows
Re
tangle r1(
x,
y,Lx,Ly);
where
x,
y should give the
oordinates of the
enter, and Lx,Ly the width and height
respe
tively. The
reated re
tangle has name r1.
5.3.3 Lines
A line segment
an be dened as:
Line line1(x1,y1,x2,y2);
This
reates a line named line1 where x1,y1 are the
oordinates of one endpoint, and x2,y2
the
oordinates of the other.
83
5.3.4 Text
If we want to write text on the s
reen, it is also
onsidered a kind of shape. The
ommand
Text t1(x,y,message);
in whi
h x,y are numbers and message is a text string
an be used to write the message on
the s
reen. So you might use the
ommand Text txt(100,200,"C++"); to write the text
C++ on the s
reen
entered at the position (100,200). Another form is:
Text t2(x,y,number);
Here number
an be a numeri
al expression. The value of the expression at the time of
exe
ution of this statement will
omprise the text. It will be
entered at the
oordinates
(x,y).
The
ommand textWidth
an be used to nd the width of the given text in pixels. For
example textWidth("C++") returns the width of the text \C++" when drawn on the
anvas.
The
ommand textHeight() returns the height in pixels. Thus the following pie
e of
ode
an be used to write text and put a snugly tting box around it.
Text t(100,100,"C++ g++");
Re
tangle R(100,100,textWidth("C++ g++"),textHeight());
If for some reason you wanted to know by how mu
h the lower part of \g" des
ends below the
line on whi
h the text gets written, you
an know this using the
ommand textDes
ent().
Ea
h shape mentioned above
an be made to move forward and rotate, ex
ept for Text
shapes, whi
h
annot be rotated. Ea
h shape also has a pen at its
enter whi
h
an be either
up or down.
In addition, for any shape s, we have the
ommands
s.moveTo(x,y);
s.move(dx,dy);
where the former moves the shape to
oordinates (x,y) on the s
reen, and the latter displa
es
the shapes by (dx,dy) from its
urrent position.
You
an
hange the size of a shape (ex
ept for text) also. Every obje
t maintains a s
ale
fa
tor, whi
h is initially set to 1, based on whi
h its size is displayed.
s.s
ale(relfa
tor);
s.setS
ale(fa
tor);
Here relfa
tor, fa
tor are expe
ted to be double. The rst version multiplies the
urrent
s
ale fa
tor by the spe
ied relfa
tor, the se
ond version sets the s
ale fa
tor to fa
tor.
You
an also de
ide whether a shape s is to appear in outline, or it is to be lled with
some
olor. For the former, use the
ommand
84
s.setFill(v);
where v must evaluate to true or false. This
ommand does not apply to Line shapes. The
olor
an be spe
ied by writing:
s.setColor(
olor);
where
olor is spe
ied for example, as COLOR("red"). Note that merely spe
ifying "red"
will not work. Instead of red, other standard
olor names, e.g. blue, green, yellow, white,
bla
k
an be used. Use all lower
ase letters. Alternatively, you may spe
ify the
olor by
giving intensities of 3 primary
olors, red, green, blue respe
tively, by writing COLOR(redVal,
greenVal, blueVal). The 3 values should be numbers between 0 and 255. As you may
guess, red and blue together give purple, while red and green give yellow.
You may hide or unhide a shape s using the
ommands
s.hide();
s.show();
respe
tively.
5.4.1 Rotation in radians
The left, right
ommands of Chapter 1 required angles to be supplied in degrees. However, most programming languages in
luding C++ prefer angles to be represented in radians.
For this a rotate
ommand is provided. Thus if s is a shape you may write s.rotate(angle)
where angle must be the angle in radians, measured
lo
kwise.
5.4.2 Tra
king a shape
As the program exe
utes, you may move shapes or rotate them or s
ale them. You
an of
ourse keep tra
k of the position, orientation, s
ale fa
tor yourself, but you do not need to;
simple
pp does it for you. The following
ommands will return the x
oordinate, the y
oordinate, the orientation, and the s
ale fa
tor respe
tively.
s.getX()
s.getY()
s.getOrientation()
s.getS
ale()
You may print the values by writing
out << s.getOrientation(); and so on, or you may
use them in
omputation. The getOrientation
ommand will return the angle made by
the shape with the positive x axis, measured
lo
kwise.
5.4.3 Imprinting on the
anvas
Suppose s is a shape. Then the following
ommand
auses an image of the shape to be
printed on the
anvas, at the
urrent position of s.
s.imprint();
85
After this, the shape might move away, but the image stays permanently. You
an print as
many images of a single shape as you desire. The new image overwrites older images, if any.
The
ommand works with all shapes s.
If you merely want to draw lines on the s
reen for some reason (e.g. Se
tion 18.3) an
additional
ommand is also provided.
imprintLine(x1,y1,x2,y2,
olor)
or
imprintLine(x1,y1,x2,y2)
This will draw a line between the points (x1,y1) and (x2,y2), of
olour
olor. If
olor is
not given, then the line will be bla
k. You
ould have got the same ee
t by
reating a line
and then
alling imprint on it; however, the
ommand imprintLine is mu
h faster. The
speed is sometimes important, as in the appli
ation of Se
tion 18.3.
5.4.4 Resetting a shape
For ea
h shape ex
ept Turtle, an reset
ommand is provided. This
ommand takes the
same arguments as required for
reation, and re
reates the shape using the new values. For
example, you
ould have
Cir
le
(100,100,15);
wait(1);
.reset(100,100,20);
The
ommand getCli
k()
an be used to wait for the user to
li
k on the
anvas. It
auses
the program to wait until the user
li
ks. Suppose the user
li
ks at a point (x; y) on the
s
reen. Then the value v = 65536 x + y is returned by the
ommand. Note that the
li
k
is
onsidered to be happening on some pixel, i.e. the
oordinates x; y of the
li
k position
are integers. The value returned by getCli
k() is also of type int.
Note that standard
omputer s
reens will have at most a few thousand pixels along the
height and along the width. Thus the
li
k
oordinates x; y will at most be a few thousand.
Thus x; y < 65536. So if you are given v = 65536 x + y, then you
an re
over x; y by noting
that
x = bv=65536
;
y = v mod 65536
As an example, the following program waits for the user to
li
k, and then prints out the
oordinates of the point at whi
h the user
li
ked.
main_program{
int
li
kPos;
86
initCanvas();
li
kPos = getCli
k();
By the way, 65536 = 2 ; so when you
ompute v = 65536 x + y, you are ee
tively pla
ing
the x
oordinate in the most signi
ant 16 bits of v and y in the least signi
ant 16.
16
We will now write a program that simulates the motion of a proje
tile. Suppose that the
proje
tile has initial velo
ity 1 pixel per step in the x dire
tion, and -5 pixels per step in the
y dire
tion (note that the y
oordinate grows downward, so this is upward velo
ity). Let
us arbitrarily x the gravitational a
eleration to be 0.1 pixels/step . For simpli
ity assume
that the velo
ity only
hanges at the end of ea
h step: at the end of ea
h step 0.1 gets added
to the y
omponent velo
ity.
2
repeat(100){
proje
tile.move(vx,vy);
vy += gravity;
wait(0.1);
}
wait(10);
The program waits for the user to
li
k. It then pla
es a proje
tile, a Cir
le at the
li
k
position. Then it moves the proje
tile as per its velo
ity. The pen of the proje
tile is put
down so that the path tra
ed by it is also seen. The proje
tile is moved for 100 steps.
87
Suppose you are given a set of points (x ; y ); (x ; y ); : : : ; (xn; yn). Your goal is to nd a line
y = mx +
whi
h is the
losest to these points. We will see a way to do this assuming a
spe
i
denition of \
losest".
A natural denition of the distan
e from a point to a line is the perpendi
ular distan
e.
Instead, we will
onsider the \verti
al" distan
e yi mxi
. We will try to minimize the
total distan
e of all points from our line; a
tually, sin
e the quantity yi mxi
an be
positive or negative, we will instead minimize the sum of the squares of these quantities, i.e.
1
n
X
min (yi
)2
mxi
i=1
Basi
ally we have to sele
t m;
su
h that the above quantity is minimized. At the
hosen
value of m, the above sum must be smallest, i.e. the derivative of the sum must be 0.
0 = m
n
X
i=1
(yi
)2 =
mxi
n
X
i=1
xi (yi
mxi
(5.1)
We
an get another equation by asserting that the quantity to be minimized be
ome as small
as possible for the
hosen value of
, or at that value the derivative must be
ome 0:
0=
n
X
(y
i=1 i
mxi
= 2 (yi
X
i
n
X
xi + n =
mxi
i=1
(5.2)
yi
88
+=
+=
+=
+=
x*x;
x;
x*y;
y;
}
double m = (n*r - q*s)/(n*p - q*q);
double
= (p*s - q*r)/(n*p - q*q);
Line l(0,
, 500, 500*m+
);
wait(10);
If it appears to you that dening shapes is like dening variables, you would be right! Indeed,
statement su
h as:
does indeed dene two variables,
1 and
2. The
ommands dis
ussed above are invoked on
these variables, and as a result they
ause the images on the s
reen to be
hanged. But from
the view of the C++
ompiler,
1,
2 are in fa
t variables.
Just as ordinary variables
an be dened inside repeat loops, so
an these shapes. But
just as ordinary variables will get destroyed on
e we get to the end of the parent blo
k, so
will these shapes.
Further, the names of the shapes, Cir
le, Re
tangle, Line, Turtle in fa
t are the
data types of the
orresponding variables. These are spe
ial data types
reated for simple
pp.
C++ allows
reation of data types su
h as these. We will study this in Chapter 15. For
now, you
an just use them.
1. Draw an 8 8
hessboard having red and blue squares. Hint: Use the imprint
ommand. Use the repeat statement properly so that your program is
ompa
t.
2. Plot the graph of y = sin(x) for x ranging in the interval 0 to 4. Draw the axes and
mark the axes at appropriate points, e.g. multiples of =2 for the x axis, and multiples
of 0.25 for the y axis.
89
3. Modify the proje
tile motion program so that the velo
ity is given by a se
ond
li
k.
The proje
tile should start from the rst
li
k, and its initial velo
ity should be in the
dire
tion of the se
ond
li
k (relative to the rst). Also the velo
ity should be taken
to be proportional to the distan
e between the two
li
ks.
4. Another idea is to treat the se
ond
li
k to be the highest point rea
hed by the proje
tile
as it moves. For this you may note that if ux; uy are the initial velo
ities of the
proje
tile in the x; y dire
tions, and g the gravitational a
eleration, then maximum
height rea
hed is ugy . The horizontal distan
e
overed by the time the maximum height
is rea
hed is uxguy .
5. Modify the proje
tile motion program to tra
e the traje
tories of the proje
tile for the
same initial velo
ity and dierent angles. As you may know, for a xed velo
ity, the
proje
tile goes farthest if it is laun
hed at 45 degrees to the horizontal. You should be
able to verify this statement using your program.
6. Write a program to produ
e the following ee
t. First a square appears on the s
reen.
Then a tiny
ir
le appears at the
enter. Slowly, the
ir
le grows until it tou
hes the
sides of the square. Then both the
ir
le and the square start shrinking until they
vanish.
7. Suppose you are given some observed positions of a proje
tile. Ea
h position is an
(x; y) pair. You are further told that the proje
tile is surely known to pass through
the origin (0,0). Derive the best t traje
tory for the given points, su
h that it passes
through (0,0). For this you will have to adapt the pro
ess we followed to t a straight
line.
8. Write a program that a
epts 3 points on the
anvas (given by
li
king) and then draws
a
ir
le through those 3 points.
9. Write a program that a
epts 3 points, say p; q; r. Then the program draws the line
joining p; q. Then the line is rotated around the point r, slowly, through one full
rotation. The key question here is how to rotate a line through a point whi
h is not
its
enter. This
an be done in two ways. You
ould
al
ulate the next position of the
line, and then reset the line to that position. Alternatively, you
an observe that a
rotation about an external point su
h as r
an be expressed as a rotation about the
enter and a translation, i.e. a move. This will require you to
al
ulate the amount of
translation.
10. In this problem you are to determine how light re
e
ts o a perfe
tly re
e
ting spheri
al
surfa
e. Suppose the sphere has radius r and is
entered at some point (x; y). Suppose
there is a light sour
e at a point (x0 ; y). Rays will emerge from the sour
e and boun
e
o the sphere. As you may know, the re
e
ted ray will make an angle to the radius at
the point of
onta
t equal to that made by the in
ident ray. Write a program whi
h
tra
es many su
h rays. It should take r; x; y; x0 as input. Of
ourse, in the plane the
sphere will appear as a
ir
le.
2
90
11. This is an extension to the previous problem. Extend the re
e
ted rays ba
kward till
they meet the line joining the
ir
le
enter and light sour
e. The points where the rays
meet this line
an be said to be the image of the light sour
e in the mirror; as you will
see this will not be a single point, but the image will be diused. This is the so
alled
spheri
al aberration in a
ir
ular mirror.
Chapter 6
Conditional Exe
ution
Suppose we want to
al
ulate the in
ome tax for an individual. The a
tual rules of in
ome
tax
al
ulation are quite
omplex. Let us
onsider very simplied rules as follows:
For males, there is no tax if your in
ome is at most Rs. 1,80,000. If your
in
ome is between Rs. 180,000 and Rs. 500,000 then you pay 10% of the amount
by whi
h your in
ome ex
eeds Rs. 180,000. If your in
ome is between Rs. 500,000
and Rs. 800,000, then you pay Rs. 32,000 plus 20% of the amount by whi
h your
in
ome ex
eeds Rs. 500,000. If your in
ome ex
eeds Rs. 800,000, then you pay
Rs. 92,000 plus 30% of the amount by whi
h your in
ome ex
eeds Rs. 800,000.
In the programs that we have written so far, ea
h statement was exe
uted on
e, or ea
h
statement was exe
uted a
ertain number of times, as a part of a repeat blo
k. The statements that we have learned do not allow us to express something like \If some
ondition
holds, then exe
ute a
ertain statement, otherwise exe
ute some other statement.". This
onditional exe
ution is required for the tax
al
ulation above.
The main statement whi
h expresses
onditional exe
ution is the if statement. We will
also dis
uss the swit
h statement, whi
h is sometimes more
onvenient. We also dis
uss
logi
al data, and how it
an be stored in the bool type.
We rst give the program whi
h
al
ulates the tax, and then explain ea
h statement.
main_program{
float in
ome; // in rupees.
float tax;
// in rupees.
out << "What is your in
ome in rupees? ";
in >> in
ome;
if(in
ome <= 180000) tax = 0;
if((in
ome > 180000) && (in
ome <= 500000))
tax = (in
ome - 180000)* 0.1;
91
// first if statement
// se
ond if statement
92
Previous Statement
True
Condition
Consequent
False
Next Statement
// third if statement
// fourth if statement
This program uses the simple form of the if statement, whi
h is as follows.
if (
ondition)
onsequent
In this the
ondition must be an expression whi
h evaluates to true or false. We will
soon des
ribe how su
h expressions
an be written. In any
ase, the exe
ution of the if
statement begins with the evaluation of the
ondition expression. If it evaluates to true,
then the
onsequent, whi
h
an be any C++ statement, is exe
uted. If the
ondition
evaluates to false, then the
onsequent is ignored. At this point the exe
ution of the if
statement ends, and
ontrol passes to the next statement in the program. Pi
torially, this
is often shown in the form of a
ow
hart, Figure 6.1. In this gure, boxes are used to hold
statements to be exe
uted, or a
tions to be performed. It is
ustomary to write
onditions
inside diamonds. Lines join the boxes and diamonds showing how
ontrol
an
ow. As you
an see, after evaluating the
ondition, either the true bran
h is taken, in whi
h
ase the
onsequent is exe
uted, or the false bran
h is taken in whi
h
ase the
ontrol dire
tly goes
to the next statement.
The simplest form of
ondition is as follows.
exp1 relop exp2
where exp1 and exp2 are numeri
al expressions, and relop is a relational operator, e.g.
<,>,<=,>=,==,!= whi
h respe
tively stand for less than, greater than, less than or equal,
greater than or equal, equal, and not equal. Thus in the rst if statement in the program,
in
ome <= 180000 is a
ondition. If during exe
ution, the value of the variable in
ome is
at most 180000, then the
ondition evaluates to true, and the
ondition is said to su
eed.
If so the
onsequent is exe
uted. Thus tax is set to 0. If in
ome is greater than 180000,
93
the
ondition evaluates to false, and is said to fail. In this
ase the
onsequent is not
exe
uted, i.e. tax remains un
hanged. Similarly, in the last if statement, the
ondition
is in
ome > 800000. The
onsequent here, tax = 92000 + (in
ome - 800000) * 0.3 is
exe
uted if and only if the value of in
ome is greater than 800000.
It is possible to spe
ify a more
omplex
ondition in the if statement. For example,
you may wish to perform a
ertain operation only if some two
onditions are both true. In
other words, you want
ondition1 to be true and
ondition2 to be true. Thus our
ondition
an be a
onjun
tion (and) of two or more
onditions. This is written as follows.
ondition1 &&
ondition2 && ... &&
onditionn
The
hara
ters && should be read as \and". In our se
ond if statement, we have an example
of this. Here, the
ompound
ondition is true only if both the sub
onditions, in
ome >
180000 and in
ome <= 500000 are true. In other words, the
ompound
ondition is true
only if the in
ome is between 180000 (ex
lusive) and 500000 (in
lusive). Only in this
ase
is the tax set to (in
ome - 180000)* 0.1, i.e. 10% of the amount by whi
h the in
ome
ex
eeds 180000.
Note that we
an have a
ompound
ondition whi
h holds if at least one of some set
of
onditions holds. Su
h a
ondition is said to be a disjun
tion of (sub)
onditions and is
expressed as:
1
The
hara
ters ||,
onstitute the logi
al or operator, i.e. the
ompound
ondition is true
if
ondition1 is true or
ondition2 is true and so on. Finally, one
ondition
an be the
negation of another
ondition, written as follows:
!
ondition
where the
ondition !
ondition is said to be the negation of
ondition. The
ondition
!
ondition is true if
ondition is itself false, and !
ondition is false if
ondition is true.
We note that the se
ond if statement
an also be written as:
if(!((in
ome <= 180000) || (in
ome > 500000)))
tax = (in
ome - 180000)* 0.1;
Noti
e that (in
ome <= 180000) || (in
ome > 500000) is true if in
ome is either less
than or equal to 180000 or greater than 500000, i.e. if the in
ome is not in the range 180000
(ex
lusive) and 500000 (in
lusive). But the ! at the beginning negates this
ondition, so
the entire
ondition is true only if the in
ome is indeed in the range 180000 (in
lusive and
500000 (ex
lusive). But this is the same
ondition as tested in the se
ond if statement in
the program!
It is important to
learly understand how the above program is exe
uted. The exe
ution
is as usual, top to bottom. After printing out a message and reading the value of in
ome,
the program exe
utes the rst if statement. For this the
ondition in it is
he
ked, and
then the
onsequent is exe
uted if the
ondition is true. After this the se
ond if statement
is exe
uted. So every if statement will be exe
uted; the
onditions have been so designed
1
The single hara ter & is also an operator, but it means something dierent, see Appendix E.
94
Read income
True
income <= 180000
Tax = 0;
False
True
False
True
tax = 32000 +
(income 500000)*0.2;
False
True
income > 800000
tax = 92000 +
(income 800000)*0.3;
False
Print tax
Figure 6.2: Flow hart for the rst in ome tax program
95
so that the
ondition in only one if statements will evaluate to true, and hen
e only one
onsequent statement will be exe
uted. Perhaps the way in whi
h
ontrol
ows is more
obvious in the
ow
hart of the entire program, shown in Figure 6.2. Note that on
e we
dis
over a
ertain
ondition to be true, e.g. that the in
ome is at most 180000, we know that
the other
onditions
annot be true. So the natural question arises: why should we even
he
k them?
The more general if statement, dis
ussed in the next se
tion, allows you to prevent su
h
unne
essary
he
ks. But before dis
ussing that, we dis
uss the notion of blo
ks.
6.2 Blo ks
In the if statement dis
ussed above, the
onsequent was expe
ted to be a single statement.
In general, we might want to exe
ute several statements if a
ertain
ondition held, not just
one. The blo
k
onstru
t helps us in this
ase.
As dis
ussed earlier, a blo
k is simply a
olle
tion of statements that are grouped together
in bra
es, f and g. By putting statements into a blo
k, we are making a single
ompound
statement out of them. A blo
k
an be pla
ed wherever a single C++ statement is required,
e.g. as the
onsequent part of the if statement. Suppose for example, we want to print a
message \This person is in the highest tax bra
ket." if the in
ome is more than 8 lakhs, as
well as
al
ulate the tax, we would repla
e the fourth if statement in the program with the
following.
if (in
ome > 800000){
tax = 92000+(in
ome - 800000)* 0.3;
out << "This person is in the highest tax bra
ket." << endl;
}
You have already used a blo
k as a part of the repeat statement. Let us now note that the
general form of the repeat statement is:
repeat (
ount) a
tion
96
Previous Statement
True
False
Condition
Alternate
Consequent
Next Statement
This statement is exe
uted as follows. First,
ondition1 is
he
ked. If it is true, then
onsequent1 is exe
uted, and that
ompletes the exe
ution of the statement. If
ondition1
is false, then
ondition2 is
he
ked. If it is true, then
onsequent2 is exe
uted, and that
ompletes the exe
ution of the statement. In general,
ondition1,
ondition2, ... are
exe
uted in order, until some
onditioni is found to be true. If so, then
onsequenti
is exe
uted, and the exe
ution of the statement ends. If no
ondition is found true, then
the alternate is exe
uted. It is a
eptable to omit the last line, i.e. else alternate. If
the last line is omitted, then nothing is exe
uted if none of the
onditions are found true.
Figure 6.4 shows a
ow
hart, for 3
onditions.
Now we
an rewrite our tax
al
ulation program as follows.
main_program{
float in
ome, tax;
out << "What is your in
ome? ";
in >> in
ome;
if(in
ome <= 180000) tax = 0;
else if(in
ome <= 500000)
// new first if
// new se
ond if
97
Previous Statement
True
False
Condition 1
Consequent 1
True
False
Condition 2
Consequent 2
False
Alternate
True
Condition3
Consequent 3
Next Statement
Noti
e that this program
ontains only 3
onditions, rather than 4 as in the previous program.
This is be
ause if all the 3
onditions are false, we know that the in
ome must be bigger
than 800000. Thus even without
he
king this
ondition we
an dire
tly set the tax to
92000+(in
ome - 800000)* 0.3.
Also note that the se
ond and third
onditions are mu
h simpler! In the rst program,
we
he
ked if the in
ome was larger than 180000 and at most 500000. In the new program,
we know that the \new se
ond if" is exe
uted only if the
ondition of \new rst if" failed,
i.e. if the in
ome was greater than 180000. But then, we dont need to
he
k this again in
the \new se
ond if". So it su
es to just
he
k if in
ome is at most 50000. The third if
statement also simplies similarly.
Further note that the original program would
he
k ea
h of its 4
onditions no matter
whi
h one is true, whereas in this program as soon as the rst true
ondition is found,
the
orresponding
onsequent a
tion is performed, and the subsequent
onditions are not
he
ked. Thus the new program is more e
ient than the previous program. Figure 6.5
shows the
ow
hart for the new program. By
omparing to Figure 6.2, perhaps it is easier
98
Read income
True
False
income <= 180000
tax = 0;
True
False
income <= 500000
False
income <= 800000
tax = 92000 +
(income 800000)*0.3;
tax = 32000 +
(income 32000)*0.2;
print tax
The turtle driving programs we saw in
hapter 1 required us to put information about the
gure we wanted to draw right into program, i.e., the exa
t sequen
e of forward and turn
ommands that we want to exe
ute had to be written out in the program. We will now write
a program whi
h will allow the user to
ontrol the turtle during during exe
ution.
Let us de
ide that the user must type the
hara
ter 'f' to make the turtle go forward by
100 pixels, the
hara
ter 'r' to make the turtle turn right by 90 degrees, and the
hara
ter 'l'
to make the turtle turn left by 90 degrees. Our program must re
eive these
hara
ters that
the user types, and then move the turtle a
ordingly. Here it is.
main_program{
har
ommand;
turtleSim();
repeat(100){
in >>
ommand;
if (
ommand == 'f')
else if (
ommand ==
else if (
ommand ==
else
out << "Not a
forward(100);
'r') right(90);
'l') left(90);
proper
ommand, " <<
ommand << endl;
99
Remember that
har data is really numeri
al, so it is perfe
tly a
eptable to
ompare it
using the operator ==. This program will exe
ute 100 user
ommands to move the turtle
before stopping. Try it!
6.4.1 \Buttons" on the
anvas
We
an build \buttons" on the
anvas using the Re
tangle shapes of Se
tion 5.3. We
an
ontrol the turtle by
li
king on the buttons. This gives yet another turtle
ontroller.
main_program{
initCanvas();
onst float bFx=150,bFy=100, bLx=400,bLy=100, bWidth=150,bHeight=50;
Re
tangle buttonF(bFx,bFy,bWidth,bHeight), buttonL(bLx,bLy,bWidth,bHeight);
Text tF(bFx,bFy,"Forward"), tL(bLx,bLy,"Left Turn");
Turtle t;
repeat(100){
int
li
kPos = getCli
k();
int
x =
li
kPos/65536;
int
y =
li
kPos % 65536;
if(bFx-bWidth/2<=
x &&
x<= bFx+bWidth/2 &&
bFy-bHeight/2 <=
y &&
y <= bFy+bHeight/2) t.forward(100);
The program begins by drawing the re
tangles on the s
reen. Noti
e that we have not given
the
oordinate information of the buttons by writing numbers dire
tly, but rst
reated the
names bFx,bFy and so on having spe
i
values and then used these names in the button
reation. Using su
h names is
onvenient: if you want to adjust the layout of buttons later,
you just need to
hange the value of some name. Without names, you would have needed to
make
hanges in every pla
e the number appeared. In the present
ase, if you want to
hange
the width of the re
tangles, you just need to assign a dierent value to bWidth, instead of
worrying in whi
h all pla
es the width value needs to be
hanged.
Next, text is put in the re
tangles. Then we go into a loop. Inside, we wait for the user
to
li
k. We
he
k whether the
li
k is inside either of the two re
tangles. This is done in the
two if statements in the loop. Ea
h
he
k has two parts: we must
he
k if the x
oordinate
of the
li
k is between the left edge of the re
tangle and the right edge, i.e. the left edge
100
oordinate must be smaller or equal, and the right edge
oordinate must be larger or equal.
And
orrespondingly we must
he
k for the y
oordinate as well.
This program will only allow 100
li
ks; we see later how to make the loop indenitely
or stop if some
ondition is met.
In the turtle
ontrol program, there was a single variable,
ommand, depending upon whi
h
we took dierent a
tions. A similar situation arises in many programs. So C++ provides
the swit
h statement so that we
an express our
ode su
in
tly. The general form of the
swit
h statement is:
swit
h (expression){
ase
onstant1:
group(1) of statements usually ending with ``break;''
ase
onstant2:
group(2) of statements usually ending with ``break;''
...
default:
default-group of statements
}
The portion
onsisting of default: and the group of statements following that is optional.
The expression expression must be of type int. Further ea
h
onstati in above is required
to be an integer
onstant.
The statement exe
utes in the following manner. First the expression is evaluated.
If the value is identi
al to
onstanti for some i, then we start exe
uting group(i) statements. We exe
ute group(i) statements, then group(i+1) statements and so on, in
luding
default-group statements, unless we en
ounter a break; statement. If we en
ounter a
break then the exe
ution of the swit
h is
omplete, i.e. we do not exe
ute the statements
following the break but dire
tly go to the statement in the program following the swit
h
statement. If the value of expression is dierent from any of the
onstant values mentioned, then the default-group of statements is exe
uted.
If a
ertain group(i) does not end in a break, then the exe
ution is said to \fall-through"
to the next group. Fall-throughs are
onsidered to be rare.
Using a swit
h our turtle
ontrol program
an be written as follows.
main_program{
har
ommand;
turtleSim();
repeat(100){
in >>
ommand;
swit
h(
ommand){
ase 'f': forward(100);
break;
101
Suppose the input is 5. Then the exe
ution will start after the point labelled
ase 5:. It
will fall through the
ases 5,7,8,10 to
ase 12. In this the number of days will be printed to
be 31, and then a break is en
ountered. This will
omplete the exe
ution of the sele
t.
The swit
h statement is
onsidered somewhat error-prone be
ause you may forget to
write break;. So be
areful.
102
The evaluation of this pro
eeds as follows. First the
ondition is evaluated. If it is true,
then the
onsequent-expression expression is evaluated, and that is the value of the overall
expression. The alternate-expression is ignored. If on the other hand the
ondition
evaluates to false, then the
onsequent-expression is ignored, the alternate-expression
is evaluated and the resulting value is the value of the overall expression.
Here are some simple examples.
int marks;
in >> marks;
int a
tualmarks = (marks > 100) ? 100 : marks;
har grade = (marks >= 35) ? 'p' : 'f';
In this if marks read in were more than 100, then a
tualmarks would be
apped to 100, else
a
tualmarks would be set equal to marks. Further, if the marks are at least 35, then grade
//
**
We merely read in the in
ome, and then
al
ulate the tax as an expression and dire
tly
print it out without storing it into a variable. In the above the parentheses marked ** are
ne
essary. This is be
ause the operator << has higher pre
eden
e than the operator <=, i.e.
by default C++ attempts to exe
ute << before <=.
The above program is very
ompa
t, but not re
ommended. Most programmers would
onsider it unreadable. However, the
onditional expression without nesting is
onsidered to
be a useful
onstru
t.
An important part of the if statement are the
onditions. We have already seen that a
ondition is either true or false, i.e. we
an asso
iate the value true or the value false
103
with ea
h
ondition. We have also seen that
onditions
an be
ombined in dierent ways.
The resulting
ombination will also be true or false. We have also seen that there may be
several equivalent ways of writing the same
ondition (as we saw for the se
ond if statement
of our rst program). In this sense,
onditions are similar to numeri
al expressions, numeri
al
expressions have a value, numeri
al expressions
an be
ombined to build bigger numeri
al
expressions, we
an have numeri
al expressions that are equivalent. In that
ase, why not
treat
onditions, as just another kind of data? This turns out to be a very good idea, and an
algebra for manipulating
onditions, or what we will hereafter refer to as logi
al expressions
was developed by George Boole in 1940. C++ supports the manipulation and storage of
logi
al data, and in honour of Boole the data-type for storing logi
al data is named bool.
You have already seen this data type in Chapter 3, now we will do more interesting things
with it.
First we note that we
an assign values of logi
al expressions to bool variables. Consider
the following
ode.
float in
ome;
in >> in
ome;
bool lowIn
ome, midIn
ome, highIn
ome;
lowIn
ome = (in
ome <= 180000);
midIn
ome = (in
ome > 180000) && (in
ome <= 800000);
highIn
ome = (in
ome > 800000);
Suppose during exe
ution the value 200000 is given for in
ome. Then after the exe
ution
of the subsequent statements, the variables lowIn
ome, midIn
ome, highIn
ome would
respe
tively have the values false, true, false.
As you
an see, the right hand sides of the above assignment statements are
onditions,
and whatever the values these
onditions have will been put in the
orresponding left hand
side variables.
As another example, let us dene a bool variable that will be true if a
hara
ter read
from
in happens to be a lower
ase
hara
ter. Note that this will happen if the ASCII
value of the
hara
ter is at least 'a' and at most 'z'. Thus the
ode for this
ould be
har in_
h;
bool lowerCase;
in >> in_
h;
lowerCase = (in_
h >= 'a') && (in_
h <= 'z');
We will next
onsider a more
omplex program whi
h determines whether a given number
is prime or
omposite. The ability to store logi
al values will be useful in this program.
To understand that program we will need to reason about expressions
ontaining logi
al
data. So we rst dis
uss this.
num
104
if v is true, then true || false is
learly true. If v is false, then false || false is
learly false. Thus false plays the same role with respe
t to || that 0 plays with respe
t
to numeri
al addition. More formally, false is said to be the identity for ||. Likewise true
&& v has the value v for any v. Or in other words, true is the identity for &&.
Another rule is the so
alled distributivity of && over ||. Thus, if x,y,z are boolean
variables (or equivalently,
onditions), then (x && y) || z is the same as (x && z) || (y
&& z). In a similar manner, it turns out that || also distributes over &&.
Another important rule is that x || !x is always true, and hen
e we
an repla
e su
h
expressions with true. Similarly, x && !x
an be repla
ed with false.
Finally, an important rule is DeMorgan's Law. This says that !x && !y is the same as
!(x || y). Similarly !x || !y is the same as !(x && y).
Consider rst a
ondition su
h as in
ome <= 180000. In
ome being at most 180000 is
the same as it not being bigger than 180000. Hen
e we
an write this
ondition also as
!(in
ome > 180000).
While it is ne to be able to intuitively understand that the
onditions
(in
ome >
and
!((in
ome <=
are the same, you should also be able to dedu
e this given the rules given in this se
tion.
6.7.2 Determining whether a number is prime
Determining whether a number is prime is an important problem, for whi
h very sophisti
ated, very fast algorithms are known. We will only
onsider the simplest (and hen
e
substantially slower than the fastest known) algorithms in this book.
Here is the most obvious idea. We go by the denition. A number n is prime if it has no
divisors other than itself and 1. So it should su
e to
he
k whether any number i between 1
and itself (both ex
lusive) divides it. If we nd su
h an i then we de
lare x to be
omposite;
otherwise it is prime.
This requires us to generate all numbers between 2 and x 1 (both in
lusive this time)
so that we
an
he
k whether they divide x. This is really the sequen
e generation pattern
(Se
tion 3.4.1) whi
h we saw, say in the spiral drawing program of Se
tion 3.4. There we
made i take 10 values starting at 1. Now we want i to take the x 2 values from 2 to x 1.
So here is the
ode fragment we should use:
i=2;
repeat(x-2){
/*
Here i takes values from 2 to x-1.
*/
i = i + 1;
}
105
At the end of this found will indeed be true if any of the expressions (x % i) == 0 was true,
for any value of i. Thus following this
ode we simply print prime/
omposite depending upon
whether found is false/true. And at the beginning we need to read in x et
. The
omplete
program is as follows.
main_program{ //De
ide if x is prime.
int x;
in >> x;
int i=2;
bool found = false;
repeat(x-2){
found = found || (x % i) == 0;
i = i+1;
}
106
6.8 Remarks
There is a potential pitfall asso
iated with the use of the operators = and ==. In mathemati
s,
the operator = is used to denote
omparison, and sin
e most of us learn mathemati
s before
programming, we are likely predisposed to use = to mean
omparison even in C++, rather
than ==. This will lead to errors. The situation is more serious than what you might think
at rst glan
e. If you write
ode su
h as
if(p = 25) q = 37;
when you mean if(p == 25) q = 37; the
ompiler will not regard it as an error. This is
be
ause assignment is also an expression, and in this
ase, p = 25, the value is 25. The
ompiler will, on its own, try to
onvert this value to a boolean value. For this the rule
of
onversion is a bit non-intuitive: any non-zero value be
omes true and only 0 be
omes
false. Thus in the exe
ution of the above statement, the assignment q=37 will always
happen.
Many
ompilers
an be asked to warn if they en
ounter su
h statements whi
h most
likely are silly mistakes made by the programmer. Indeed the GNU C++
ompiler will
give a warning if it sees su
h statements in your program, provided you invoke it using the
option -Wparentheses. And in fa
t, s++ whi
h you use with simple
pp indeed
alls the
GNU C++
ompiler with this option, so you will get these warnings already if you
ompile
with s++. If you really intended the statement to mean the assignment expression (and did
not mistakenly write = instead of ==), then you
an merely put the expression inside a pair
of parentheses and write if((p = 25)) q = 37;. This ee
tively de
lares your rm intent
that you mean p = 25 to be an assignment expression. Thus in this
ase no warning will be
issued even if you use the option -Wparentheses.
Another pitfall
on
erns nesting of if statements, say if the
onsequent of an if is itself
another if statement.
if(a > 0) if(b > 0)
= 5; else
= 6;
In other words, the else joins with the innermost if, and the outer if is left without an
lause. Keeping tra
k of su
h rules is rather
umbersome, so it is best if you insert the
bra
es yourself. Of
ourse if you meant to asso
iate the else with the outer if you
ould
have written
else
If you omit the bra
es, then the
ompiler again will warn you if you have used the -Wparentheses
option. Note that the
ompiler will have
ompiled your program as per the rules of C++,
even when it issues a warning. However, you should treat
ompiler warnings as suggestions
to improve the readability of your
ode. Indeed, if you use parentheses or bra
es as suggested
above, you make your
ode more readable to other programmers as well.
107
1. Modify the turtle program so that the user
an spe
ify how many pixels the turtle
should move, and also by what angle to turn. Thus if the user types \f100 r90 f100
r90 f100 r90 f100" it should draw a square.
2. Write a program that reads 3 numbers and prints them in non-de
reasing order.
3. Write a program whi
h takes as input a number denoting the year, and says whether
the year is a leap year or not a leap year.
4. Write a program that takes as input a number y denoting the year and a number d,
and prints the date whi
h is the dth day of the year y. Suppose y is given as 2011 and
d as 62, then your program should print \3/3/2011".
5. Write a program that takes as input 3 numbers a; b;
and prints out the roots of the
quadrati
equation ax + bx +
= 0. Make sure that you handle all possible values of
a; b;
without running into a division by zero or having to take the square root of a
negative number. Even if the roots are
omplex, you should print them out suitably.
6. Suppose we wish to write a program that plays
ards. The rst step in su
h a program
would be to represent
ards using numbers. In a standard de
k, there are 52
ards,
13 of ea
h suite. There are 4 suites: spades, hearts, diamonds, and
lubs. The 13
ards of ea
h suit have the denomination 2,3,4,5,6,7,8,9,10,J,Q,K,A, where the last 4
respe
tively are short for ja
k, queen, king and a
e. It is natural to assign the numbers
3,2,1,0 to the suites respe
tively. The denominations 2 { 10 are assigned numbers same
as the denomination, whereas the ja
k, queen, king, and a
e are respe
tively assigned
the numbers 11, 12, 13, and 1 respe
tively. The number assigned to a
ard of suite s
and denomination d is then 13s + d. Thus the
lub a
e has the smallest denomination,
1, and the spade king the highest, 52. Write a program whi
h takes a number and
prints out what
ard it is. So given 20, your program should print \7 of diamonds", or
given 51, it should print \queen of spades".
7. Write a program that takes a
hara
ter as input and prints 1 if it is a vowel and 0
otherwise.
8. Can you write the program to determine if a number is prime without using a bool
variable? Hint:
ount how many fa
tors the number has.
9. A number is said to be perfe
t if it is equal to the sum of all numbers whi
h are its
fa
tors (ex
luding itself). So for example, 6 is perfe
t, be
ause it is the sum of its
fa
tors 1, 2, 3. Write a program whi
h determines if a number is perfe
t. It should
also print its fa
tors.
10. Write a program whi
h prints all the prime numbers smaller than n, where n is to be
read from the keyboard.
2
108
11. Write a program that reads in 3
hara
ters. If the three
hara
ters
onsist of two digits
with a '.' between them, then your program should print the square of the de
imal
number represented by the
hara
ters. Otherwise your program should print a message
saying that the input given is invalid.
12. Make an animation of a ball boun
ing inside a re
tangular box. Assume that the box
is atta
hed to the ground, and the ball moves horizontally inside, without fri
tion.
Further assume for simpli
ity that the ball has an elasti
ollision with the walls of
the box, i.e. the velo
ity of the ball parallel to the wall does not
hange, but the
velo
ity perpendi
ular to the wall gets negated. Put the pen of the ball down so that
it tra
es its path as it moves. You
an either read the ball position and velo
ity from
the keyboard, or you
an take it from
li
ks on the
anvas. Move the ball slowly along
its path so that the animation looks ni
e.
13. Modify the animation assuming that the box has mass equal to the ball, and is free
to move in the x dire
tion, say it is mounted on fri
tionless rails parallel to the x
dire
tion. Note that now in ea
h
ollision the velo
ity of the box will also
hange. If
the box has velo
ity v and the ball has velo
ity u parallel to the x axis at the time of
the
ollision, then these velo
ities will be ex
hanged during
ollision, i.e. will be
ome
u and v respe
tively. Show the animation of this system. You may want to start o
the system su
h that the total momentum in the x dire
tion is zero, thereby ensuring
that the box doesnt move out of the s
reen.
14. * In the hardest version of the ball in a box problem the box is sitting on a fri
tionless
surfa
e, and is free to turn. Now after a
ollision, the box will in general start rotating
as well as translating. Assume for simpli
ity that the mass of the box is uniformly
distributed along its 4 edges, i.e. the base is massless.
15. A digital
ir
uit takes as input ele
tri
al signals representing binary values and pro
esses them to generate some required values, again represented as ele
tri
al signals.
As dis
ussed in Se
tion 2.2, a
ommon
onvention is to have a high voltage value (e.g.
0.7 volts) represent 1, and a low value (e.g. 0 volts) represent a 0. A digital
ir
uit is
made out of
omponents
ustomarily
alled gates. An AND gate has two inputs and
one output. The
ir
uit in an AND gate is su
h that the output is 1 (i.e voltage at
least 0.7 volts) if both inputs are 1. If any input is a 0 (i.e. voltage 0 volts or less),
then the output is a 0. Likewise, an OR gate also has 2 inputs and a single output.
The output is a 0 if both inputs are 0, and it is one if even one of the inputs is a 1.
An XOR gate has 2 inputs and one output, and the output is 0 i the inputs are both
the same value. Finally, a NOT gate has one input and one output, and the output is
1 if the input is 0, and 0 if the input is 1.
Figure 6.6 shows the symbols for the NOT gate, the AND gate and the OR gate at
the top, left to right, and a
ir
uit built using these gates at the bottom.
The inputs to a digital
ir
uit are drawn on the left side, and the outputs on the right.
The gates are pla
ed in between. A gate input must either be
onne
ted a
ir
uit input
or to the output of some gate to its left. The gate input takes the same value as the
ir
uit input or the output to whi
h it is
onne
ted. In the gure, a; b;
are the
ir
uit
109
d
b
Chapter 7
Loops
where
ondition is a boolean expression, and body is a statement, in
luding a blo
k statement. The while statement exe
utes as follows.
1. The
ondition is evaluated.
110
111
False
Condition
True
Body
112
The exe
ution will start by setting i to 1. Then we
he
k whether i is smaller than or equal
to 100. Sin
e it is, we enter the body. The rst statement in the body
auses us to print
\The
ube of 1 is 1", be
ause i has value 1. Then we in
rement i. After that we go ba
k to
the top of the statement, and
he
k the
ondition again. Again we dis
over that the
urrent
value of i, 2, is smaller than or equal to 100. So we print again, this time with i=2, so
what gets printed is \The
ube of 2 is 8". We again exe
ute the statement i = i + 1;,
ausing i to be
ome 3. We then go ba
k and repeat everything from the
ondition
he
k.
In this way it
ontinues, until i is no longer smaller than or equal to 100. In other words,
we exe
ute iterations of the loop until (and in
luding) i be
omes 100. When i be
omes 101,
the
ondition i >= 100 fails, and so we go to the statement following the loop. Thus we
print \Done!" and stop. But before this we have exe
uted the loop body for all values of i
from 1 to 100. Thus we will have printed the
ube of all the numbers from 1 to 100.
7.1.1 Counting the number of digits
We
onsider a more interesting problem: read in a non-negative integer from the keyboard
and print the number of digits in it. The number of digits in a number n is simply the
smallest positive integer d su
h that 10d > n. So our program
ould merely start at d = 1,
and try out su
essive values of d until we get to a d su
h that 10d > n.
Thus we have to generate the sequen
e 10; 10 ; 10 ; : : :; but this is just the sequen
e
generation idiom. We should stop generating the sequen
e as soon as we generate a sequen
e
element, say 10d whi
h is larger than n. In other words, we should not stop while 10d n.
This is what the following
ode does.
2
main_program{
int n;
out << "Type a number: ";
in >> n;
int d = 1, ten_power_d=10;
// ten_power_d will always be set to 10 raised to d
while(n >= ten_power_d){
}
}
d++;
ten_power_d *= 10;
// if loop entered,
// number of digits in n must be > d
// so we try next
hoi
e for d
out << "The number has " << d << " digits." << endl;
113
Let us see what happens when we run the program. Say in response to the request to type
in a number, we entered 27. Then we would set d to 1 and ten power d to 10. Then we
would
ome to the while loop. We would nd that n, whi
h equals 27 is indeed bigger than
or equal to ten power d) whi
h equals 10. So we enter the loop. Inside the loop, we add 1
to d so that it be
omes 2, and we multiply ten power d by 10, so it be
omes 100. We then
go ba
k to the beginning of the loop and
he
k the
ondition. This time we would nd that
n whose value is 27 is smaller than ten power d whose values is 100. So we do not enter the
loop but instead go to the statement following the loop. Thus we would print the
urrent
value of d, whi
h is 2, as the number of digits. This is the
orre
t answer: the number of
digits in 27 is indeed 2.
7.1.2 Mark averaging
This problem, like many problems you will see later, is what we might
all a data streaming
problem. By that we mean that the
omputer re
eives a stream (sequen
e) of values, and
we are expe
ted to produ
e something when the stream terminates. O
asionally, we may
be expe
ted to print out a stream of values as well, but in the
urrent problem, we have to
only print out their average. A general strategy for ta
kling su
h problems is to ask yourself:
what information do I need to remember at a point in exe
ution when some n values of the
stream have been read? The answer to this often suggests what variables are needed, and
how they should be updated.
For the mark averaging problem, we know what we want at the end: we want to print
out the average. To
al
ulate the average we need to know the sum of all the values that we
read, and a
ount of how many values we read. So at an intermediate point in the program,
when some n values have been read, we should keep tra
k of n as well as their sum. We dont
need to remember the individual values that we have read so far! So it would seem that we
should keep a variable sum in whi
h we maintain the sum of the values that we have read
till any point in time. We should also maintain a variable
ount whi
h should
ontain the
number of values we read. Both variables should start o 0. We will have a repeated portion
in whi
h we read a value, and for this we will have a variable
alled nextmark. Using these
it would seem that we need to do the following steps repeatedly.
1. Read a value into nextmark.
2. If nextmark is negative, then we have nished reading, and so we go on to
al
ulating
and printing the average.
3. If nextmark is non-negative, then we add nextmark to sum, and also in
rement
ount.
4. We repeat the whole pro
ess from step 1.
In this we have not written down the pro
ess of
al
ulating the average et
. But that is
simply dividing sum by
ount. Figure 7.2(a) shows this as a
ow
hart.
Can we express this
ow
hart using the while statement? For this, you would need to
mat
h the pattern of the
ow
harts of Figure 7.1 and Figure 7.2(a). It seems natural to
mat
h the
ondition in the former with the test nextmark >= 0 in the latter. But there is
an important dieren
e in the stru
ture of the two
ow
harts. In Figure 7.1 the
ondition
114
Start
Start
A
A
cin >> nextmark;
A
P
nextmark >= 0
False
nextmark >= 0
True
False
True
count = count + 1;
count = count + 1;
(a)
(b)
115
test is the rst statement of ea
h iteration, while in Figure 7.2(a), the rst statement is
reading the data, and only the se
ond statement is the
ondition
he
k.
The
ru
ial question then is:
an we somehow modify the
ow
hart of Figure 7.2(a)
so that the exe
ution remains the same, but the new
ow
hart mat
hes the pattern of
Figure 7.1? Suppose we de
ide to move the box labelled A upwards above the point P where
two bran
hes merge. We do not want to
hange what happens on ea
h bran
h that enters P,
so then it simply means that we must pla
e a
opy of A on both bran
hes
oming into P. This
gives us the
ow
hart of Figure 7.2(b). As you
an see, the two
ow
harts are equivalent in
that they will
ause the same statements to be exe
uted no matter what input is supplied
from the keyboard.
Note now that box B and the left
opy of A in Figure 7.2(b) are exe
uted su
essively,
so we
an even merge them into a single box
ontaining 3 statements. This new box
an
be
ome the body of a while statement, and box C the
ondition. Thus we
an write our
ode as follows.
main_program{
float nextmark, sum=0;
int
ount=0;
in >> nextmark;
// box C
// Box B
// Box B
}
}
in >> nextmark;
out << "The average is: " << sum/ ount << endl;
The above program assumes that there will be at least one true mark, so that
ount will not
be zero at the end.
Note the general idea
arefully: the natural way of expressing our program
ould involve
a test in the middle of the
ode we wish to repeat. In su
h
ases, we
an get the test to be
at the top by moving around some
ode and also making a
opy of it. Soon you will start
doing this automati
ally.
C++ allows a break; statement to be used inside the body of a while (both forms). The
break statement
auses the exe
ution of the
ontaining while statement to terminate immediately. If this happens, exe
ution is said to have broken out of the loop. Here is a dierent
way of writing our mark averaging program using the break statement:
float nextmark, sum=0;
116
int
ount=0;
while(true){
in >> nextmark;
if(nextmark < 0) break;
sum = sum + nextmark;
ount =
ount + 1;
}
out << sum/
ount << endl;
The rst point to note here is that
ondition is given as true. This means that the
statement will potentially never terminate! However, the statement does terminate be
ause
of the break statement in the body. After the nextmark is read, we
he
k if it is negative
{ if so the statement terminates immediately, and we exit the loop. If the nextmark is nonnegative, we add nextmark to sum and so on. The result of this exe
ution will be the same
as before. Note that this is similar to the
ow
hart of Figure 7.2(a).
Is the new program better than the old one? It is better in one sense: the statement
in
>> nextmark; is written only on
e. In general it is a good idea to not dupli
ate
ode. First,
this keeps the program small, but more importantly it prevents possible errors that might
arise later. For example, suppose you later de
ide that just as you read mark you also want
to print what was read. If the reading
ode is in several pla
es, then you might forget to
make the
hange in all the pla
es. You may also think that the basi
repetitive unit of work
in the problem is read-pro
ess, rather than pro
ess-read, as it appears in the old
ode. So
in this sense the new
ode is more natural.
The old
ode was better in that the
ondition for terminating the loop was given up
front, at the top. In the new
ode, the reader needs to sear
h a little to see why the loop
will not exe
ute ad innitum. This
ould be
umbersome if the loop body was large. So we
annot unequivo
ally say that the new
ode is better.
Note nally that in
ase of nested loops, the break statement allows us to break out of
only the innermost loop statement in whi
h it is
ontained.
What if someone typed in a number larger than 100 for nextmark? Sin
e we are assuming
that marks are at most 100, we
ould perhaps ignore the numbers above 100 as being
erroneous. This is
onveniently expressed using the
ontinue statement.
When a
ontinue statement is en
ountered during exe
ution, the remaining part of the
loop body is ignored. The
ontrol goes to the top of the loop, and
he
ks the
ondition
and begins the next iteration if
he
k
omes out true, and so on.
The main loop in the program
an be written as follows using the
ontinue statement.
while(true){
in >> nextmark;
if(nextmark > 100){
out << "Larger than 100, ignoring." << endl;
117
ontinue;
}
if(nextmark < 0) break;
sum = sum + nextmark;
ount =
ount + 1;
If nextmark is bigger than 100, then the message is rst printed, and then the rest of the
loop body is skipped. The next iteration is begun, starting with the
ondition
he
k, whi
h
in this
ase is always true.
Note nally that in
ase of nested loops, the
ontinue statement
auses exe
ution to
skip the rest of the body of the innermost loop statement
ontaining it.
7.4 The do
while
statement
The while statement has a variation in whi
h the
ondition is tested at the end of the
iteration rather than at the beginning. It is written slightly dierently. The form is:
do body while (
ondition);
So you may wonder: why do we have this extra form as well? As you
an see, the new form
is more
ompa
t if you dont want the
ondition
he
ked for the rst iteration. Here is a
typi
al example.
main_program{
float x;
har response;
do{
out << ``Type the number whose square root you want: ``;
in >> x;
out << ``The square root is: `` << sqrt(x) << endl;
out << ``Type y to repeat: ``;
in >> response;
118
}
while(response == 'y');
Suppose you want to print a table of
ubes of the integers from 1 to 100. You would solve
this problem using the following pie
e of
ode.
int i = 1;
repeat(100){
out << i << `` `` << i*i*i << endl;
i = i + 1;
}
The variable i plays a
entral role in this
ode. All iterations of the repeat are identi
al,
ex
ept for the value of i. Further, i
hanges from one iteration of the loop to another in a
very uniform manner, in the above
ase it is in
remented by 1 at the end of ea
h iteration.
This general
ode pattern: that there is a
ertain variable whi
h takes a dierent value in
ea
h iteration and the value determines how the iteration will exe
ute, is very
ommon.
Be
ause of this, the designers of C++ (and other programming languages) have provided a
me
hanism for expressing this pattern very
ompa
tly. This me
hanism is the for statement.
Using the for statement, we
an express the above
ode as follows.
for(int i=1; i <= 100; i = i + 1)
out << i << ` ` << i*i*i << endl;
This
ode is equivalent to the repeat loop above. Exa
tly why this is the
ase will be
ome
apparent when we understand the for statement in its general form:
for(initialization ;
ondition ; update) body
In this, initialization and update are required to be expressions, typi
ally assignment
expressions. As you might remember, an assignment expression is simply assignments to
a variable without in
luding the semi
olon, e.g. i = i + 1. Further we may in
lude the
denition along with the assignment e.g. int i = 0. As you might expe
t
ondition must
be a boolean expression. The last part, body may be any C++ statement, in
luding a blo
k
statement. In our example above, the body
onsisted of the statement
out << i << `` ``
<< i*i*i << endl;.
The exe
ution of a for statement starts with the exe
ution of initialization. Then
ondition is evaluated. If
ondition is false, then the statement terminates. If the
ondition is true, the statements in the body are exe
uted followed by the update. We
repeat this pro
ess again starting from evaluation of
ondition. This is shown as a
ow
hart
in Figure 7.3.
Note that any of the elds initialization,
ondition, update or body
an be empty.
If the
ondition is empty, then it is taken as true.
119
False
Condition
True
Body
Update
120
The variable named in initialization and update is
ustomarily
alled the
ontrol
loop. As you might expe
t, initialization assigns an initial value to the
ontrol variable, and the update says how the variable must
hange from one iteration to the
next. As you
an see, in our
ube table example, the update indeed adds 1 to the
ontrol
variable.
You probably also see why the statement is
alled a for statement. It is be
ause we
exe
ute the body many times, for dierent values of the
ontrol variable.
variable of the
In this
ase, we will have shadowing, as dis
ussed in Se
tion 3.6.3. In parti
ular the i dened
in the rst statement will be dierent from the one dened in the for statement, but it will
be the same as the one in the last statement! Thus the for statement will print a table of
ubes as before. The last statement will print 10, be
ause the variable i referred to in it is
the variable dened in statement 1.
7.5.2 Break and
ontinue
If a break statement is en
ountered during the exe
ution of body, then the exe
ution of the
for statement nishes. This is exa
tly as in the while statement.
If the
ontinue statement is en
ountered, then the exe
ution of the
urrent iteration is
terminated, as in the while statement. However, before pro
eeding to the next iteration,
the update is exe
uted. After that
ontrol
ontinues with the next iteration, starting with
he
king
ondition and so on.
7.5.3 Style issue
You may well ask: why should we learn a new statement if it is really not needed? Indeed,
any program that uses a for statement
an be rewritten using a while, with a few additional
variables and assignments.
121
The reason
on
erns style. It is mu
h the same as why we speak loudly on
ertain
o
asions and softly on others: our softness/loudness help the listener understand our intent
in addition to our words. Likewise, when I write a for statement, it is very
lear to the
reader that I am using a
ertain
ommon programming idiom in whi
h there is a
ontrol
variable whi
h is initialized at the beginning and in
remented at the end of ea
h iteration. If
I use either a while statement or a repeat statement, then the reader does not immediately
see all this.
7.5.4 Determining if a number is prime
In Se
tion 6.7.2 we developed a program to determine if a number is prime. We remarked
there that the program
an be made more e
ient by noting that on
e we nd a fa
tor for
the given number, we
an stop
he
king for additional fa
tors and immediately report that
the number is
omposite. We
an implement this idea using the for statement as follows.
In the program of Se
tion 6.7.2 we
he
k whether the number x whi
h is to be
he
ked
for primality is divisible by i, where i goes from 2 to x-1. Clearly, i will serve ni
ely as a
ontrol variable. Furthermore, on
e we dete
t that x is divisible by i, we
an break out of
the loop.
main_program{
int x;
in >> x;
bool found = false;
for(int i=2; i < x; i++){
}
Note the
omment we have added to the
ode. It expresses how our knowledge about whether
x is prime evolves as the program goes through the loop. No matter whi
h iteration it is,
whatever value i has, we know that x is not divisible by the numbers in the range 2 through
i-1. Here if i is 2, the range is empty, so our
laim is to be
onsidered true. Comments
su
h as this one are useful to people reading the
ode, they help in understanding what is
going on.
Most often, the initialization and update in the for statement ea
h
onsists of an assignment to a single variable. However, there are other possibilities too, as we will see in
this se
tion.
122
out << "The number has " << d << " digits." << endl;
There are two noteworthy features of the for statement in the above
ode. First, the
initialization and update both
onsist of two assignments separated by a
omma. This
is allowed. It turns out in C++ the
omma is
onsidered to be an operator in su
h a
ontext,
and it merely joins together two assignments!
The se
ond noteworthy aspe
t is that the above for statement has no body. This is
a
eptable.
The above
ode is very
ompa
t, but might be
onsidered tri
ky by some. The point of
to note, of
ourse, is that
omma separated assignments
an be used as initialization
and update in a for statement in general.
7.6.2 Input in initialization and update
Here is how we
ould write the mark averaging
ode using a for statement.
main_program{
float nextmark,sum=0;
float
ount=0;
We said that initialization and update in a for statement must be expressions; but it
turns out that
in >> nextmark is an expression! We will dis
uss what value it returns in
Se
tion 12.6.3. But right now the value does not
on
ern us; so you
an go ahead and use
su
h input expressions in initialization and update.
I suspe
t that some programmers will like this way of writing the program. It is a bit
un
onventional, however, it does make sense to
onsider nextmark to be a
ontrol variable
for this program.
123
We will now dis
uss what is one of the most elegant, oldest, and useful algorithms ever: the
algorithm for nding the greatest
ommon divisor (GCD) due to Eu
lid, around 300 B.C.
As you know, the inputs for this problem are positive integers m; n. We are required to
ompute their GCD, whi
h is dened to be the largest integer that divides m; n both. It
an
be written using a single while loop.
The algorithm for this, as taught in primary s
hools, is to fa
torize both the numbers,
and then the greatest
ommon divisor (GCD) is the produ
t of the
ommon fa
tors. Another
possibility is to go with the spe
i
ation: examine the numbers between 2 and min(m; n),
and nd the largest one that divides both. This will work, but is slower than the primary
s
hool method.
Eu
lid's algorithm is mu
h faster than both these methods. The starting point for it is
a relatively simple observation: if d is a
ommon divisor of positive integers m; n then it is
a
ommon divisor also of m n; n, assuming m > n. The proof is simple: Sin
e d divides
m; n we have m = pd; n = qd, for integers p; q . Thus m n = (p q )d, and hen
e d divides
m n also. By a similar argument you
an also prove the
onverse, i.e. if d is a
ommon
divisor of m n; n, then d is a
ommon divisor of m; n also.
Thus we have shown that every
ommon divisor of m; n is also a
ommon divisor of
m n; n, and vi
e versa. But then it means that the set of
ommon divisors of m; n is
identi
al to the set of
ommon divisors of m n; n. Thus the greatest in the rst set must
be the greatest in the se
ond set, i.e. GCD(m; n) = GCD(m n; n).
The last statement has profound
onsequen
es. It should be read as saying: if you want
the GCD of m; n, you may instead nd the GCD of m n; n assuming m > n. This
ould
be
onsidered progress, be
ause intuitively, you would think that nding the GCD of smaller
numbers should be easier than nding the GCD of larger numbers.
Let us take an example. Suppose we want to nd the GCD of 3977, 943. Thus we
have GCD(3977; 943) = GCD(3977 943; 943) = GCD(3034; 943). But there is no reason why we should use this idea just on
e: we
an use it many times. Thus we get
GCD(3034; 943) = GCD(2091; 943) = GCD(1148; 943) = GCD(205; 943). At this point
you might realize that we
an subtra
t all multiples in one shot, and the result is simply
the remainder when dividing the original number 3977 by 943. Thus we
ould more dire
tly
have written GCD(3977; 943) = GCD(3977%943; 943) = GCD(205; 943).
Be
ause GCD is a symmetri
fun
tion we
an subtra
t multiples of m just as well as
n. Thus GCD(205; 943) = GCD(205; 943%205) = GCD(205; 123). This further simplies: GCD(205; 123) = GCD(205%123; 123) = GCD(82; 123) = GCD(82; 123%82) =
GCD(82; 41). At this point if we try to apply our rule we get 82%41 = 0, i.e. the smaller of
the numbers divides the larger, and so it must be the GCD. Thus we have obtained, overall,
that GCD(3977,943)=41.
We
an summarize the ideas above into a simple theorem.
Theorem 1 (Eu
lid) Suppose m; n are positive integers. If m%n = 0, then GCD(m; n) =
n. Otherwise GCD(m; n) = GCD(m%n; n).
This is enough to write a program. The program starts by reading the numbers into
variables m,n. Then in ea
h iteration, we will use Eu
lid's theorem to obtain new values for
124
m,n su
h that the GCD of the new values is the same as the GCD of the old values. The new
values will keep on getting smaller, but we know that this
annot happen indenitely. Hen
e
there must
ome a time when we
annot redu
e the values of m,n using Eu
lid's theorem.
But this
an happen only when n divides m, whereupon we
an print out n as the GCD.
main_program{ // Compute GCD of m,n, where m > n >0.
int m,n;
out << "Enter the larger number (must be > 0): ";
in >> m;
out << "Enter the smaller number (must be > 0): ";
in >> n;
while(m % n != 0){
int Remainder = m % n;
m = n;
n = Remainder;
}
out << "The GCD is: " << n << endl;
It should be intuitively
lear that the programs dis
ussed in this
hapter are
orre
t. However, intuition
an be de
eptive, and as we have dis
ussed earlier, it is better to
ross-
he
k.
In this se
tion we dis
uss how to argue the
orre
tness of programs more formally.
In arguing the
orre
tness of repeat loop based programs we
an typi
ally state what
progress we expe
t will happen in ea
h iteration, and this
an be expressed in the plan that
we write and prove (Se
tion 4.2.2). The argument for proving the
orre
tness of programs
that use while/for loops is more
omplex than the argument for repeat based programs
(Se
tion 4.2.2). This is be
ause we do not know in general how many times a while/for
loop will exe
ute. Thus the argument must also show that the loop eventually terminates.
The proof argument for while/for loops tends to typi
ally have a two parts: a loop
invariant, and a potential. We will explain these notions next, and along with the explanation
we will prove the
orre
tness of the GCD program given above.
7.8.1 Loop Invariant
A loop invariant is an assertion about the values taken by variables in a program that must
be true before and after every iteration of the loop. The term invariant is to be understood
like the
onservation prin
iples of Physi
s, e.g. the total energy of the system is the same
after the experiment as it was before. A loop invariant is similar in spirit to the plan we
dis
ussed in Se
tion 4.2.2.
We next des
ribe the invariants needed to prove the
orre
tness of our GCD program.
Suppose m ; n are the values given as input for variables m,n. We will prove the following
invariants.
Invariant 1: (Before and after ea
h iteration of the loop) The GCD of m,n
remains un
hanged, i.e. equals the GCD of m ; n .
0
125
7.8.2 Potential
Intuitively, it should be
lear that the values of m,n will keep redu
ing and hen
e eventually
the loop test must su
eed. We now observe it formally. The value of m in the next iteration
is the
urrent value of n, whi
h is known to be smaller than the
urrent value of m. Hen
e in
ea
h iteration, the value of m de
reases by at least 1. But sin
e n is guaranteed to be always
positive, we know that m will always be positive, i.e. never drop to 0 or be
ome negative.
Hen
e, the number of iterations
annot be more than the value m typed in by the user at
the beginning of the program. Thus the loop must terminate sometime!
The key idea in this argument is the observation that some quantity must de
rease by at
least some xed amount, but the nature of the loop body is su
h that the quantity
annot
de
rease below a
ertain threshold. This establishes that the number of iterations must be
nite, otherwise the quantity will have de
reased below the threshold. In the
ase of GCD,
it is
onvenient to
hoose as potential the value of m. But in other programs, there will be
other
hoi
es, sometimes
reativity will be needed to dene a suitable potential.
0
126
This quantity is metaphori
ally
alled the Potential, inspired by arguments involving the
notion of potential energy in Physi
s.
7.8.3 Corre
tness
Given appropriate invariants and a suitable potential, the
orre
tness proof is almost done.
Usually it is only a matter of tying up some loose ends.
When the GCD program terminates, we know from the invariant that GCD of the
urrent
values of m,n must be the same as the GCD of m ; n . Sin
e the loop test must have failed
just before termination, we know that Remainder == 0, i.e. m % n == 0. But then the
GCD must be n, whi
h is indeed what we print. Thus we have established
orre
tness.
0
+1
+1
+1
+2
7.9 Remarks
Looping is a very important operation in programming. In this
hapter we have seen how
various problems
an be solved using the while loop as well as the for loop, and earlier
we saw the repeat loop. For while and for, there were further variations depending upon
whether we used break, or repli
ated
ode. Later on in the book, we will see even further
ways of expressing some of the programs we have seen in this
hapter.
As we have indi
ated, ea
h way of writing loops has some advantages and disadvantages.
One may be more readable or less readable, another may avoid dupli
ation of
ode, and yet
another may be less e
ient be
ause it does unne
essary work. Another
onsideration is
127
naturalness:
does a
ertain way of writing
ode more
onsistent with how you might think
about the problem? So the
hoi
e of how to express a program is in the end a subje
tive
hoi
e. So you should develop your own taste in this regard.
The while and for loops are tri
kier than repeat loops. This is be
ause it is possible
to make a programming error and write while/for loops that do not terminate. Hen
e we
must be more
areful in using these loops as
ompared to repeat loops. This
omplexity
is re
e
ted in the manner in whi
h we argue the
orre
tness. You may observe that the
orre
tness argument for repeat did not need to have anything like a Potential be
ause the
repeat loops are guaranteed to terminate no matter what.
We have remarked earlier that proving programs
an be tedious for large programs.
However, we will emphasize that even if you dont do full proofs, you should write down
invariants and potentials for ea
h non-trivial program that you write.
Exer ises
1. Write a program that prints a
onversion table from Centigrade to Fahrenheit, say
between 0 C to 100 C. Write using while and also using for.
2. Suppose we are given n points in the plane: (x ; y ); : : : ; (xn; yn). Suppose the points
are the verti
es of a polygon, and are given in the
ounter
lo
kwise dire
tion around
the polygon. Write a program using a while loop to
al
ulate the perimeter of the
polygon. Also do this using a for loop.
3. Write a program that returns the approximate square root of a non-negative integer.
For this exer
ise dene the approximate square root to be the largest integer smaller
than the exa
t square root. Your are expe
ted to not use the built-in sqrt or pow
ommands, of
ourse. Your program is expe
ted to do something simple, e.g.
he
k
integers in order 1; 2; 3; : : : to see if it qualies to be an approximate square root.
4. Suppose some
ode
ontains some while statements. Show how you
an repla
e the
while statements by for statements without
hanging the output produ
ed by the
ode.
5. Add a \Stop" button to the turtle
ontroller of Se
tion 6.4.1. Modify the program so
that it runs until the user
li
ks on the stop button. Also there should be no limit on
the number of
ommands exe
uted by the user (100 in Se
tion 6.4.1).
6. Write a program that prints out the digits of a number starting with the least signi
ant
digit, going on to the most signi
ant. Note that the least signi
ant digit of a number
n is simply n % 10.
7. Write a program that takes a number n and prints out a number m whi
h has the same
digits as m, but in reverse order.
8. A natural number is said to be a palindrome if the sequen
e its digits is the same
whether read left to right or right to left. Write a program to determine if a given
number is a palindrome.
1
128
9. Write a program that takes as input a natural number x and returns the smallest
palindrome larger than x.
10. Add
he
ks to the GCD
ode to ensure that the numbers typed in by the user are
positive. For ea
h input value you should prompt the user until she gives a positive
value.
11. Suppose the user types in the smaller number rst and the larger number se
ond, in
response to the requests during the exe
ution of the GCD program of Se
tion 7.7.
Show that the
orre
t answer will nevertheless be given. State how the invariants and
the analysis of the number of iterations will
hange.
12. Write a program that takes a natural number and prints out its prime fa
tors.
13. * Write a program that reads in a sequen
e of
hara
ters, one at a time, and stops
as soon as it has read the
ontiguous sequen
e of
hara
ters 'a', 'b', 'r', 'a', '
', 'a',
'd', 'a', 'b', 'r', 'a', i.e. the string \abra
adabra". Hint: After you have read a
ertain
number of
hara
ters, what exa
tly do you need to remember? You do you need to
remember the entire pre
eding sequen
e of
hara
ters, even the last few
hara
ters
expli
itly. Figure out what is needed, and just remember that in your program.
14. * Let x ; : : : ; xn be a sequen
e of integers (possibly negative). For ea
h possible subsequen
e xi ; : : : ; xj
onsider its sum Sij . Write a program that reads in the sequen
e
in order, with n given at the beginning, and prints out the maximum sum Sij over all
possible subsequen
es.
Hint: This is a di
ult problem. However, it will yeild to the general strategy: gure
out what set of values V (k) we need to remember having seen the rst k numbers.
When you read the k + 1th number, you must
ompute V (k + 1) using the number
read and V (k) whi
h you
omputed earlier.
1
Chapter 8
Computing
ommon mathemati
al
fun
tions
In this
hapter we will see ways to
ompute some
ommon mathemati
al fun
tions, su
h as
trigonometri
fun
tions, square roots, exponentials and logarithms. We will also see how to
ompute the greatest
ommon divisor of two numbers using Eu
lid's algorithm. This is one
of the oldest interesting algorithm, invented well before
omputers were even
on
eived.
The main statement in all the programs of the
hapter will be a looping statement. You
ould
onsider this
hapter to be an extension of the previous, giving more ways in whi
h
loop statements
an be used.
Some of the material in this
hapter requires somewhat deep mathemati
s. We will state
the relevant theorems, and try to explain intuitively why they might be true. The pre
ise
proofs are outside the s
ope of this book.
Suppose we wish to
ompute f (x) for some fun
tion f , su
h as say f (x) = sin(x). Suppose
we know how to
ompute f (x ) for some xed x . Suppose that the derivative f 0 of f and the
derivative f 00 of f 0 and so on exist at x , and we
an evaluate these. Then if x is reasonably
lose to x then f (x) equals the sum of the Taylor series of f at x . The ith term of the
Taylor series is f i0(x )(x x )i=i!, in whi
h f i0 is the fun
tion obtained from f by taking
derivative i times. Thus we have:
(x x ) + f 000(x ) (x x ) +
f (x) = f (x ) + f 0 (x )(x x ) + f 00 (x )
2!
3!
In the typi
al s
enario, we only
ompute and sum the rst few terms of the series, and that
gives us a good enough estimate of f (x). The general theory of this is dis
ussed in standard
mathemati
s texts and is outside our s
ope. However, you may re
ognize the rst two terms
as
oming from a tangent approximation of the
urve, as shown in Figure 8.1. The value of
f (x) equals (the length of) FD. We approximate this by FC, whi
h in turn is FB + BC =
EA + (BC/AB)AB = f (x ) + f 0(x ) (x x ).
0
129
130
f (x)
E
x0
131
+1
2 +1
= ( 1)
x2k
(2k
x2
k +1
main_program{
double x;
in >> x;
double epsilon = 1.0E-20, sum = x, term = x;
for(int k=2; abs(term) > epsilon; k++){
// Plan: term = t_{k-1}, sum = sum of k-1 terms
term *= -x * x /((2*k-2)*(2*k-1));
sum += term;
}
}
The
ommand abs stands for absolute value, and returns the absolute value of its argument.
8.1.2 Natural log
Consider f (x) = ln x, the natural logarithm of x. One way of dening it is
Z x
ln x = 1 du
1
132
So from this, we
an nd its Taylor series. Clearly f 0(x) = 1=x. f 00(x) = 1=x and so on.
It is
onvenient to use x = 1. Thus we get:
2
h2
h3
h4
ln1 + h = h 2 + 3 4
A very important point to note for this series is that the series is valid only for 1 < h 1.
We noted earlier that the Taylor series is valid only if x is
lose enough to x , or equivalently
x x = h is small. For the ln fun
tion, we have a pre
ise des
ription of what
lose enough
means: within a unit distan
e from x .
Even so, note that you
an indeedxuse the series to
al
ulate ln x for arbitrary values of
x. Simply observe that ln x = 1 + ln e . Thus by fa
toring out powers of e we will need to
use the series only on a number smaller than 1.
0
h2
h
+
f 000 (x ) +
2!
3!
3
x
f (x) = f (0) + f 0 (0)x + f 00 (0)
+ f 000(0) x
2!
3! +
In general, the terms of the Taylor series in
rease with x. Thus, it is best to keep x
small if possible. For example, suppose we wish to
ompute sin(100:0). One possibility is
to use the previous program spe
ifying 100.0 as input. A better way is to subtra
t as many
multiples of 2 as possible, sin
e we know that sin(x + 2n) = sin(x) for any integer n. In
fa
t identities su
h as sin(x) = sin( x)
an be used to further redu
e the value used in
the main loop of the program. In fa
t, noting that the Taylor series for
os(x) is:
os(x) = 1 x2! + x4! x6! +
we
an in fa
t
ompute the sine using sin(x) =
os(=2 x) if =2 x happens to be smaller
in absolute value than x.
The proof of the Taylor series expansion is well beyond the s
ope of this book. However,
we have provided some intuitive justi
ation for the rst two terms by
onsidering the tangent
to approximate the fun
tion
urve, Figure 8.1.
2
You might be familiar with the formula ( ) = + 12 2, in whi
h ( ) is the distan
e
overed in time
by a parti
le moving at a
onstant a
eleration , with initial velo
ity . Note that = (0), = (0)
and with this substitution the formula
an be written as ( ) = (0) + (0) + (0) t22 , whi
h resembles the
Taylor series in the rst 3 terms.
1
s t
ut
at
s t
s t
00
00
133
In other words, ln x is the area under the
urve y = 1=u between u = 1 and u = x. So we
an nd ln x if we
an nd the area!
Well, we
annot
ompute the area exa
tly, but we
an approximate it. In general suppose
we wish to approximate the area under a
urve f (u) = 1=u from some p to q. Then we
an
get an overestimate to this area by
onsidering the area of the smallest axes parallel re
tangle
that
overs it. The height of this re
tangle is f (p) (be
ause f is non-in
reasing) and the
width is q p. Thus our required approximation (over estimate) is (q p)f (p). This is
the strategy we will use, after dividing the required area into n verti
al strips. Sin
e the
urve goes from 1 to x the width of ea
h strip is w = (x 1)=n. The ith strip extends from
u = 1+ iw to u = 1+(i +1)w, where we will
onsider i to be ranging between 0 and n 1 as
is
ustomary, rather than between 1 and n. The height of the re
tangle
overing this strip
is f (1 + iw) and hen
e the area is wf (1 + iw). Thus the total area of the re
tangles is:
1
1 + iw
i
i
But evaluation of this formula is easily translated into a program! In fa
t i will naturally
serve as a
ontrol variable for our for loop. We will take ea
h su
essive term of the series
and add it into a variable area whi
h we rst set to 0. The following is the
omplete program.
n 1
X
=0
wf (1 + iw) =
n 1
X
=0
main_program{
float x;
in >> x;
// will
al
ulate ln(x)
int n;
in >> n;
// number of re
tangles to use
float w = (x-1)/n;
// width of ea
h re
tangle
float area = 0;
// will
ontain ln(x) at the end.
for(int i=0; i < n; i++)
area = area + w /(1+i*w);
out << "Natural log, from integral: "<< area << endl;
}
We note that C++ already provides you a single
ommand log whi
h
an be invoked as
log(x) and it returns the value of the natural logarithm. This
ommand uses some
ode
probably more sophisti
ated than what we have written above, and it guarantees that the
answer it returns will be
orre
t to as many bits as your representation. So we
an use the
ommand log to
he
k how good our answer is. To do this simply add the line
out << "Natural log, from built-in fun
tion: "<< log(x) << endl;
before the end of the program given above. This will
ause our answer to be printed as well
as the true answer, and so we
an
ompare.
134
It is worth pointing out that there are two kinds of errors in a
omputation su
h as the
one above. The rst is the error produ
ed by the mathemati
al approximation we use to
al
ulate a
ertain quantity. For the natural log, this
orresponds to the error that arises
be
ause of approximating the area under the
urve by the area of the re
tangles. This error
will redu
e as we in
rease n, the number of re
tangles. The se
ond kind of error arises
be
ause on a
omputer numbers are represented only to a xed pre
ision. Thus, we will have
error be
ause our
al
ulation of the area of ea
h re
tangle will itself not be exa
t. If we use
float representation then every number is
orre
t only to a pre
ision of about 7 digits. If
you add n numbers ea
h
ontaining an (additive) error of , then the error in the sum
ould
be
ome n, assuming all errors were in the same dire
tion. Even assuming
that the errors
p
are random, it is possible to show that the error will be proportional to n. In other words,
if you add 10000 numbers, ea
h with an error of about 10 , your total error is likely to have
risen to about 10 (if not to 10 ). Thus, we should
hoose n large, but not too large. The
exer
ises ask you to experiment to nd a good
hoi
e. Note that you
an redu
e the se
ond
kind of error by representing the numbers in double rather than float.
Another variation on the method is to approximate the area under the
urve by a sequen
e
of trapeziums. This indeed helps. This method, and the more intriguing method based on
Simpson's rule are left to the exer
ises.
7
A root of a fun
tion f is a value x su
h that f (x ) = 0. In other words, a point where the
plot of the fun
tion tou
hes the x axis. Many problems
an be expressed as nding the roots
of an equation. For example, suppose we want to nd the square root of 2. Then instead we
ould ask for the rootspof the polynomial f (x) = x 2. Clearly, if f (x) = 0 then we have
x 2 = 0, i.e. x = 2 and this would give us the square root of 2. So nding roots is a
very important mathemati
al problem.
In this se
tion, we will see a very simple method for nding roots approximately. The
method will require that (a) we are given values xL xR su
h that f (xL ) and f (xR ) have
opposite signs, (b) f is
ontinuous between xL and xR . These are fairly minimal
onditions,
for example for f (x ) = x 2 we
an
hoose xL = 0 giving f (xL) = 2, and xR = 2 (or any
large enough value), giving f (xR ) = 2. Clearly xL ; xR satisfy the
onditions listed above.
Be
ause f is
ontinuous, and has opposite signs at xL; xR , it must pass through zero
somewhere in the (
losed) interval [xL; xR . We
an think of xR xL is the degree of un
ertainty, (or maximum error) in our knowledge of the root. Getting a better approximation
merely means getting a smaller interval, i.e. xL; xR su
h that xR xL is smaller. If the size
of the interval is really small, we
an return either endpoint as an approximate root. So the
main question is:
an we somehow pi
k better xL ; xR given their
urrent values.
A simple idea works. Consider the interval midpoint: xM = (xL + xR )=2. We
ompute
xM and nd the sign of f (xM ). Suppose the sign of f (xM ) is dierent from the sign of
f (xL ). Then we know
an set xR = xM . Clearly the new values xL ; xR satisfy our original
requirements. If the sign of xM is the same as the sign of xL, then it must be dierent from
the sign of xR . In that
ase (see Figure 8.2) we set xL = xM . Again the new values of xL; xR
satisfy our 2
onditions. Hen
e in ea
h
ase, we have redu
ed the size of the interval, and
thus redu
ed our un
ertainty. Indeed if we want to redu
e our error to less than some , then
0
135
(xR ; f (xR))
xL
xM
xR
Root
(xM ; f (xM ))
(xL ; f (xL))
Figure 8.2: Bise
tion method. Next we will have xL = xM .
136
we must repeat this pro
ess until xR xL be
omes smaller than . Then we would know
that they are both at a distan
e at most from the root, sin
e the root is inside the interval
[xL ; xR .
The
ode is then immediate. We write it below for nding the square root of 2, i.e. for
f (x) = x 2.
2
main_program{
// find root of f(x) = x*x - 2.
float xL=0,xR=2; // invariant: f(xL),f(xR) have different signs.
float xM,epsilon;
in >> epsilon;
bool xL_is_positive, xM_is_positive;
xL_is_positive = (xL*xL - 2) > 0;
// Invariant: xL_is_positive gives the sign of f(x_L).
We
an get a faster method for nding a root of a fun
tion f if we have a way of evaluating
f (x) as well as its derivative f 0 (x) for any x. To start o this method, we also need an initial
guess for the root, whi
h we will
all x . Often, it is not hard to nd an initial guess; indeed
in the example we will take, almost any x works as the initial guess.
In general, the Newton-Raphson method takes as input a
urrent guess for the root, say
xi . It returns as output a (hopefully) better guess, say xi . We then
ompute f (xi ), if it
is
lose enough to 0, then we report xi as the root. Otherwise, we repeat the method with
xi to get, hopefully, an even better guess xi .
The pro
ess of
omputing xi given xi is very intuitive. We know from Se
tion 8.1 that
f (x) f (xi ) + f 0 (xi ) (x xi ), assuming x xi is small. In this equation we
ould
hoose
x to be any point, in
luding the root. So let us
hoose x to be the root. Then f (x) = 0.
Thus we have 0 f (xi) + f 0(xi) (x xi ). Or in other words, x xi ff0 xxii . Noti
e that
the right hand side of this equation
an be evaluated. Thus we
an get an approximation to
the root! This approximation is what we take as our next
andidate.
f (xi )
xi = xi
(8.1)
f 0 (xi )
That is all there is! Figure 8.3 shows what happens graphi
ally. The point A with
oordinates
(xi ; 0) represents our
urrent estimate of the root. We draw a verti
al line from A up to the
0
+1
+1
+1
+1
+2
+1
+1
137
B (xi ; f (xi))
Root
C
xi+1
f (x)
A
xi
+1
+1
+1
+1
+1
The Babylonians used the same method as early as 2000 BC to nd square roots. But they probably
did not derive it as a spe
ial
ase of a general root nding pro
edure.
2
138
Next we need an initial guess. The standard idea is to make an approximate plot of the
fun
tion, and
hoose a point whi
h appears
lose to the root. In this
ase it turns out that
almost any initial guess is ne, ex
ept for 0, be
ause at 0 the term y=xi would be undened.
So for simpli
ity, we
hoose x = 1. So we are ready to write the program. The basi
idea is
to maintain a variable xi representing the
urrent guess. We will update xi in ea
h iteration
using the above rule, and initialize xi to 1.
0
main_program{
double xi=1, y;
in >> y;
repeat(10){
xi = (xi + y/xi)/2;
}
out << xi << endl;
}
This program will run a xed 10 iterations, and
al
ulate the estimates x ; x ; : : : ; x , starting with x = 1. But we
an also run a number of iterations depending upon how mu
h
error we wish to tolerate.
This is slightly tri
ky. If the a
tual root is x , then the error in the
urrent estimate
xi is jxi x j. Indeed, if we exa
tly knew the error, i.e. the value v = jxi x j, we
ould
dire
tly
ompute the root by noting that x = xi v. So we need to make an estimate for
the error. A
ommon estimate is f (xi). Indeed, f (xi) is the verti
al distan
e of the point
(xi ; 0) to the
urve f whereas the exa
t error, xi x is the horizontal distan
e of the point
(xi ; 0) to the
urve. Indeed, when the verti
al distan
e be
omes 0, so would the horizontal.
So our program
an terminate when the error estimate f (xi) = xi y be
omes small, i.e.
when xi*xi-y be
omes smaller than some threshold
1
10
main_program{
float y;
in >> y;
float xi=1;
while(abs(xi*xi - y) >0.001){
xi = (xi + y/xi)/2;
out << xi << endl;
}
}
In the above
ode we have used the built-in fun
tion abs whi
h returns the absolute value
of its argument.
8.5 Summary
This hapter has introdu ed several new ideas, though no new programming langauge features.
139
Exer ises
1. Write a program to nd ln x for arbitrary x using the Taylor series. Che
k your answer
by using the built in log
ommand.
2. Write down the Taylor series for f (x) = ex, noting that f i0(x) = ex. It is
onvenient
to expand around x = 0, i.e.
onsider the Ma
Laurin series. This series is valid for
all values of x, however, it is a good idea to use it on as small values of x as possible.
Write a program to
ompute ex, and
he
k against the built in
ommand exp.
3. Run the program for
omputing natural log for various
hoi
es of n and see how the
result varies. For what value of n do you get an answer
losest to the log fun
tion of
C++?
4. A more a
urate estimate of the area under the
urve is to use trapeziums rather than
re
tangles. Thus the area under a
urve f (u) in the interval [p; q will be approximated
0
140
5.
6.
7.
8.
9.
by the area of the trapezium with
orners (p; 0), (p; f (p)), (q; f (q)), (q; 0). This area
is simply (f (p) + f (q))(q p)=2. Use this to
ompute the natural logarithm.
Simpson's rule gives the following approximation of the area under the
urve of a
fun
tion f : Z b
b a
a+b
f (x)dx
6 f (a) + 4f 2 + f (b)
a
Use this rule for ea
h strip to get another way to nd the natural log.
Suppose we are given n points in the plane: (x ; y ); : : : ; (xn; yn). Suppose the points
are the verti
es of a polygon, and are given in the
ounter
lo
kwise dire
tion around the
polygon. Write a program to
al
ulate the area of the polygon. Hint 1: Break the area
into small triangles with known
oordinates. Then
ompute the lengths of the sides of
the triangles, and then use Heron's formula to nd the area of the triangles. Then add
up. Hint 2: Break the boundary of the polygon into two parts, an up fa
ing boundary
and a down fa
ing boundary. Express the area as the area under these boundaries ea
h
onsidered as fun
tions f (u).
Children often play a guessing game as follows. One
hild, Kashinath, pi
ks a number
between 1 and 1000 whi
h he does not dis
lose to another
hild, Ashalata. Ashalata
asks questions of the form \Is you number between x and y?" where she
an pi
k x; y
as she wants. Ashalata's goal is to ask as few questions as possible and determine the
number that Kashinath pi
ked. Show that Ashalata
an guess the number
orre
tly
using at most 10 questions. Hint: Use ideas from the bise
tion method.
Write a program to nd ar
sin(x) given x.
Consider a
ir
uit in whi
h a voltage sour
e of VCC = 1:5 volts is applied to a diode
and a resistan
e of R = 1 Ohm
onne
ted in series, Figure 8.4. The
urrent I through
a diode a
ross whi
h there is a potential drop of V is
I = IS (eV = nVT 1)
where IS is the reverse saturation
urrent of the diode, VT is the thermal voltage whi
h
is about 25 mV at room temperature (300 Kelvin), and n is the ideality fa
tor. Suppose
the diode we are using has n = 1 and IS = 30 mA. Write a program that nds the
urrent. Use your program to also nd the
urrent when the voltage sour
e is reversed.
Consider the problem of nding the roots of f (x) = x x=2+1=4. See what happens
using the Newton-Raphson method for guesses for the initial value. In parti
ular, try
x = 1 and x = 0:5. Can you solve this using the bise
tion method?
1
10.
141
Chapter 9
Fun
tions
In the pre
eding
hapters, we have seen programs to do many things, from drawing polygons
and mis
ellaneous pi
tures to
al
ulating in
ome tax and nding the greatest
ommon divisor
(GCD) and nding roots. It is
on
eivable that we will want to write more and more
omplex
programs in whi
h some of these operations, e.g. nding the GCD, is needed at many pla
es.
One possibility is to
opy the
ode for the operation in as many pla
es as is required. This
doesnt seem too elegant, and is also error prone. Wouldnt it be ni
e, if for ea
h frequently
used operation you
ould somehow
onstru
t a \
ommand" that
ould then be used wherever
you want in your program? Just as we have a
ommand sqrt for
omputing the square root,
or
ommands for to
ompute the trigonometri
ratios (Se
tion 1.5)
an we build a g
d
ommand that will
ompute the GCD of two numbers when demanded? This
an be done,
and how to build su
h
ommands is the subje
t of this
hapter.
The term fun
tion is used in C++ to denote what we have so far informally
alled a
ommand. In some languages the terms pro
edure or subprogram are also used. In what
follows, we will use the term fun
tion.
Suppose that we indeed need to frequently
ompute the GCD, and so would like to have
a fun
tion whi
h
omputes this. It is natural to
hoose the name g
d for this fun
tion. It
ould take two numbers as arguments, and return their GCD, whi
h
ould then be used. As
an example, suppose you wanted to nd the GCD of 24 and 36, and also the GCD of 99 and
47. If we had a g
d fun
tion as des
ribed, then we
ould write a very simple main program
as follows.
main_program{
int a=36,b=24,
=99,d=47;
out << g
d(a,b) << endl;
out << g
d(
,d) << endl;
}
Sin
e we dont already have su
h a g
d fun
tion, we must dene it. We dis
uss how to do
this next.
142
143
int g
d
// return-type fun
tion-name
(int m, int n)
// parameter list: (parameter-type parameter-name ...)
{
// beginning of fun
tion body
while(m % n != 0){
int Remainder = m % n;
m = n;
n = Remainder;
}
return n;
}
// end of fun
tion body
The denition begins with type-of-return-value, whi
h indi
ates the type of the value
returned by the fun
tion. In the GCD example, the fun
tion
omputes and evaluates the
GCD, whi
h has type int, so our denition (Figure 9.1) mentions this.
Next is fun
tion-name, the name of the fun
tion being dened. In our example, we
hose
to
all our fun
tion g
d, so that is what the
ode states. Any valid identier (Se
tion 3.1.1)
an be
hosen as a fun
tion name.
Next is a parenthesised list of the parameters to the fun
tion, together with their types.
In our
ase, there are two parameters, m,n both of type int.
Finally,
omes the
ode, body, that is used to
ompute the return value. The body is
expe
ted to be a sequen
e of statements, just as you would expe
t in any main program. It
an
ontain de
larations of variables,
onditional statements, looping statements, everything
144
that
an be present in a main program. However, there are two additional features. The
ode in the body
an refer to the parameters, as if they are variables. Further, the the body
must
ontain a return statement, whi
h we explain shortly. We note that the body of the
denition in Figure 9.1 is taken substantially from the program developed in Se
tion 7.7.
9.1.1 Exe
ution:
all by value
Consider our g
d fun
tion and main program. While exe
uting the main program, suppose
that
ontrol arrives at the
all g
d(a,b). We des
ribe the general rule that determines what
happens, and also mention what happens in our spe
i
ase.
1. The arguments to the
all are evaluated. In our
ase it simply means fet
hing the
values of the variables a,b, viz. 36,24. But in general, the arguments
ould be arbitrary
expressions whi
h would have to be evaluated.
2. The exe
ution of the
alling subprogram, i.e. the subprogram whi
h
ontains the
all,
main program, in this
ase, is suspended. The
alling subprogram will be resumed
later. When resumed, the exe
ution will
ontinue from where it was suspended.
3. Preparations are made to start running a subprogram. The subprogram will exe
ute
the
ode given in the body of the fun
tion. The subprogram must be given a separate
area of memory so that it
an have its own variables. It is
ustomary to refer to this
area as the a
tivation frame of the fun
tion
all. Immediately, spa
e is allo
ated in
the a
tivation frame for storing the variables
orresponding to the parameters of the
fun
tion.
Thus in our
ase, an a
tivation frame is
reated
orresponding to the
all g
d(a,b).
The g
d fun
tion has two parameters, L, S. So variables, L and S will be
reated in
the a
tivation frame.
4. The value of the rst argument is
opied to the memory asso
iated with the rst
parameter. The value of the se
ond argument to the se
ond parameter, and so on.
Thus, in our
ase, 36 will be
opied into the variable L, and 24 into the variable S in
the a
tivation frame
reated for the
all g
d(a,b). Figure 9.2(a) shows the state of
the memory at this time. We have referred to the memory area used by main program
as its a
tivation frame. This is
ustomary.
5. Now the body of the
alled fun
tion is exe
uted. The body must refer to variables
or parameters stored only in the a
tivation frame of the
all. If spa
e needs to be
reserved for variables et
., it is done only inside the a
tivation frame of the
all.
Thus in
ase of our program, the
ode may refer to the parameters L, S. The
ode
annot refer to variables a,b,
,d be
ause they are not in the a
tivation frame of
g
d(a,b). When the rst statement of the body is exe
uted, it
auses the
reation of
the variable Remainder. The spa
e for this is allo
ated in the a
tivation frame of the
all. Su
h variables are said to be lo
al to the
all.
1
145
6. The body of the fun
tion is exe
uted until a return statement is en
ountered. The
expression following return is
alled the return-expression and its value is sent ba
k
to the
alling program. The value sent ba
k is
onsidered to be the value of the
all in
the
alling program.
In our
ase the exe
ution of the fun
tion happens as follows. In the rst iteration of
the loop, L, S have values 36,24. At the end of this iteration, the values be
ome 24,12.
The state of the memory at this point is shown in Figure 9.2(b). In the next iteration,
Remainder be
omes 0, and so the break statement is exe
uted. Thus the
ontrol exits
from the loop, and return is rea
hed. The return-expression is S whi
h has value
12. This value is sent ba
k to the
alling program.
7. The a
tivation frame
reated for the
all is not needed any longer, and is destroyed,
i.e. that area is marked available for general use.
8. The
alling program resumes exe
ution from where it had suspended. The returned
value is used as the value of the
all itself.
In our
ase the
all was g
d(a,b), and its value is required to be printed. Thus the
value returned, 12, will be printed. After this the next
out statement will be exe
uted
(in whi
h we will en
ounter the se
ond
all to g
d). This will
ause an a
tivation frame
to be
reated again et
.
In this model of exe
uting fun
tion
alls, only the values of the arguments are sent from the
alling program to the
alled fun
tion. For this reason, this model is often termed as
all by
value. We will see another model later on.
It is worth
onsidering what happens on the se
ond
all to g
d, i.e. the
all g
d(
,d) in
the
ode. The same set of a
tions would repeat. A new a
tivation frame would be
reated
for this
all, and very likely it would use the same memory as was used for the a
tivation
frame of the previous
all. The point to be noted is that ea
h
all requires some additional
memory, but only for the duration of the exe
ution of the
all.
9.1.2 Names of parameters and lo
al variables
We have already said that when a fun
tion
all exe
utes, it
an only a
ess the variables
(in
luding the parameters) in its a
tivation frame. In parti
ular, the variables in the
alling
program (in this
ase main program)
annot be a
essed. So it is perfe
tly ne if variables
in the
alling program and the
alled fun
tion have the same name! Note further that when
the
alling program is exe
uting, the a
tivation frame of the
alled fun
tion does not even
exist, so there is no question of any
onfusion.
Suppose now that you wish to develop a program to
ompute the least
ommon multiple
(LCM) of two numbers. This is easily done using the following relationship between the
LCM, L, and the GCD, G of two numbers m; n:
mn
L=
G
146
The exe
ution of l
m follows the same idea as in our dis
ussion earlier for g
d. Suppose l
m
is
alled in by a main program as follows.
main_program{
out << l
m(36,24) << endl;
}
When we exe
ute the main program, we will need to run a subprogram for l
m, whi
h
involves
reating the a
tivation frame for this
all. As this subprogram exe
utes, we will
en
ounter the expression g
d(m,n) with m,n taking the values 36,24. To pro
ess this
all,
we will need to start a subprogram for g
d. So at this point, we will have 3 a
tivation
frames in memory, one for main program, one for l
m(36,24) and another for g
d(36,24).
This is perfe
tly ne! When the subprogram for g
d(36,24) nishes, then the result, 12,
will be sent ba
k to the subprogram for l
m(36,24). The result 12, will be used as the
value of the
all g
d(m,n). Thus the expression m*n/g
d(m,n)
an now be evaluated to
be 36*24/12=72. This will in fa
t be the value that the subprogram l
m(36,24) returns
ba
k to main program. At this point, the
omputation of main program will resume with
the re
eived value.
147
While it is important to know how a fun
tion
all exe
utes, while thinking about fun
tions,
a dierent, metaphori
al view is useful.
The idea is to think of a fun
tion
all as giving out a
ontra
t to get a job done. We think
of the main program as an agent doing its work as des
ribed in its program. Suddenly, the
agent en
ounters a statement su
h as l
m(36,24). Rather than doing the work required to
ompute l
m(36,24) itself, the main program agent engages another agent. This agent is
the subprogram for the
all l
m(36,24). The main program agent sends the input data to
the subprogram agent, and waits for the result to be sent ba
k. This is not unlike engaging a
tailor, giving the tailor the
loth and measurements, and waiting for the tailor to send ba
k
a shirt.
The similarity extends further. There is nothing to prevent the tailor from further
ontra
ting out the work to others. It so happens, that stit
hing the
ollar of a shirt is a
spe
ialized job, whi
h most tailors would in fa
t
ontra
t out to
ollar-spe
ialists. Thus it is
possible that we may be waiting for the tailor to send us ba
k the shirt, and the tailor might
be waiting for the
ollar spe
ialist to send ba
k a
ollar. Noti
e that this is very similar to
main program waiting for l
m(36,24) whi
h in turn is waiting for g
d(36,24).
148
fun
tion. In addition, the
alling program might also have to deal with post-
onditions,
as will be dis
ussed in Se
tion 9.4.
2. Responsibilities of the
alled program: If the
alling program fulllls its responsibilities
(i.e. the arguments satisfy the pre
onditions), and only if the
alling program fulllls
its responsibilities, is the
alled program obliged to do whatever was promised. There
is no telling what will happen if the pre
onditions are not satised. Thus in
ase of
g
d, if a negative value or zero is supplied: nonsense values may be returned, or the
program may never terminate, or terminate with an error.
It is extremely important to
learly write down the spe
i
ation of a fun
tion. You may
sometimes avoid doing so, thinking that the spe
i
ation is obvious. But it may not be so!
For example, a more general denition of GCD might allow one of the numbers to be zero,
in whi
h
ase the other number is dened to be the GCD. If this is the denition a user
is familiar with, he/she might supply 0 as the value of the se
ond parameter n. This will
ertainly
ause the program to terminate be
ause of a division by zero in the very rst step
of our
ode. To prevent su
h misunderstandings, it is best to write down the spe
i
ations
in full detail.
The natural pla
e to write down the spe
i
ation is immediately before the fun
tion
denition. So your fun
tion for g
d should really look like the following.
int g
d(int L, int S)
// Fun
tion for
omputing the greatest
ommon divisor of integers L, S.
// PRE-CONDITION: L, S > 0
{
...
}
Please get into the habit of writing spe
i
ations for all the fun
tions that you write. Note
that in the spe
i
ation it is important to not write how the fun
tion does what it does, but
only what the fun
tion does, and for what pre
onditions.
A des
ription of how the fun
tion does what it does, often referred to as the des
ription
of the implementation of the fun
tion is also important. But this should be kept separate
from the spe
i
ation. The des
ription of how
an be in a separate do
ument, or
ould be
written as
omments in the body of the
ode of the fun
tion. For example, the following
omment might be useful to explain how the g
d fun
tion works.
// Note the theorem: If n divides m, then GCD(m,n) = n.
//
If n does not divide m, then GCD(m,n) = GCD(n, m mod n)
Every fun
tion (or
ommand) does not need to return a value. You have already seen su
h
fun
tions, e.g. forward, whi
h
auses the turtle to move forward, but itself does not stand
149
for any value. The
ommand forward is predened for you, but you
an also dene new
fun
tions or
ommands that do something and do but do not return a value.
For example, you might wish to build a fun
tion whi
h draws a polygon with a given
number of sides, and having a
ertain given sidelength. Clearly, it must take two arguments,
an integer giving the number sides, and a double giving the side length. Suppose we name
it polygon. The fun
tion does not return any value, so we are required to spe
ify the return
type in the denition to be void. Also, sin
e nothing is being returned, we merely write
return with no value following it.
void polygon(int nsides, double sidelength)
// draws polygon with spe
ified sides and spe
ified sidelength.
// PRE-CONDITION: The pen must be down, and the turtle must be
// positioned at a vertex of the polygon, pointing in the
lo
kwise
// dire
tion along an edge.
// POST-CONDITION: At the end the turtle is in the same position and
// orientation as at the start. The pen is down.
{
for(int i=0; i<nsides; i++){
forward(sidelength);
right(360.0/nsides);
}
return;
}
Note the pre
ondition: it states where the polygon is drawn in
omparison to where the
turtle is pointing. Similarly, we should mention where the turtle is at the end, this will be
needed in order to know how to draw subsequently. A
ondition su
h as this one, whi
h will
be true after the exe
ution of the fun
tion, is said to be a post-
ondition of the fun
tion. A
post-
ondition is also a part of the spe
i
ation.
We would like to develop a program using whi
h it is possible to write on the s
reen using
our turtle. For example, we might want to write \IIT MUMBAI". How should we organize
su
h a program?
A natural (but not ne
essarily the best, see the exer
ises) way of organizing this program
is to have a separate fun
tion for writing ea
h letter. For example, we will have a fun
tion
drawI for drawing the letter 'I'. Suppose we de
ide that we will write in a simple manner, so
that the letter 'I' is just a line, without the serifs (horizontal lines at the top and bottom),
i.e. as .
What is the spe
i
ation of drawI? Clearly it must draw the line as needed. But where
should the line get drawn? This must be mentioned in the spe
i
ations. It is tempting to
say that the line will get drawn at the
urrent position of the turtle, in the dire
tion the
turtle is pointing. Is this really what we want? Keep in mind that you dont just want to draw
one letter, but a sequen
e of letters. So it is important to bring the turtle to a
onvenient
position for drawing subsequent letters. And what is that
onvenient position?
I
150
forward(sp/2);
penDown();
left(90);
forward(ht);
penUp();
left(180);
forward(ht);
left(90);
forward(sp/2);
return;
Fun
tions for other letters are left as exer
ise for you. So assume that you have written
them. Then to write our message, our main program
ould be as follows.
main_program{
int ht=100, sp=10;
turtleSim();
left(90);
// turtle is pointing East at the beginning.
drawI(ht,sp);
drawI(ht,sp);
drawT(ht,sp);
forward(sp);
drawM(ht,sp);
151
drawU(ht,sp);
drawM(ht,sp);
drawB(ht,sp);
drawA(ht,sp);
drawI(ht,sp);
loseTurtleSim();
A remark is in order. You will see that there are lo
al variables named ht and sp in the main
program, as well as the fun
tions have parameters
alled ht and sp. This is a
eptable. When
the fun
tion is being exe
uted, the exe
ution refers only to its a
tivation frame, and hen
e
the variables in the main program are not visible. When the main program is exe
uting, the
a
tivation frame of the fun
tions is not even present, so there is no
onfusion possible.
There are a few seemingly simple things we
annot do using our
urrent notion of a fun
tion.
For example, we might want to write a fun
tion whi
h takes as arguments the Cartesian
oordinates of a point and returns the Polar
oordinates. This is not immediately possible
be
ause a fun
tion
an only return one value, not two. Another example is: suppose we want
to write a fun
tion
alled swap whi
h ex
hanges the values of two integer variables. Suppose
we dene something like the following.
void swap(int a, int b){
int temp;
temp = a;
a = b;
b = temp;
}
// will it work?
If we
all this by writing swap(p,q) from the main program, we will see it does not
hange
the values of p,q in the main program. The reason for this is that when swap exe
utes,
it does ex
hange the values a,b, but a,b are in the a
tivation frame of swap, and their
ex
hange does not have any ee
t on the values of p,q whi
h are in the a
tivation frame of
the main program.
As a third example,
onsider the mark averaging program from Chapter 7. An important
step in this program is to read the marks from the keyboard and
he
k if the marks equal
200. If the marks equal 200, then the loop needs to terminate. Here is an attra
tive way to
write the program.
int main(){
double nextmark, sum=0;
int
ount=0;
while(read_marks_into(nextmark)){
sum = sum + nextmark;
}
}
152
ount = ount + 1;
out << ``The average is: `` << sum/ ount << endl;
Our hope is that we
an write a fun
tion read marks into that will behave in the following
manner. It will read the next mark into the variable given as the argument, and also return
a true or false depending upon whether the reading was su
essful, i.e. true if the value read
was not 200, and false if it was. But what we have learned so far does not allow us to write
this fun
tion: The value of the argument nextmark will be
opied to the parameter of the
fun
tion, but will not be
opied ba
k.
It turns out that all the 3 problems listed above have a ni
e solution in C++. This
solution is based on another way of passing arguments to fun
tion,
alled
all by referen
e.
We will see this next.
Following that we will see how the problem is solved in the C language. As you might
know, C++ is
onsidered to be an enhan
ed version of C. There are a number of reasons for
dis
ussing the C solution. First of all, it is good to know the C solution be
ause C is still in
use, substantially. Also, you may see our so
alled C solution in C++ programs written by
someone, be
ause essentially all C programs are also C++ programs. Se
ond, the C solution
uses the notion of pointers, whi
h are needed in C++ also. Finally, the C solution is in fa
t
a less magi
al version of the
all by referen
e solution of C++. So in
ase you
are, the C
solution might help you understand \what goes on behind the s
enes" in
all by referen
e.
The idea of
all by referen
e is simple: when you make a
hange to a fun
tion parameter
during exe
ution, you want the
hange to be re
e
ted in the
orresponding argument? Just
say so and it is done! The way to \say so" is to de
lare the parameter whose value you
want to be re
e
ted as a referen
e parameter, by adding an & in front of the name of the
parameter. So here is how we might write the fun
tion to
onvert from Cartesian to Polar.
void Cartesian_To_Polar(double x, double y, double &r, double &theta){
r = sqrt(x*x + y*y);
theta = atan2(y,x);
}
In this fun
tion, r and theta have been de
lared to be referen
e parameters. No storage
is allo
ated for a referen
e parameter in the a
tivation frame of the fun
tion, nor is the
value of the
orresponding argument
opied. Instead, during the exe
ution of the fun
tion, a
referen
e parameter dire
tly refers to the
orresponding argument. Hen
e whatever
hanges
the fun
tion seems to make to a referen
e parameter are really made to the
orresponding
argument dire
tly.
This
an be
alled in the normal way, possibly as follows.
int main(){
153
Here is how the
all CartesianToPolar(x1,y1,r1,theta1) exe
utes. The values of x1,y1
are
opied to the
orresponding parameters x,y. However, as mentioned, the values of r1,
theta1 are not
opied. Instead, all referen
es to r, theta in the fun
tion are deemed to
be referen
es to the variables r1,theta1 instead! Thus, as CartesianToPolar exe
utes, the
assignments in the statements r=... and theta=... get made to r1
and theta1
p dire
tly.
So indeed, when the fun
tion returns, the variable r1 would
ontain p1 + 1 = 2 1:4142,
and theta1 would
ontain tan 1 = =4 0:785, and these would be printed out.
The fun
tion to swap variable values
an also be written in a similar manner.
1
int main{
int x=5, y=6;
swap2(x,y);
out << x << " " << y << endl;
}
Both the arguments of swap2 are referen
es, and so nothing is
opied into the a
tivation
area of swap2. The parameters a,b refer dire
tly to x,y, i.e. ee
tively we exe
ute
temp = x;
x = y;
b = temp;
This will
learly ex
hange the values of x,y, so at the end \6 5" will be printed.
Our last example is the read marks into fun
tion dis
ussed in Se
tion 9.6.
bool read_marks_into(int &var){
in >> var;
return var != 200;
}
This denition will work as desired with the main program given in Se
tion 9.6. When the
fun
tion exe
utes, the rst line will read a value into var. But var is a referen
e for the
orresponding parameter nextmark, and hen
e the value will in fa
t be read into nextmark.
The expression var != 200 is true if var is not 200, and false if it is 200. So the while
loop in the main program will indeed terminate as soon as 200 is read. Continuing the
dis
ussion at the end of Se
tion 7.2, we note that perhaps this is the ni
est way of writing
the mark averaging program: we do not dupli
ate any
ode, and yet the loop termination is
by
he
king a
ondition at the top, rather than using a break statement in the body.
154
9.7.1 Remarks
Call by referen
e is very
onvenient. However two points should be noted.
The manner by whi
h we spe
ify arguments of a fun
tion in a
all is the same, no matter
whether we use
all by value or by referen
e for a parameter. This makes it hard to read
and understand
ode. When we see a fun
tion
all, we need to either nd the fun
tion
denition or de
laration (Se
tion 11.2.1) to know whi
h of the arguments, if any,
orrespond
to referen
e parameters, and hen
e might
hange when the fun
tion returns. The C language
solution whi
h uses pointers, dis
ussed next, does not have this drawba
k. On the other had
it has other drawba
ks, as you will see.
If a
ertain parameter in a fun
tion is a referen
e parameter, then the
orresponding
argument must be a variable. For example, we
annot write swap2(1,z). This would make
a,b refer to 1,z respe
tively and then statements in the fun
tion su
h as a = b; would have
to mean something like 1 = z;, whi
h is meaningless. So supplying anything other than a
variable is an error if the
orresponding parameter is a referen
e parameter. However, also
see the dis
ussion about
onst referen
e parameters in Se
tion 15.2.1.
9.7.2 Referen
e variables
In the dis
ussion above we noted that a referen
e parameter should be thought of as just a
name, what it is a name of is xed only when we make the
all. In a similar manner, we
an
have referen
e variables also.
int x = 10;
int &r = x;
out << r << endl;
r = 20;
out << x << endl;
The rst statement denes the variable x and assigns it the value 10. The se
ond statement
de
lares a referen
e r, hen
e the & before the name. In the de
laration itself we are obliged
to say what the name r is refers to. This is spe
ied after =. Thus the se
ond statement
de
lares the integer referen
e r whi
h is a referen
e to x, or just another name for the variable
x. In the third statement, we print r, sin
e this is a name for the variable x, the value of
that, 10, gets printed. In the fourth statement, we assign 20 to r, but sin
e r is just a name
for x, it really
hanges the value of x. Finally, this
hanged value, 20, gets printed in the last
statement.
The utility of referen
e variables will be
ome
lear later, in Se
tion 25.3.3.
9.8 Pointers
We rst dis
uss pointers in general, and then say how they are helpful in solving the problems
of Se
tion 9.6.
We know from Se
tion 2.5.1 that memory is organized as a sequen
e of bytes, and the
ith byte is supposed to have address i, or be at address i. When memory is allo
ated to a
variable, it gets a set of
onse
utive bytes. The address of the rst byte given to the variable
is also
onsidered to be the address of the variable.
155
This will print out the address of p. Note that the
onvention in C++ is to print out
addresses in hexade
imal, so you will see something that begins witn 0x, whi
h indi
ates
that following it is an hexade
imal number. Note that in hexade
imal ea
h digit takes value
between 0 and 15. Thus some way is needed to denote values 10, 11, 12, 13, 14, 15, and for
these the letters a,b,
,d,e,f respe
tively are used.
9.8.2 Pointer variables
We
an store addresses into variables if we wish. But for this we need to dene variables of
an appropriate type. For example, we may write:
int p=15;
int *r;
// not ``int multiplied by r''!
r = &p;
out << &p << " " << r << endl;
See below.
The rst statement de
lares a variable p as usual, of type int. The next statement should
be read as (int*) r;, i.e. int* is the type and r is the name of the de
lared variable. The
type int* is used for variables whi
h are expe
ted to
ontain addresses of int variables.
This is what the third statement does, it stores the address of the int type variable p into
r. If you exe
ute this
ode, you will see that the last statement will indeed print identi
al
hexade
imal numbers.
Figure 9.3 s
hemati
ally shows a snapshot of memory showing the ee
t of storing the
address of p into r. In this we have assumed that p is allo
ated bytes 104 through 107, and
r is allo
ated bytes 108 through 111. The address of p, 104, appears in bytes 108 through
111, as a result of the exe
ution of r = &p;.
Likewise we may write:
double q;
double* s = &q;
Here we have de
lared and initialized s in the same statement. Note that double* and int*
are dierent types, and you may not write s = &p; or r = &q;.
Variables r,s are said to be pointers to p,q. In general, variables of type T* where T
is a type are said to be pointer variables.
Finally, even though we use integers to number memory lo
ations, it is never ne
essary
in C++ programs to expli
itly store a spe
i
onstant, say 104, into a pointer variable. If
156
= &p;
you somehow
ome to know that 104 is the address of a
ertain variable v, and so you want
104 stored in some pointer variable w, then you
an do so by writing w = &v;, without using
the number 104 itself. In fa
t, it is a
ompiler error in C++ to write something su
h as
w=104, where w is a pointer, e.g. of type int*. Be
ause you dont need to write this, if you
a
tually do, it is more likely to be a typing mistake. So the
ompiler
ags it as an error.
Finally it should be noted that C++ de
larations are a bit
onfusing. The following
int* p, q;
// both pointers?
de
lares p to be a pointer to int, while q is simply an int. Even if you put no spa
e between
int and * in the above statement, the * somehow \asso
iates" with p than with int.
9.8.3 Dereferen
ing operator *
If we know the address of a variable, we
an get ba
k that variable by using the dereferen
ing
operator, *. Very simply put, the unary *
an be
onsidered to be the inverse of &. The
hara
ter * also denotes the multipli
ation operator, and is also used in de
laration of pointer
variables, but it will be
lear from the
ontext whi
h operator is meant.
Formally, suppose xyz is of type T* and has value v. Then we
onsider the memory at
address v to be the starting address of a variable of type T, and *xyz denotes this variable.
The unary * is to be read as \
ontent of", e.g. an expression su
h as *xyz above is to be
read as \
ontent of xyz".
For an example,
onsider the denitions of p,r as given above. Then to nd what *r
means, we note that r is of type int* and has value &p. Thus *r denotes a variable of type
int stored at address &p. But p is exa
tly su
h a variable. Hen
e *r denotes the variable p
itself. Thus, if *r were to appear on the left hand side of an assignment statement, we would
really be storing a value into p. If *r appeared on the right hand side of an assignment, or
in an expression, we would be using the value of p in pla
e of the expression *r. Thus we
may write (after the
ode int p=15; int *r; r = & p;):
*r = 22;
int m;
m = *r;
157
In the rst statement, we would store 22 into p. In the third statement, we would store the
value of p, 22 in this
ase, into m.
9.8.4 Use in fun
tions
We rst note that fun
tions
an take data of any type as arguments, in
luding types su
h
as int* or double*. Thus we
an write a fun
tion to
ompute the polar
oordinates given
Cartesian as follows.
void CartesianToPolar(double x, double y, double* pr, double* ptheta){
*pr = sqrt(x*x + y*y);
*ptheta = atan2(y,x);
}
Let us rst make sure that the types of the arguments in the
all and the parameters in
the fun
tion denition mat
h. The rst and se
ond parameters, x, y are required to be a
double, and indeed the rst and se
ond arguments are both 1.0, of type double. The third
parameter pr is of type double*. The third argument is the expression &r, whi
h means the
address of r. Sin
e r is of type double, the type of &r is indeed double*, and hen
e the
type of the third argument and the third parameter mat
h. Similarly the type of the fourth
argument &theta is also seen to mat
h the type double* of the fourth parameter. So
learly
our program should
ompile without errors.
Let us see how this will exe
ute. When the fun
tion CartesianToPolar is
alled, none
of the parameters are referen
e parameters, and so all arguments have to be
opied rst. So
1.0 is
opied to the parameter x in the a
tivation frame of CartesianToPolar. The se
ond
argument 1.0 is
opied to y. The third argument &r is
opied to pr, and nally the fourth
argument &theta is
opied to ptheta.
Then the body of the fun
tion is exe
uted.
The rst statement is *pr = sqrt(x*x +
p
y*y);. The right hand side evaluates to 2, be
ause x and y are both 1. This value is to
be pla
ed in the variable denoted by the left hand side. Now *pr is interpreted exa
tly as
des
ribed in Se
tion 9.8.3. Given that pr is of type double*, the expression *pr denotes
that double variable whose address appears in pr. But we pla
ed the address of r of the
main program in pr. Hen
e *pr denotes the variable r of the main program. Hen
e the
statement
p *pr=sqrt(x*x + y*y), even if it appears in the
ode of CartesianToPolar will
store 2 into the variable r of main.
Next let us
onsider the statement *ptheta = atan2(y,x);. Sin
e y,x are both 1, the
ar
tangent will be found to be =4 0:785. Reasoning as before, the expression *ptheta
will denote the variable theta of the main program. Thus 0.785 will be stored in theta of
158
p
After this the
all will terminate. When the exe
ution of main resumes, 2 and 0.785
would get printed by the last statement in main.
We next
onsider the swap fun
tion. It should be
lear to you now what we should
do: instead of using the variables as arguments, we should use their addresses. Here is the
fun
tion.
main.
The arguments to the
all are &x, &y, having type int*, be
ause x,y are of type int. Thus
they mat
h the types of the parameters of the fun
tion. Thus our program will be
ompiled
orre
tly.
So let us
onsider the exe
ution. The address of x will be
opied into pa, and the address
of y into pb. Thus we may note that *pa in swap will really refer to the variable x of the main
program, and *pb in swap will refer to the variable y of the main program. The statement
temp = *pa; will
ause the value of x to be
opied to temp. In the next statement, the value
of y is
opied to x. The last statement
auses the value in temp, i.e. the value in x at the
beginning to be
opied to y (whi
h is what *pb denotes). The fun
tion
all
ompletes. The
main program then resumes and will print the ex
hanged values, 6 and 5.
The
hanges required to the main program for mark averaging and the fun
tion read marks into
are left as exer
ises.
9.8.5 Referen
e vs. Pointers
You have seen that there are two ways of writing the fun
tions Cartesian To Polar, swap
and read marks into. Whi
h one is better?
Clearly, the fun
tions are easier to write with
all by referen
e. So that is
learly to be
re
ommended in C++ programs.
2
You are probably wondering: when a fun
tion exe
utes, and some parameter is a referen
e parameter,
how does the
omputer know what variable the parameter refers to? A simple answer is: at the time of
the
all, C++ automati
ally sends the address of the variables referred to by the referen
e parameters to
the fun
tion a
tivation frame. Also, during the fun
tion exe
ution, C++ itself dereferen
es the address of
the referen
e variables, and gets to the variables as needed. So in other words, the operations of sending
addresses and dereferen
ing them that had to be manually written out in C are performed \behind the
s
enes" by C++.
2
159
It is possible to return referen
es and pointers from fun
tions. But this has to be done
with
are. We will explain the idea, but for interesting use of it you will have to wait till
Se
tion 16.4, Se
tion 19.3.3 or Se
tion 19.3.4.
First of all, in order to return a referen
e to a type T the return type of the fun
tion must
be given as T&, as you might expe
t. Here is an example.
int &f(int &x, int &y){
if(x > y) return x;
else return y;
}
main_program{
int p=5, q=4;
f(p,q) = 2;
// fun
tion
all appears on the left!
out << p <<' '<< q << endl;
out << f(p,q) << endl;
}
This main program
ontains a fun
tion
all on the left hand side of an expression! Normally,
a fun
tion returns a value, and there is no notion of assigning one value to another value!
However, we
an
ertainly assign a value to a referen
e, and hen
e a fun
tion that returns a
referen
e
an indeed be on the left hand side of an assignment statement. Thus the statement
will exe
ute by evaluating the value of the right hand side, whi
h will then be pla
ed in the
variable that the left hand side refers to.
So
onsider the exe
ution of f(p,q)=2; in the above
ode. Noting that p,q are being
passed by referen
e, the referen
es x,y will refer respe
tively to the variables p,q of the main
program. Thus the expression x>y is identi
al to p>q, where p,q are variables in the main
program. Thus x>y will evaluate to true. Thus the statement return x; will be exe
uted.
Be
ause the fun
tion has return type referen
e to int (int), the value of x (whi
h is the
value of the variable it refers to) is not returned, but the referen
e itself is returned. Sin
e x
is a referen
e to p, the
all returns a referen
e to p. But then the main program statement
f(p,q) = 2; is equivalent to p = 2;. Hen
e the statement will
ause p to be
ome 2. Thus
the print statement will print \2 4".
Note that a
all to a fun
tion that returns a referen
e does not have to be on the left hand
side of the assignment. Indeed, in the last line, we print f(p,q). When this
all exe
utes,
x,y refer to p,q as you might expe
t. The
omparison x>y is now false, be
ause p now has
value 2. Thus y is returned by referen
e, i.e. a referen
e to q is returned. Thus the last
statement,
out << f(p,q) << endl; is equivalent to the statement
out << q << endl;,
and hen
e the value 4 will get printed.
It should be noted that returning a referen
e
an be dangerous.
double &h(){
double x = 5;
return x;
}
int main(){
160
h() = 7;
The fun
tion
all h() will return a referen
e to the lo
al variable x of the fun
tion. Unfortunately, after the fun
tion returns, the lo
al variable will no longer exist! Thus in the
main program it will be in
orre
t to either modify h() or get its value. Note that most
C++
ompilers will not give any errors for the
ode above. However, this
ode is denitely
in
orre
t.
Similar ideas apply to returning pointers. The analogue of the rst example above is as
follows.
int *f(int *x, int *y){
if(*x > *y) return x;
else return y;
}
int main(){
int p=5, q=4;
*f(&p,&q) = 2;
out << p <<' '<< q << endl;
out << *f(&p,&q) << endl;
}
In this, the
all f(&p,&q) returns the address of a variable, so it
an be dereferen
ed and
then we
an either modify the variable or get its value. Thus the rst print statement will
print \2 4" and the se
ond will print 4 as before.
Analogously it is in
orre
t to return the address of a variable that will be deallo
ated by
the time the address
an be used.
double *h(){
double x = 5;
return &x;
}
int main(){
*h() = 7;
}
The fun
tion
all h() returns the address of the variable x lo
al to the fun
tion
all. The
variable is destroyed when the fun
tion
all returns. Hen
e it is in
orre
t to use h() in any
way in main.
It is possible to assign default values to parameters of a fun
tion. If a parti
ular parameter
has a default value, then the
orresponding argument may be omitted while
alling it. The
default value is spe
ied by writing it as an assignment to the parameter in the parameter
list of the fun
tion denition. Here is a polygon fun
tion in whi
h both parameters have
default values.
161
Given this denition, we are allowed to
all the fun
tion either by omitting the last argument,
in whi
h
ase the sidelength parameter will have value 100, or by omitting both parameters,
in whi
h
ase the nsides parameter will have value 4 and sidelength will have value 100. In
other words, we
an make a
all polygon(5) whi
h will
ause a pentagon to be drawn with
side length 100. We
an also make a
all polygon() for whi
h a square of sidelength 100 will
be drawn. We are free to supply both arguments as before, so we may
all polygon(8,120)
whi
h will
ause an o
tagon of sidelength 120 to be drawn.
In general, we
an assign default values to any sux of the parameter list, i.e. if we wish
to assign a default to the ith parameter, then a default must also be assigned to the i + 1th,
i + 2th and so on.
Further, while
alling we must supply values for all the parameters whi
h do not have
a default value, and to a prex of the parameters whi
h do have default values. In other
words, if the rst k parameters of a fun
tion do not have default values and the rest do, then
any
all must supply values for the rst j parameters, where j k.
C++ allows you to dene multiple fun
tions with the same name, provided the fun
tions
have dierent parameter type lists. This
omes in handy when you wish to have similar
fun
tionality for data of multiple types. For example, you might want a fun
tion whi
h
al
ulates the g
d of not just 2 numbers, but several, say 3 as well as 4. Here is how you
ould dene fun
tions for doing both, in addition to the g
d fun
tion we dened earlier.
int g
d(int p, int q, int r){
return g
d(g
d(p,q),r);
}
int g
d(int p, int q, int r, int s){
return g
d(g
d(p,q),g
d(r,s));
}
The above fun
tions in fa
t assume that the previous g
d fun
tion exists. Here is another
use. You migth want to have an absolute value fun
tions for double data as well as int.
C++ allows you to give the name Abs to both fun
tions.
int Abs(int x){
if (x>0) return x;
162
While it is
onvenient to have the same name in both
ases, you may wonder how does the
ompiler know whi
h fun
tion is to be used for your
all. The answer is simple: if your
all
is abs(y) where y is int then the rst fun
tion is used, if the type is double then the se
ond
fun
tion is used. Likewise, the right g
d fun
tion will be pi
ked depending upon how many
arguments you supplied.
You might look at the two abs fun
tions we dened in the pre
eding se
tion and wonder:
sure the fun
tions work on dierent types, but the bodies are really identi
al,
ould we not
just give the body on
e and then have the
ompiler make
opies for the dierent types? It
turns out that this
an also be done using the notion of fun
tion templates as follows.
A fun
tion template does not dene a single fun
tion, but it denes a template, or a
s
heme, for dening fun
tions. The template has a
ertain number of variables: if you x
the values of the variables, then you will get a fun
tion! Here is an example.
template<typename T>
T Abs(T x){
if (x>0) return x;
else return -x;
}
The name T is the template variable. You
an put in whatever value you want, and it will
dene a fun
tion. In fa
t C++ will put in the value as needed! So if you have the following
main program
int main(){
int x=3;
float y=-4.6;
Then C++ will
reate two Abs fun
tions for you. On seeing the rst
out statement, C++
will realize that it
an
reate an Abs fun
tion taking a single int argument by setting T to
int. Likewise T will be set to float and another fun
tion will be generated for use in the
last statement.
A more interesting example is given in the exer
ises.
163
Exer ises
=
=
=
=
=
&b;
&x;
y;
*x;
*b;
Ea
h of the assignments is in
orre
t. Can you guess why? If not, write the
ode in a
program,
ompile it, and the
ompiler will tell you!
7. Write a fun
tion to nd roots of a fun
tion f using Newton's method. It should take
as arguments pointers to f and also to the derivative of f.
p
8. The k-norm of a ve
tor (x; y; z) is dened as k xk + yk + zk . Note that the 2-norm is
in fa
t the Eu
lidean length. Indeed, the most
ommonly used norm happens to be
the 2 norm. Write a fun
tion to
al
ulate the norm su
h that it
an take k as well
as the ve
tor
omponents as arguments. You should also allow the
all to omit k, in
whi
h
ase the 2 norm should be returned.
Chapter 10
Re
ursive Fun
tions
We are now in a position to dis
uss what is perhaps the most powerful, most versatile problem
solving te
hnique ever: re
ursion. What we are going to present will not really
ontain any
new C++ statements. Rather, what you have learned so far will be used, possibly in a
manner whi
h might surprise you, to solve some di
ult
omputational problems in a very
su
in
t manner.
A fundamental idea in designing algorithms is problem redu
tion. The notion is very
ommon in Mathemati
s, where we might say \Using the substitution y = x + x the
quarti
(fourth degree) equation (x + x + 5)(x + x + 9) + 7 = 0 redu
es to the quadrati
(y + 5)(y + 9) + 7 = 0.". Of
ourse, redu
ing one problem into another is useful only if the
new problem is in some sense easier than the original. This is true in our example: quadrati
equations are easier to solve than quarti
. The strategy of redu
ing a problem to another is
easily expressed in programs: the fun
tion we write for solving the rst problem will
all the
fun
tion for solving the se
ond problem. We saw examples of this in the previous
hapter.
An interesting
ase of problem redu
tion is when the new problem is of the same type
as the original problem. In this
ase the redu
tion is said to be re
ursive. This idea might
perhaps appear to be strange, but it is in fa
t very
ommon. Consider the following rules
for dierentiation:
d
d
d
(
u + v) = u + v
dx
dx
dx
2
d
d
d
(
uv ) = v u + u v
dx
dx
dx
The rst rule, for example, states that the problem of dierentiating the sum u + v is the
same as that of rst dierentiating u and v separately, and taking their sum. You have
probably used these rules without realizing that they are re
ursive. There are two reasons
why these rules work:
1. The redu
ed problems are a
tually simpler in some pre
ise sense. In our example,
the problem of dierentiating u or of dierentiating v are indeed simpler than the
problem of dierentiating u + v, be
ause u and v are both smaller expressions than
u + v . Noti
e that when we redu
e one problem to a problem of another type (nonre
ursive redu
tion), the new problem is required to be of a simpler type. For re
ursive
redu
tion, it is enough if the new problem is of a smaller size.
164
165
2. Eventually we must have a way to solve some problems dire
tly { we
annot just keep
redu
ing problems indenitely. The problems whi
h we expe
t to solve dire
tly are
alled the base
ases. Considering dierentiation again, suppose we wish to
ompute:
d
(x sin x + x)
dx
Now, the
omputation of dxd x is not done by further redu
tion, i.e. this is a base
ase
for the pro
edure. So in this
ase we dire
tly write that dxd x = 1. To
ompute dxd x sin x
wed
ould use the produ
t rule given above, and we would need to know the base
ase
sin x =
os x.
dx
Even on a
omputer, re
ursion turns out to be extremely useful. In this
hapter we will see
several examples of the idea.
Eu
lid's algorithm for GCD is naturally expressed re
ursively, as it turns out. Here is Eu
lid's
theorem, restated for
onvenien
e.
Theorem 2 (Eu
lid) Let m; n be positive integers. If m%n = 0 then GCD(m; n) = n. If
m%n 6= 0 then GCD(m; n) = GCD(n; m%n).
The theorem essentially says that either the GCD of m; n is n, or it is the GCD of
n; m%n. But this is exa
tly like saying that the derivative of an expression
an be written
down dire
tly or it is the derivative of some simpler expression. Following the analogy, it
would seem tempting, to
all g
d from inside of itself.
int g
d(int m, int n)
// finds GCD(m,n) for positive integers m,n
{
if(m % n == 0) return n;
else return g
d(n,m % n);
}
And the most interesting thing is that it works! In the last
hapter we sket
hed out the
me
hanism used to exe
ute fun
tions, and it turns out that the same me
hanism will
orre
tly
ompute the GCD using the above
ode. We will see an example and a general proof shortly.
The fun
tion g
d as dened above
alls itself. Su
h fun
tions are said to be re
ursive.
166
Suppose the main program begins exe
ution. Immediately it
omes upon the
all g
d(36,24).
As we know, this
auses an a
tivation re
ord to be
onstru
ted for the
all, and in this a
tivation re
ord the parameters m,n are assigned the value 36,24 respe
tively.
Now the exe
ution begins in the new a
tivation re
ord. The rst
he
k, m % n == 0
fails, be
ause 36 % 24 is 12 and not 0. Thus we exe
ute the else part. But this
ontains
the
all g
d(n, m % n), i.e. g
d(24,12). Our fun
tion
all exe
ution me
hanism must be
used again. Thus another a
tivation re
ord is
reated, this time for g
d(24,12), and m,n in
this re
ord are set to 24,12 respe
tively. Also, the exe
ution of the
urrent
all, g
d(36,24),
suspends. Figure 10.1 shows the state of the world at this time in the exe
ution.
The exe
ution begins in the new a
tivation re
ord. We exe
ute the rst statement whi
h
requires us to
he
k if m % n == 0, i.e. 24 % 12 == 0. This is indeed true. So we exe
ute
the statement return n. This
auses 12 to be returned. Where does this value go? It goes
ba
k to the pla
e from where the
urrent re
ursive
all was made. Sin
e the
urrent
all
was made while pro
essing the se
ond a
tivation re
ord, the value 12 is returned there. The
se
ond a
tivation then
ontinues its exe
ution. However, there isnt mu
h more left to in this
a
tivation. This
ode was to return the value of g
d(24,12) { now that this value is known,
12, it is returned ba
k. So the value 12 is returned also from the se
ond a
tivation. This
goes ba
k to the rst a
tivation. The rst a
tivation resumes from where it was suspended.
As per its
ode, it prints out the re
eived value, 12, and then main terminates.
So as you
an see, the
orre
t value was
omputed and printed.
The number of times a fun
tion is
alled is
alled the depth of the re
ursion. In the
present example, the depth is 2. For the example
onsidered in Se
tion 7.7, GCD(3977; 943)
you
an
he
k that the depth will be 5, equalling the number of iterations required by the
program in Se
tion 7.7. Please work this out by hand. You will also observe that the values
assigned to m,n in su
essive a
tivation re
ords in the re
ursive program are in fa
t the same
values that m,n re
eive in su
essive iterations of the non-re
ursive program in Se
tion 7.7.
167
168
Figure 10.2 shows a pi
ture whi
h we might
onsider, using some imagination, to be of a
tree, say from the Afri
an Savannas. Our goal in this se
tion is to write a program to draw
169
Height h1 tree
Height h1 tree
Height h tree
170
The pro
ess of drawing is as follows. Clearly, if h = 0, we draw nothing and return.
Otherwise, the gure is drawn in a series of 7 steps.
1. Draw the left bran
h. At the start, be
ause of the pre
ondition, we know that the turtle
is pointing upwards, so it must turn by half the angle that is meant to be between the
bran
hes. Then we move forward by the length of the bran
h.
2. Draw the left subtree. We rst turn so that the turtle is fa
ing the top dire
tion,
be
ause that is a pre
ondition for drawing trees. Then we re
urse. We need to
all
with height h 1, and the bran
h length and angle shrunk by the given shrinkage
value.
3. Go ba
k to the root. After drawing the left subtree, its post
ondition guarantees
that the turtle will fa
e dire
tly upwards. To get ba
k to the root we must turn and
go ba
kwards, whi
h is a
omplished by giving a negative argument to the forward
ommand.
4. Draw the right bran
h. Sin
e the turtle is pointing in the dire
tion of the left bran
h,
we must turn it to the right, and then go forward.
5. Draw the right subtree. This is exa
tly as we did the left.
6. Go ba
k to the root, as we did after drawing the left subtree.
7. Ensure the post
ondition. Finally, we want to honour the post
ondition, so we turn
the turtle so that it fa
es dire
tly upward.
This is expressed as the following program, where we have put
omments to indi
ate the
orresponden
e with the steps des
ribed above.
void tree(int height, float length, float angle, float shrinkage)
// pre
ondition: turtle is at root, pointing up.
// post
ondition: same
{
if(height == 0) return;
// 1. draw the left bran
h
left(angle/2);
forward(length);
// 2. draw the left (sub)tree.
right(angle/2);
tree(height-1, length*shrinkage, angle*shrinkage, shrinkage);
left(angle/2);
forward(-length);
right(angle);
forward(length);
// 3.
go ba k to the root
// 4.
171
To
all the fun
tion, we must supply the arguments, but also ensure the pre
ondition. Sin
e
we know that at the start the turtle is fa
ing right, we must turn it left by 90 degrees so
that it points upwards. So our main program
ould be the following.
int main(){
turtlesim();
left(90);
tree(5,120,120,0.68);
}
wait(5);
172
This
ode is more
ompa
t, be
ause we dont have to worry about managing the post
onditions of the turtle.
However it should be noted that this
ode is only useful to grow trees verti
ally. Suppose
you want to orient the tree at an angle of 60 degrees to the verti
al, then this
ode is useless.
However, the turtle based
ode
an be used, we merely
all it after the turtle is oriented at
the required angle. This feature appears useful for drawing many real trees, i.e. the subtrees
of many trees appear grow at an angle to the verti
al. As a result, to draw realisti
looking
(botani
al) trees, it might be more
onvenient to use the turtle based
ode. See Exer
ise 13.
10.2.2 Hilbert spa
e lling
urve
Figure 10.4 shows
urves H ; H ; H and H , left to right. The exer
ises ask you to understand the re
ursive stru
ture of the
urve, i.e.
an you obtain Hi by
omposing some Hi
urves with some
onne
ting lines. This will help to write a re
ursive fun
tion to draw an
arbitrary
urve Hn. These
urves were invented by the mathemati
ian David Hilbert, and
are examples of so
alled spa
e-lling
urves.
1
173
Suppose you have an unlimited supply of bri
ks of heights 1 and 2. You want to
onstru
t
a tower of height n. In how many ways
an you do this? For example, suppose n = 4. One
possibility is to sta
k 4 height 1 bri
ks, i.e. the order of heights
onsidered top to bottom
is 1,1,1,1. Other orders are 1,1,2, or 1,2,1 or 2,1,1 or 2,2. You
an
he
k by trial and error
that no other orders are possible. Thus if you dene Vn to be the number of ways in whi
h
a tower of height n
an be
onstru
ted, we have demonstrated that V = 5. We would like
to write a program that
omputes Vn given n.
This problem was solved by Virahanka, an Indian prosodist who lived in the 7th
entury.
Virahanka (or quite possibly pre
eding prosodists, about whom denite information is not
known) used the following method to solve the problem, whi
h has been
redited to Pingala,
who lived around 3rd
entury BCE. The rst observation is that the bottom-most bri
k is
either of height 1 or height 2. You may think this is rather obvious, but from this it follows:
4
Vn
Number of ways of
Number of ways of
building a tower
building a tower
Number of ways of
= building a tower of = of height n with + of height n with
bottom-most bri
k
bottom-most bri
k
height n
of height 2.
of height 1
Virahanka observed that if you sele
t the bottom-most bri
k to be of height 1, then the
problem of building the rest of the tower is simply the problem of building a height n 1
tower. Thus we have
Number of ways of
Number of ways of
building a tower
of height n with = building a tower of = Vn
height n 1
bottom-most bri
k
of size 1
Likewise it also follows that
Number of ways of
building a tower
Number of ways of
of height n with = building a tower of = Vn
bottom-most bri
k
height n 2
of height 2
So we have
Vn = Vn + Vn
What we have written above is an example of a re
urren
e, an equation whi
h re
ursively
denes a sequen
e of numbers, V ; V ; : : : in our
ase.
Now we are ready to write a re
ursive program. Clearly, in order to solve the problem
of size n, we need a solution to problems of size n 1 and n 2 respe
tively. So we have
1
174
a pro
edure for redu
ing the size of the problem, what we need is the base
ase. Is there a
problem that we
an solve easily? Clearly, V = 1, be
ause a height 1 tower
an be built in
only 1 way { by using a single height 1 bri
k.
The trouble is, that this single base
ase, V = 1 is not enough. We should really ask
ourselves: will this allow us to nd V ; V and so on? Clearly, we
annot even get V with
just this information. However, we
an try to nd V dire
tly,
learly, there are only 2 ways:
1,1 and 2. So V = 2. But now, as we keep re
ursing, the pairs of numbers we are asking
for redu
e by one, so eventually the we will want the pair 2,1. But we know the values of V
and V , and so the re
ursion will indeed terminate. The program is then immediate.
1
If you run this, you will see that it is very slow, even for modestly large n. The reason for
it
an be seen in Figure 10.5. This gure shows the so
alled exe
ution tree for the
all
Virahanka(6). Note that we have drawn this tree growing downward, as is more
ustomary.
The root is drawn at the top and is marked 6. The root has two bran
hes whi
h are drawn
downward. There is further bran
hing too, however in some pla
es the bran
hing appears
to have stopped prematurely. We will see how to interpret all this next.
It is worth introdu
ing two terms. What we have been
alling a bran
h, is more te
hni
ally
alled an edge. The points where the edges terminate, are
alled verti
es. Thus the root
is a vertex, and there are several others.
In an exe
ution tree, the root vertex
orresponds to the original
all. So we have marked
the root in the pi
ture with the argument, 6, to the original
all. Out of ea
h vertex we have
one downward going edge for every
all made. Sin
e Virahanka(6) requires Virahanka to
be
alled rst with argument 5, and then with 4, we have 2 outgoing bran
hes. At the ends
of the
orresponding bran
hes we have put down 5 and 4 respe
tively, the arguments for
the
orresponding
alls. This goes on till we get to
alls Virahanka(2) or Virahanka(1).
Sin
e these
alls do not make further re
ursive
alls, there are no outgoing bran
hes from
the verti
es
orresponding to these
alls.
As you
an see in the gure, Virahanka(4) is
alled twi
e, on
e as a part of Virahanka(5),
and on
e dire
tly from Virahanka(6). But on
e we know V using any
all to Virahanka(4)
subsequent
alls are not really ne
essary if we
an just remember this value. In fa
t, you will
see that the
all Virahanka(3) happens 3 times, the
all Virahanka(2) happens 5 times,
and the
all Virahanka(1) happens 8 times. So the program is quite wasteful. In general, for
large n, you
an argue (Exer
ise 6) that while
omputing Vn, our fun
tion will make at least
2bn=
alls to Virahanka. Thus if you want to
ompute Vn by using the re
ursive algorithm
you are expe
ting to spend time proportional to at least 2n= . This is a huge number, and
indeed
omputing something like say V using the
all Virahanka(45) takes an enormous
amount of time on most
omputers.
4
45
175
5
4
3
4
3
2
2
176
V1 = 1
V3 = 3
V2 = 2
se
ond last
last
V4
urrent
(a)
V1 = 1
V3 = 3
V2 = 2
se ond last
V4 = 5
last
V5
urrent
+
(b)
177
}
return
urrent;
The important point to note in this
ode is the preparation of the variables se
ondlast and
for the next iteration. This also stresses the important point that we should
onsider
ea
h variable as performing a
ertain fun
tion, e.g. holding the last
omputed Virahanka
number, and the value of the variable must be
hanged to re
e
t its fun
tion.
To
ompute Vn, this fun
tion will require n 2 iterations. Ea
h iteration takes a xed
amount of time independent of n. Thus we
an say that the total time is approximately
proportional to n.
Indeed, you will see that V gets
omputed essentially instantaneously using this loop
based fun
tion.
last
45
In this we will write a program to play the game of Nim. This game is quite simple, but
nevertheless interesting, and our program will
ontain a key idea whi
h will be useful in all
game playing programs.
The game has two players, say White and Bla
k. There are some n piles of stones, the
ith pile
ontaining xi stones at the beginning. We will have dierent games for dierent
hoi
es of xi. A move for a player involves the player pi
king a pile in whi
h there is at least
one stone, and removing one or more stones from that pile. The players move alternately,
say with White moving rst. The player that makes the last valid move, i.e. after whi
h no
stones are left, is
onsidered the winner. Or you may say that the player who is unable to
make a move on his turn be
ause there are no stones left is the loser.
Here is a simple example. Suppose we have only 2 piles initially with 5 and 3 stones
respe
tively. Suppose White pi
ks 4 stones from pile 1. Then the rst pile has 1 stone left
and the se
ond has 3. Now Bla
k
an win by pi
king 2 from the se
ond pile: this will leave
1 stone in ea
h pile, and then White
an pi
k only 1 of them, leaving the last one for Bla
k.
Of
ourse, White need not have pi
ked 4 stones in the very rst move. Is there a dierent
178
hoi
e for whi
h he
an ensure a win? We will leave it to you to observe that White
an in
fa
t win this game by pi
king only 2 stones from the rst pile in his rst move.
So here is the
entral question of this se
tion: Given the game position, i.e. number
of stones in ea
h pile, determine whether the player whose turn it is to play
an win, no
matter what the other player plays. In
ase the position is winning, we would also like to
determine a winning move. Note by the way, that when we say winning/losing position, we
mean winning/losing for the player whose turn it is to move.
In trying to solve any problem, it is a good idea to try out some examples rst. Consider
the simplest possible position: the position in whi
h no pile
ontains any stone be
ause all
were taken earlier. As dened above, in this position, the player whose turn it is is
learly
the loser. The next harder example is: suppose there is only one pile with just one stone.
Clearly, this is a winning position (for the player on move) be
ause he
an take that stone.
In fa
t, if there is just one pile with any number of stones, it is a winning position be
ause
the player on move will take the entire pile.
Let us next
onsider the next more
omplex situation, say there are 2 piles, ea
h with
one stone. There are only two (similar) moves possible, either take the stone in the rst pile,
or the stone in the se
ond pile. In either
ase, we leave behind a single pile with 1 stone,
whi
h is a winning position for our opponent. Thus, an important prin
iple emerges from
this example:
Observation 1: If from a
ertain position P , suppose on every move we go to a
winning position. Then P is a losing position.
This is indeed an important observation. Let us keep going and
onsider more
omplex
situations, say there are two piles, in the rst one there are 2 stones, and the se
ond has
only one. Now we have a
hoi
e of 3 moves:
1. Pi
k one stone from the rst pile. In that
ase, one stone remains in ea
h pile. As we
have dis
ussed, this is a lost position for our opponent. So good for us!
2. Pi
k two stones from the rst pile. In this
ase, we leave just one stone. This is a
winning position, for our opponent. Hen
e not good for us.
3. Pi
k the only stone from the se
ond pile. This leaves behind one pile with two stones.
As dis
ussed above, this is also a winning position, for our opponent. Hen
e this is not
good either.
So in this position, the rst move will make us win while the remaining two will make us
lose. So what do we make of this position? Remember that we have the
hoi
e of what move
to make, and hen
e we will
ertainly
hoose the rst move! So this position is a winning
position for us. This seems to generalize into another observation.
Observation 2: If in some position P there exists a move after whi
h we rea
h a
losing position. Then P is a winning position.
Note that this observation ni
ely
omplements the rst one. If we nd that some move leads
to a losing position, then the se
ond observation applies. If we nd that there is no su
h
move, i.e. all moves lead to a winning position, then the rst observation applies.
179
The above observations gives us a re
ursive algorithm for determining if a given position
is winning or losing. We determine all moves mi possible in P , and the positions Pi they
lead to. The we determine (re
ursively!) whether Pi are winning or losing. If we nd some
Pi that is losing, we de
lare P to be winning. Otherwise if all Pi are winning, we de
lare P
to be losing. We know that in order for a re
ursive algorithm to work we must ensure that
two things:
1. Ea
h subproblem we are required to solve is simpler than the original. In our
ase this
is true in the sense that ea
h Pi must have at least one stone less than P , and is hen
e
simpler.
2. We
an argue that eventually we will rea
h some (\simplest") problems whi
h we
an
solve dire
tly. Clearly, as the game progresses we will rea
h the situation in whi
h no
stone remains. As dis
ussed, this is a losing position.
Thus we
an write a program to determine whether a Nim position is winning or losing. We
give this program for the
ase of 3 piles, but you
an see that it
an be easily extended for a
larger number of piles. The fun
tion winning given below takes a game position and returns
true if the position is winning, and false if the position is losing.
P
return false;
The fun
tion
an be
alled using a main program su
h as the one below.
int main(){
int x,y,z;
out << ``Give the number of stones in the 3 piles: ``;
in >> x >> y >> z;
if (winning(x,y,z))
out << ``Wins.'' << endl;
else
out << ``Loses.''<<endl;
}
180
Our fun
tion only says whether the given position is winning or losing, it does not say what
move to play if it is a winning position. You
an easily modify the program to do this, as
you are asked in the exer
ises.
The logi
of our fun
tion should be
lear. In the given position, we
an
hoose to take
stones from either the rst pile, the se
ond pile, or the third pile. The rst loop in the
fun
tion
onsiders in turn the
ase in whi
h we remove i stones from the rst pile. This
leaves the new position in whi
h the number of stones is x-i,y,z. We re
ursively
he
k if
this is a losing position. If so, the original position, i.e. in whi
h there are x,y,z stones
respe
tively, must be a winning position by observation 2. The subsequent loops
onsider the
ases in whi
h we remove stones from the se
ond and third piles. If we nd no losing position
after
he
king all moves, then indeed the original position must be losing, by observation 1.
So we return false in the last statement of the fun
tion.
10.4.1 Remarks
It turns out that there is a more dire
t way to say whether a given position is winning or
losing. This is very
lever, involving writing the number of stones in the piles in binary, and
performing additions of the bits modulo 2 and so on. We will not
over this, but you should
be able to nd it dis
ussed on the internet.
Our program is nevertheless interesting, be
ause the general stru
ture of the program
applies for many 2 player games. Indeed, re
ursion is an important tool in writing game
playing programs.
181
If you use double you
an work with mu
h larger values of n, but they will be
orre
t only
to 15 digits or so.
Exer ises
1. The fa
torial of a number n is denoted as n!, and
an be dened using the re
urren
e
n! = (n 1)! for n > 0 and 0! = 1. Write a re
ursive fun
tion to
ompute n!.
n
n
n
n
2. The binomial
oe
ient
an
be
dened
re
ursively
as
=
+
, for
r
r
r
r
n
n
n
n; r > 0 and
= n = 1 for all n 0. Write a fun
tion to
ompute r .
3. Consider an equation ax + by =
, where a; b;
are integers, and the unknowns x; y are
required to be integers. Su
h equations are
alled Diaphontine equations. If GCD(a; b)
does not divide
, then the equation does not have any solution. However, the equation
will have innitely many solutions if GCD(a; b) does divide
. Write a program whi
h
takes a; b;
as input and prints a solution if GCD(a; b) divides
.
Hint 1: Suppose a = 1. Show that in this
ase an integer solution is easily obtained.
Hint 2: Suppose the equation is 17x + 10y = 4. Suppose you substitute y = z x.
Then you get the new equation 7x + 10z = 4. Observe that the new equation has
smaller
oe
ients, and given a solution to the new equation you
an get a solution to
the old one.
4. Dedu
e the general stru
ture of Hilbert spa
e lling
urves by observing Figure 10.4.
Draw a pi
ture (on paper) showing how Hn is
omposed of one or more Hn
urves.
This should be in the style of Figure 10.3. Write a turtle based program to draw a
Hilbert spa
e lling
urve Hn given n.
Follow the general s
heme we used in Se
tion 10.2, i.e. begin by stating the pre and
post
onditions for the turtle for drawing Hn. Try to draw the
urve without lifting
the pen or overdrawing.
You may nd the following fa
t useful. Suppose a
ertain fun
tion f draws some gure
F. Then if we repla
e every turning angle in f by , then we will get a gure that
is a mirror image of F.
5. Consider the re
urren
e Wn = Wn + Wn + Wn , with W = W = W = 1. Write
a re
ursive program for printing Wn. Also write a loop based program.
6. Let Bn denote the number of bran
hes in the re
ursion tree for Vn. Thus B = 14,
onsidering Figure 10.5. Note that ea
h bran
h ends in a
all to Virahanka, hen
e Bn
gives a good estimate of the number of operations needed to
ompute Vn. (a) Write a
re
urren
e for Bn and use it to write a program that
omputes Bn. What are the base
ases for this? Make sure your answer mat
hes the bran
hes in the trees of Figure 10.5.
(b) Argue using indu
tion that Bn 2bn=
for n 3.
7. Prove using indu
tion that 2bn=
Vn 2n. Based on this what data type would you
use for
omputing V ? If it helps you may note the stonger result Vn 1:62n 2n= : .
1
1
1
80
1 43
182
8. Suppose you
all the fun
tion g
d on
onse
utive Virahanka numbers Vn; Vn . There
is something interesting about the arguments to the su
essive re
ursive
alls. What is
it? The depth of the re
ursion is dened to be the number of
onse
utive re
ursive
alls
made, ea
h nested inside the pre
eding one. What is the depth of the re
ursion for the
all g
d(Vn ; Vn)? Based on this, argue that the time taken by g
d when
alled on n
bit numbers
ould be as large as n=2.
9. Consider the g
d fun
tion developed in the
hapter. Suppose the initial
all is with
arguments m ; n and su
essive
alls are made with arguments m ; n , then m ; n ,
then m ; n and so on. Show that ni ni . Based on this argue that a
all to g
d
with n bit numbers will have depth of re
ursion at most 2n. In other words, the time
to
ompute the g
d will be at most proprortional to the number of bits in the numbers.
10. Consider a
omplete binary tree with height h. As you
an see, su
h a tree has 2h 1
verti
es. Our goal now is to write a program that not only draws su
h a tree, but also
assigns a unique number for ea
h vertex, in the range 1 through 2h 1. Further, the
number should be printed in the pi
ture, slightly to the right of the vertex itself. The
simplest numbering is the In-order numbering. In this the verti
es are numbered 1
through 2h 1 in left to right order. Thus the leftmost leaf would get the number 1,
the root of the entire tree would get the number 2h, and the rightmost leaf would get
the number 2h 1. You are to modify our program drawing trees without using the
turtle so that this numbering is also printed. Hint: It might be useful have a referen
e
argument to the fun
tion tree that somehow
an be used to de
ide the number of a
node.
11. Another interesting numbering of tree nodes is the Pre-order numbering. The preorder time of a node is simply the time at whi
h the subtree rooted at that node starts
getting drawn. Based on this, the pre-order number of a node is dened to be i if its
preorder time is the ith smallest.
Modify our tree drawing program (not using turtle) so that it prints the pre-order
numbers along with the tree. As examples, note that the root has pre-order number
1, the leftmost leaf the number h + 1, and the rightmost leaf the number 2h 1.
12. Another interesting numbering of tree nodes is the Post-order numbering. The postorder time of a node is simply the time at whi
h drawing of the subtree rooted at that
node is nished. Based on this, the post-order number of a node is dened to be i if
its post-order time is the ith smallest.
Modify our tree drawing program (not using turtle) so that it prints the post-order
numbers along with the tree. As examples, note that the leftmost leaf has post-order
number 1, the root the number 2h 1, and the rightmost leaf the number 2h h 1.
13. More
ommonly, (botani
al) trees have a single trunk that rises verti
ally, and then
splits into bran
hes. So you
ould
onsider a tree to be \one verti
al bran
h, with two
trees growing out of it at an angle". Draw trees expressing this idea as a re
ursive
program. It will be
onvenient to use the turtle for this. Try out variations, nd whi
h
trees look realisti
. .
+1
+1
+2
+1
+1
+1
+1
+1
+1
+1
183
14. Write a fun
tion draw Hem that draws the re
ursion tree for
alls to Virahanka, i.e.
draw Hem(6) should be able to
onstru
t the tree shown in Figure 10.5.
15. The tree drawn in Figure 10.2 is
alled a
omplete binary tree. Binary, be
ause at
ea
h bran
hing point there are exa
tly 2 bran
hes, or at the top, where they are no
bran
hes. Complete, be
ause all bran
hes go to the same height. You
ould have an
in
omplete binary tree also, say you only have one bran
h on one side and the entire
tree on the other.
Write a program whi
h takes inputs from the user and draws any binary tree. Suppose
to any request the user will only answer 0 (false) or (true). Devi
e a system of questions
using whi
h you
an determine how to move the turtle. Make sure you ask as few
questions as possible.
16. Consider the points (i; j ) where 0 i; j < n for some
onstant n, say n = 10. We want
to walk from (0; 0) to (n 1; n 1) taking exa
tly n 1 unit steps in the positive x
dire
tion, and exa
tly n 1 unit steps in the positive y dire
tion. It is a
eptable to
take order the steps in the x and y dire
tion as we wish, i.e. we may alternate them
or take all x steps rst and so on. We must take a total of 2n 2 steps, of whi
h n 1
must be along the x dire
tion. Thus the total number ofn ways
of
hoosing, whi
h is
also the number of distin
t paths that we
an follow, is n . With ea
h path we
an
asso
iate a 2n 2 bit number, whose j th least signi
ant bit is 0 if the j th step on the
path is in the x dire
tion, and 1 if the j th step is in the y dire
tion.
Write a program that takes as input integers n; p and prints out the pth largest number
in the set of all paths. Hint: How many paths will have a 0 in the most signi
ant bit?
17. Suppose you want to send a message using the following very simple
ode. Say your
message only
onsists of the letters 'a' through 'z', and in the
ode your merely repla
e
the ith letter by i. Thus 'a' will be
oded as 1, 'b' as 2, and so on till 'z' by 26. Further,
there are no separators between the numbers
orresponding to ea
h letter. Thus, the
word \bat" will be
oded as the string 2120. Clearly, some strings will have more than
one interpretation, i.e. the string 2120
ould also have
ome from \ut". Suppose you
are given a string of numbers, say 1 digit per line (so 2120 will be given on 4 lines).
You are to write a program that takes su
h a sequen
e of numbers and prints out the
number of ways in whi
h it
an be interpreted. You are free to demand that the input
be given from the last digit if you wish.
18. There are many variations possible on the game of Nim as des
ribed above. One
variation is: the player who moves last loses. How will you determine whether a
position is winning or losing for this new game?
19. In another variation, you are allowed to pi
k either any non-zero number of stones from
a single pile, or an equal number of stones from two piles. Write a fun
tion that says
whether a position is winning for this game.
20. Write a fun
tion whi
h returns a 0 if the given position is losing, but if the position
is winning, returns a value that somehow indi
ates what move to make. De
ide on a
2
2
1
184
suitable en
oding to do this. For example, to indi
ate that s stones should be pi
ked
from pile p, return the number p 10m + s, where 10m is a power of 10 larger than
x; y; z the number of stones in the piles for the given position. With this en
oding, the
last m digits will give the number of stones to pi
k, and the more signi
ant digits will
indi
ate the pile number. Some of you might wonder whether we
an return pairs of
numbers out of a fun
tion (without having to en
ode them as above) { we will see how
to do it later in the book.
21. Write a re
ursive fun
tion for nding xn where n is an integer. Try to get an algorithm
whi
h requires far fewer than the n 1 multipli
ations needed in the natural algorithm
of multiplying x with itself. Hint: Show that k multipli
ations su
e if n = 2k for
some integer k. Then build up on this.
Chapter 11
Program organization and fun
tions
We dis
ussed fun
tions as a way of en
apsulating frequently used
ode so that it
an be
written just on
e and then
alled whenever needed. However, fun
tions also play another
role in C++ programs. Just as a
ell is a basi
physiologi
al and stru
tural unit of life, a
fun
tion
an be
onsidered to be an organizational unit of C++ programs. A C++ program
essentially is a
olle
tion of fun
tions . As we will see shortly, even the main program you
have been writing is turned into a fun
tion by simple
pp, stru
turally similar to the other
fun
tions we have been writing. Managing the fun
tions
onstituting a program, espe
ially
large programs, requires a systemati
approa
h. The problem is further
ompli
ated be
ause
large programs are often developed by teams of programmers, with ea
h programmer possibly
responsible for developing some of the fun
tions. Clearly, it is
onvenient if the dierent
fun
tions needed for a program are in dierent les. In this
hapter, we will see some of the
issues in breaking up a program into fun
tions, possibly spread over multiple les, but yet
able to
all ea
h other and work together as a single program.
One important
hallenge is the management of names. A name is not useful if there is
any ambiguity as to what exa
tly it refers to. On the other hand, if a program is written
ooperatively, there is a
han
e that dierent programmers might use the same name to
dene a fun
tion that they wrote. Or it might be that you are borrowing a pa
kage (su
h as
simple
pp) written by someone else and using it in your program. Ee
tively, this also sets
up the possibility of a name
lash: how do you ensure that you dont use the same name that
is used in the pa
kage that you borrowed? Or if you do use it, will it
ause any ambiguity?
These are some of the issues dis
ussed in this
hapter.
We begin by showing how simple
pp turns the main program that you write into a
fun
tion as required by C++. After that we will see the rules for breaking up a program
into multiple les, or alternatively, for assembling a program given a set of fun
tions spread
over several les. As mentioned above, an important issue will be management of names.
The notion of de
larations will be useful in this. Another useful notion will be that of a
namespa
e, whi
h we will also study.
We mentioned in the introdu
tion that simple
pp hides some of the te
hni
ally advan
ed
features of C++ so as to make it easier for a beginner to get to the heart of our subje
t: how
to write programs. But this
hapter will have introdu
ed you to all the te
hni
al features
hidden by simple
pp. Thus by the end of the
hapter, you will be able to use C++ dire
tly,
1
185
186
if you so
hoose, without having to in
lude simple
pp in your programs. We will dis
uss
this in Se
tion 11.6.
We will end by making some philosophi
al remarks on how to break a program into
fun
tions.
C++ in fa
t requires that the main program be written as the body of a fun
tion
alled
main, whi
h takes no arguments and returns an integer value. We did not tell you all this
at the beginning of the book be
ause at that time you did not know about fun
tions, and
it might have been too overwhelming to nd out. So instead, we asked you to write the
main program in a blo
k following the name main program. Our pa
kage simple
pp uses
the prepro
essor fa
ility (Appendix G) of C++ to
hange the name main program that you
write into the phrase int main(). Thus what you spe
ify as the main program be
omes the
body of a fun
tion
alled main as required.
When a C++ program is
ompiled and run, the operating system expe
ts that there be
a fun
tion in it
alled main. Running a program really means
alling the fun
tion main. The
main fun
tion has return type int be
ause of some histori
al reasons not worth understanding. You may also be wondering why we havent been writing a return statement inside
main if in fa
t it is supposed to return an int. The C++
ompiler we have been using, the
GNU C++
ompiler, ignores this transgression, that's why!
Now that you know that the main program is just another fun
tion, from now on we
will drop the name main program and start writing the main program as a fun
tion named
main. So should you.
It is possible to pla
e the main program and the other fun
tions in dierent les if we wish.
If a program is very large, breaking it up into multiple les makes it easier to manage. A
program
an be partitioned into a
olle
tion of les provided the following rules are obeyed.
Rule 1: If a
ertain fun
tion f is being
alled by the
ode in le F, then the fun
tion f
must be de
lared inside F, textually before the
all to f. Note that a fun
tion denition
is a de
laration, but not vi
e versa. We will see what a de
laration is shortly.
Rule 2: Every fun
tion that is
alled must be dened exa
tly on
e in some le in the
olle
tion.
On
e you have a
olle
tion of les satisfying the above rules, they
an be
ompiled into a
program provided they
ontain a fun
tion main. We will see this with an example shortly.
11.2.1 Fun
tion De
laration or Prototype
The de
laration of a fun
tion states the name of the fun
tion, its return type, and the types
of its arguments. Indeed, a fun
tion de
laration
an be spe
ied by giving its denition
without the body. Here for example are the de
larations of l
m and g
d.
187
The names of the parameters
an be omitted from de
larations, e.g. you may write just
int l
m(int,int); in the de
laration. The de
laration is also
alled a prototype, or even a
signature.
Suppose a
ompiler is pro
essing a le
ontaining the statement
out << l
m(24,36);.
When it rea
hes this statement, it needs to be sure that l
m is indeed a fun
tion, and not some
typing mistake. It also needs to know the type of the value returned by l
m { depending upon
the type the printing will happen dierently. Both these needs are met by the de
laration.
A de
laration of a fun
tion f provides (a) an assuran
e that f as used later in the program
is indeed a fun
tion, and that in
ase it has not been dened so far, it will be dened later
in this le itself or in some other le, (b) a des
ription of the types of the parameters to the
fun
tion and the type of the value returned by the fun
tion. Given the de
laration, the le
an be
ompiled ex
ept for the
ode for exe
uting the fun
tion itself, whi
h
an be supplied
later (Se
tion 11.2.3). Noti
e that a fun
tion denition also provides the information in (a),
(b) mentioned above, and hen
e is a also
onsidered to be a de
laration.
11.2.2 Splitting a program into multiple les
Using the notion of a de
laration, we
an break up programs into multiple les. As an
example,
onsider the main program of Se
tion 9.2 that
alls our fun
tion l
m to nd the
LCM of 36 and 24. Thus there are 3 fun
tions in our program overall: the fun
tion main,
the fun
tion l
m and the fun
tion g
d. Figure 11.1 shows how we
ould form 3 les for the
program.
First
onsider the le g
d.
pp, whi
h
ontains the fun
tion g
d. It does not
all any
other fun
tion, and so does not need to have any additional de
laration. Next
onsider the
le l
m.
pp. This
ontains the fun
tion l
m whi
h
ontains a
all to g
d. So this le has
a de
laration of g
d at the very beginning. Finally the le main.
pp
ontains the main
program. This
alls the fun
tion l
m, so it
ontains a de
laration of l
m at the beginning.
Note that the main program uses the identier
out to write to the
onsole. For this it
needs to in
lude simple
pp. The other les do not
ontain anything whi
h needs servi
es
from simple
pp, so those do not have the line #in
lude <simple
pp> at the top.
There are various ways in whi
h we
an
ompile this program. The simplest possibility
is to issue the
ommand
s++ main.
pp l
m.
pp g
d.
pp
This will produ
e an exe
utable le whi
h will indeed nd the LCM of 36,24 when run.
11.2.3 Separate
ompilation and obje
t modules
But there are other ways of
ompiling as well. We
an separately
ompile ea
h le. Sin
e ea
h
le does not
ontain the
omplete program by itself, an exe
utable le
annot be produ
ed.
What the
ompiler will produ
e is
alled an obje
t module, and this
an be produ
ed by
issuing the
ommand
}
//-----------------------------------------------------------------
(a) The le g
d.
pp
//----------------------------------------------------------------int g
d(int, int);
// de
laration of fun
tion g
d.
int l
m(int m, int n){
return m*n/g
d(m,n);
}
//-----------------------------------------------------------------
(b) The le l
m.
pp
//----------------------------------------------------------------#in
lude <simple
pp>
int l
m(int m, int n);
// de
laration of fun
tion l
m.
int main(){
out << l
m(36,24) << endl;
}
//-----------------------------------------------------------------
(
) The le main.
pp
Figure 11.1: The les in the program to nd LCM
188
189
s++ - filename
The option -
tells the
ompiler to produ
e an obje
t module and not an exe
utable. Here
filename should be the name of a le, say main.
pp. In this
ase, an obje
t module of
name main.o is produ
ed. If dierent programmers are working on dierent les, they
an
ompile their les separately giving the -
option, and they will at least know if there are
ompilation errors.
We
an form the exe
utable le from the obje
t modules by issuing the
ommand s++
followed by the names of the obje
t modules. Thus for our example we
ould write:
s++ main.o g
d.o l
m.o
This use of s++ is said to link the obje
t modules together. The linking pro
ess will
he
k
that every fun
tion that was de
lared in a module being linked is dened either in the same
module or in one of the other modules being linked. After this
he
k, the
ode in the dierent
modules is stit
hed up to form the exe
utable le.
It is a
eptable to mix .
pp les and .o les as arguments to s++, e.g. we
ould have
issued the
ommand
s++ main.
pp g
d.o l
m.o
This would
ompile main.
pp and then link it with the other les. The result main.o of
ompiling will generally not be seen, be
ause the
ompiler will delete it after it is used for
produ
ing the exe
utable.
11.2.4 Header les
Suppose programmers M; G; L respe
tively develop the fun
tions main, g
d, l
m. Then G
has to tell L how to de
lare the fun
tion g
d in the le l
m.
pp. The most natural way
of
onveying this information is to write it down in a so
alled header file. A header le
has a sux .h, or a sux .hpp or no sux at all, like our le simple
pp, whi
h is also a
header le. A simple strategy is to have a header le F.h for every program le F.
pp whi
h
ontains fun
tions used in other les. The le F.h merely
ontains the de
larations of all the
fun
tions in F.
pp that are useful to other les. Thus we might have les g
d.h
ontaining
just the line int g
d(int,int), and l
m.h
ontaining the line int l
m(int,int). Now
the programmer L writing the fun
tion l
m
an read the le g
d.h and put that line into
l
m.
pp. However, it is less errorprone and hen
e more
ustomary that M will merely pla
e
the in
lusion dire
tive
#in
lude "l
m.h"
in his le instead of the de
laration. This dire
tive
auses the
ontents of the mentioned
le, (l
m.h in this
ase) to be pla
ed at the position where the in
lusion dire
tive appears.
The mentioned le must be present in the
urrent dire
tory (or a path
ould be given).
2
The name of the le must typi
ally be given in quotation marks if the le is present in the
urrent
dire
tory or at a pla
e given using an expli
it path. If the le is present in some dire
tory that the
ompiler
is asked to look into using some other me
hanisms, then angled bra
es are used, e.g. #in
lude <simple
pp>.
To get the exa
t details you will need to
onsult the do
umentation of your
ompiler/linker.
2
190
Thus all that is needed in addition is to pla
e the le l
m.h also in the dire
tory
ontaining
Likewise M will pla
e the line #in
lude "l
m.h" in main.
pp, as a result of
whi
h the de
laration for l
m would get inserted into the le main.
pp as needed.
Note that we
ould have used a single header le, say g
dl
m.h
ontaining both de
larations.
main. pp.
int g
d(int,int);
int l
m(int,int);
We
ould in
lude this single le in main.
pp and l
m.
pp. This will
ause both de
larations
to be inserted into ea
h le, while only one is needed. Having extra de
larations is a
eptable.
11.2.5 Header guards
Header les
an be
ome quite involved. If there are several header les, then you
an pla
e
the in
lusion dire
tives themselves in another header le. In
luding the latter le will
ause
the former les to be in
luded. If we have header les in
luded inside one another, it raises
the following possibility: we in
lude some le a.h whi
h in turn in
ludes les b.h and
.h,
both of whi
h in
lude the le d.h. This
an
ause a problem be
ause as per our
urrent
denition of header les, whatever is in d.h will get in
luded, and hen
e dened twi
e.
De
laring the same name again is of
ourse an error.
So what we need is a way to say, \in
lude what follows only if it has not been in
luded
earlier".
This is what header guards provide. Indeed, it is more
ustomary to write the header
le g
dl
m.h in the following form.
#ifndef GCDLCM_H
#define GCDLCM_H
int g
d(int,int);
int l
m(int,int);
#endif
The rst line
he
ks if a so
alled prepro
essor (Appendix G) variable GCDLCM H has already
been dened. Only if it is not dened, then the rest le, till the line #endif is pro
essed.
Noti
e that the se
ond line, should it be pro
essed, will dene GCDLCM H. This will ensure
that subsequent in
lusions of the le g
dl
m.h will have no ee
t.
Note that prepro
essor variables, unlike C++ variables,
an be dened without being
assigned a value, as in the
ode above. Also note that the name of the variable
an be
anything of your
hoosing; using the
apitalized name of the le with a sux H is just a
onvention.
11.2.6 Pa
kaging software
The above dis
ussion shows how you
ould develop fun
tions and supply them to others.
You
reate a F.
pp le and the F.h le
ontaining de
larations of the fun
tions dened in
F.
pp. Next you
ompile the F.
pp le giving the -
option. Then you supply the resulting
F.o le and the F.h le to whoever wants to use your fun
tions. They must pla
e the le
191
in the dire
tory
ontaining their sour
e les (i.e. les
ontaining their C++ programs),
and pla
e the line #in
lude "F.h" in the les whi
h need the fun
tions de
lared in F.h.
Next, they must mention the le F.o while
ompiling, giving the pathname in
ase it is not
in the
urrent dire
tory. Note that other programmers do not need to see your sour
e le
F.
pp if you dont wish to show it to them.
F.h
11.3 Namespa es
We next
onsider an important question whi
h typi
ally arises when a program makes use of
fun
tions developed by dierent people, developed possibly at dierent times. The question
is: what happens if you borrow
ode from two programmers, both of whom have dened a
fun
tion with the same name and the same signature? Note that you may not get the sour
e
ode for the fun
tions, and hen
e there may be no way of
hanging the name of any of the
fun
tions.
Su
h
on
i
ts are typi
ally resolved using the notion of a namespa
e. A namespa
e is
like a
atalog. When you pla
e a name name in a namespa
e
atalog, the a
tual name
you dene is not name, but
atalog::name. Even after a name is pla
ed in a namespa
e,
it is possible to use just the short version name rather than always needing to use the full
version
atalog::name, C++ does provides you me
hanisms for that. However, the full
name
atalog::name is always available to use if the need arises.
192
It is expe
ted that if you
reate fun
tions for publi
use, you will pla
e them in a suitably
named namespa
e. Thus, if you borrow
ode from two programmers, then quite likely they
will use either dierent fun
tion names or dierent namespa
es. Thus, even if the fun
tion
names happen to be the same, by using the full name you
an unambiguously refer to ea
h
fun
tion.
Next we see how to
reate and use namespa
es. We
onsider only the main ideas and
omit many details.
11.3.1 Denition
You
an dene a namespa
e named name-of-namespa
e and the names inside it by writing:
namespa
e name-of-namespa
e{
de
larations of definitions of names
}
Inside the blo
k following the name name-of-namespa
e you
an de
lare or dene as many
names as you like. For example, you might write:
namespa
e mySpa
e{
int g
d(int,int);
int l
m(int m,int n){return m*n/g
d(m,n);}
}
After you write this, the namespa
e myspa
e will
ontain the names g
d and l
m. It is
a
eptable to give just a de
laration (as we have done for g
d) or the
omplete denition (as
we have done for l
m). Inside the namespa
e blo
k, you
an refer to the names in it dire
tly.
Thus, the denition of l
m refers to g
d dire
tly. However, outside the namespa
e blo
k, by
default you must use the full name. For example, after the denition of mySpa
e above, you
may dene g
d by writing the following.
int mySpa
e::g
d(int m, int n){
if(n == 0) return m;
else return g
d(n, m % n);
}
You will note that the last line of the above denition uses the name g
d without prexing
it with the name of the namespa
e. This is ne; the denition of a fun
tion belonging to a
namespa
e is
onsidered to be an extension of that namespa
e, as a result other fun
tions
from that namespa
e
an be referred to dire
tly by their short names.
Finally, we
an use the fun
tions in the namespa
e as follows.
int main(){
out << mySpa
e::l
m(36,24) << endl;
}
This must follow the namespa
e denition and the denition of mySpa
e::g
d, and of
ourse
in main the full name mySpa
e::l
m must be used.
193
Here ns is the name of a namespa
e, and n a name dened inside it. In the
ode following
the using de
laration, all referen
es to the name n would be
onsidered to be referring to
ns::n. Thus we might write
using mySpa
e::l
m;
If we put this line before the main program, then in the main program we
an use l
m rather
than having to write mySpa
e::l
m.
Another variant is to merely state
using namespa
e ns;
This is
alled a namespa
e dire
tive. With this, all names in the namespa
e ns
an be used
using the short form in the
ode that follows.
11.3.3 The global namespa
e
When you dene fun
tions without putting them in a namespa
e, they impli
itly enter
an omnipresent, nameless global namespa
e. When you use a name without a namespa
e
qualier, it is expe
ted to be present in either the global namespa
e or in a namespa
e for
whi
h an appropriate using de
laration or dire
tive has been given.
Suppose we have the namespa
e mySpa
e as dened above, and further we have put a
using namespa
e mySpa
e; dire
tive in our main program.
using namespa
e mySpa
e;
int l
m(int m,int n){return m*n;}
int main(){
out << l
m(36,24) << endl;
}
In this
ase, the
ompiler will
ag an error, saying that the referen
e l
m in the main program
is ambiguous, it
ould refer to the l
m fun
tion in the global namespa
e, or the l
m fun
tion
in mySpa
e whi
h has been made available through the using dire
tive. In su
h a
ase, you
must give the full name of the fun
tion and in doing so pi
k one.
To spe
i
ally refer to a fun
tion l
m in the global namespa
e, you
an write ::l
m.
Thus in the main program above, you must
hange l
m to either ::l
m or mySpa
e::l
m.
Note that if the two l
m denitions had a dierent signature, then this problem would not
have arisen.
194
while
ompiling.
Sometimes you might intend the fun
tion f to be used only within le F1.
pp and not
be exposed outside. For this, you
an dene it inside an unnamed namespa
e by writing
namespa
e{
de
laration of f
}
With this, you
an use f inside the le in whi
h the de
laration appears, dire
lty by its name,
but not outside of the le. You
an think of an expli
itly
reated nameless namespa
e as a
global namespa
e available only to fun
tions in the le in whi
h the de
laration appears.
11.3.5 Namespa
es and header les
Generally, namespa
e denitions are made inside header les, and in su
h denitions only
de
larations are put. The fun
tion denitions are put in implementation les, in whi
h the
header le
ontaining the namespa
e denition must be in
luded.
The les that use the fun
tions in the namespa
e must also in
lude the le
ontaining
the namespa
e denition, and may use the fun
tions in the namespa
e either by giving the
full name or by giving a using dire
tive and the short name.
Figure 11.3.5 shows an example.
So far in this book we have had variables dened inside fun
tions. However, it is possible,
though not re
ommended, to dene a variable outside all fun
tions. In su
h
ases, the
variable be
omes a global variable, i.e. it
an be a
essed by any fun
tion. Note that the
ompiler will typi
ally have a separate region of memory where global variables will be
allo
ated; global variables are not allo
ated in the a
tivation frame of any fun
tion.
Here is an example.
int i=5;
void f(){ i = i * i; }
int main(){
out << i << endl;
f();
namespa
e mySpa
e{
int g
d(int,int);
int l
m(int,int);
}
(
) The le main.
pp
Figure 11.2: Program in multiple les using a namespa
es
195
196
If you exe
ute this
ode, the rst statement will print 5, the
urrent value of the global
variable i. Then the
all f() inside the main program with
hange i to 25, whi
h will be
printed by the third statement.
Use of global variables is not en
ouraged, be
ause global variables make
ode hard to
understand. Potentially, any fun
tion
all
ould modify the variable, and hen
e it is di
ult
to make
laims about the value taken by the variable at any point of the exe
ution. However,
global variables are used in several (espe
ially older) programs, and hen
e this dis
ussion.
We note that a global variable dened in one le
an be used in another le as well.
However, it must be de
lared as extern in that le. Thus, to use the global variable i
dened above in another le, that le would need to have the de
laration
extern int i;
Note that this does not dene spa
e for i, it merely de
lares i to be an int whi
h will be
dened in some other le.
You may also put global variables in namespa
es. The simplest way is to pla
e the
de
laration (whi
h is merely the denition prexed by extern) inside the namespa
e blo
k,
whi
h
an be in a header le. Then one of the implementation les should
ontain the
denition of the variable.
We nally note that individual fun
tions may
ontain denitions of a lo
al variable having
the same name as a global variable. In su
h
ases, the lo
al variable will shadow the global
variable (Se
tion 3.6.3).
The most important namespa
e that C++ programmers need to know about is the namespa
e std.
The namespa
e std
ontains many names that you might have so far been
onsidering
to be reserved words that are a part of the language. Indeed, the words
in,
out, endl,
as well as the words string and others that you will see soon are in this namespa
e. But if
in is in std, you may wonder why you have not been for
ed to write std::
in instead of
just
in so far. The answer, as you might guess, is that the le simple
pp that you in
lude
when you write
#in
lude <simple
pp>
197
We now
onsider the question of how C++ programs
an be written and
ompiled without
using simple
pp. We show an example le in Figure 11.3.
This le, sidelength.
pp
an be
ompiled using any C++
ompiler, say the GCC
ompiler. Most
ommonly the GCC
ompiler for C++ is invoked by the
ommand g++ as
follows.
g++ sidelength.
pp
Everything in the le should look familiar ex
ept for the rst two lines. The le iostream
is a header le that
ontains de
larations of fun
tions needed for performing input output.
The le
math is a header le that
ontains de
larations of mathemati
al fun
tions su
h as
sqrt and also other fun
tions in
luding trigonometri
fun
tions. You may wonder why did
we not need to in
lude these les so far { and the answer of
ourse is that we in
luded the
le simple
pp whi
h was in turn in
luding these two les.
If you program does not
ontain graphi
s, then you
an dispense with simple
pp if you
wish, as seen above for the le sidelength.
pp. All you need to do is that instead of
in
luding simple
pp, you in
lude the les iostream and
math, and also put in the using
dire
tive. Also you also
annot use the
ommand repeat; but we have already suggested
that you start using the other looping
ommands (Chapter 7) instead. Finally, you should
not dene the main program as main program but denes it as a fun
tion named main.
There are a few other minor features in simple
pp that you
annot use { and we will dis
uss
these as we en
ounter them.
In Se
tion 8.3 we dis
ussed the bise
tion method for nding the roots of a mathemati
al
fun
tion f (x). Ideally, we should have written the bise
tion method itself as a C++ fun
tion,
to whi
h you pass the mathemati
al fun
tion f (x) as an argument. This was not how we
wrote the method then. Instead, we presented
ode for the method in whi
h the
ode for
evaluating f (x) (not a
all to it) was inserted as needed. But we
an do better now.
198
First, there is the question of how to represent a mathemati
al fun
tion f (x). The
simplest way is as a C++ fun
tion, say f whi
h takes a single double argument at returns a
double! But then, we need a way of passing a C++ fun
tion (f) as an argument to another
C++ fun
tion, whi
h we might
all, say bise
tion.
Figure 11.4 shows how this
an be done. This
ode
ontains C++ equivalents of two
mathemati
al fun
tions, f (x) = x 2, and g(x) = sin(x) 0:3. The single fun
tion
bise
tion is used to nd the roots of both these fun
tions. We will explain how bise
tion
works shortly, but rst
onsider the main program. As you
an see, main
alls bise
tion for
nding ea
h root. Consider the rst
all. The rst two arguments to the
all are the left and
right endpoints of the interval in whi
h we know the fun
tion
hanges sign. We have used
the same endpoint values as xL,xR of Se
tion 8.3, viz. 0,2. The next argument is epsilon,
whi
h gives the a
eptable error in the fun
tion value. For this we have
hosen the value
0.0001, instead of reading it from the keyboard. The last argument somehow supplies the
fun
tion f whose root is to be
omputed. Exa
tly why we need to write &f will be
ome
lear
shortly. The se
ond
all is similar. We are asking to nd a root in the interval 0 to =2,
where we know that g(0) < 0 while g(1) > 0. Hen
e the bise
tion method
an be applied. So
we
all that, spe
ifying epsilon as 0.0001. The last two lines merely print out the answers
and
he
k if the square of the rst answer is
lose to 2, and the sine of the se
ond answer is
lose to 0.3
First, the key idea. As you know from Se
tion 2.8,
ode and data are both pla
ed
in memory. Just as we
an identify a variable by its starting address, we
an identify a
fun
tion also by the address from where its
ode starts. Thus C++ has the notion of a
fun
tion pointer, whi
h you
ould think of as the starting address of the
ode
orresponding
to the fun
tion. On
e we have a pointer to a fun
tion, we simply dereferen
e it and use
it! Thus, the expressions &f and &g are merely pointers to our fun
tions f,g. So all that
remains to explain is how the fun
tion bise
tion will dereferen
e and use them.
The name of the last parameter of bise
tion is pf. We explain its
ompli
ated looking
de
laration soon. In the body, this parameter pf indeed appears dereferen
ed. In the rst line
it appears as (*pf)(xL), where the parentheses are ne
essary be
ause just writing *pf(xL)
will be interpreted as *(pf(xL)) whi
h is not what we want. Noting that dereferen
ing
works exa
tly as we expe
t, the expression (*pf)(xL) will indeed evaluate to f(xL) when
pf has value &f. Similarly the expression (*pf)(xM) will evaluat f(xM). Thus this
ode will
work exa
tly like the
ode in Se
tion 8.3 for the rst
all.
So the only thing that remains to be explained now is the
rypti
de
laration of the
last parameter of bise
tion. Basi
ally we need a way to say that something is a fun
tion
pointer. Note that bise
tion
an only be used to nd roots of fun
tions of one real variable,
i.e. it does not make sense to pass a fun
tion su
h as g
d (whi
h takes 2 int arguments and
returns an int). Pointers to only
ertain types of fun
tions are a
eptable as arguments to
bise
tion: spe
i
ally pointers to fun
tions that take a single double argument and return
a double as result.
First, let us
onsider how to de
lare a fun
tion that takes a double as argument and
returns a double result. If the name of the fun
tion is pf, then we know from Se
tion 11.2.1
that it
an be de
lared as:
2
double pf(double);
199
int main(){
double y = bise
tion(1,2,0.0001,&f);
out << "Sqrt(2): " << y << "
he
k square: " << y*y << endl;
200
But now we simply note the general strategy for de
laring pointers: if a de
laration de
lares
name v to be of type T, then repla
e v by *v and the new de
laration will de
lare v to be
pointer to T . Doing this we get what we wanted.
double (*pf)(double); // *pf is fun
tion taking double and returning double
// pf is pointer to fun
tion taking double and returning double
where the parentheses have been put to avoid asso
iating * with double.
De
laring pointers is somewhat tri
ky and
rypti
. It takes a bit of pra
ti
e to write su
h
de
larations and even read them. To take another example, this is how you would de
lare
ph to be a pointer to a fun
tion whi
h takes a double and int as argument and returns a
double.
double (*ph)(double,int);
Perhaps the best way to read it is the reverse of what we did above. Repla
e *ph by h
and observe that h must be a fun
tion taking double,int arguments and returning double.
Hen
e *ph must be a pointer to su
h a fun
tion.
11.7.1 Some simpli
ations
The C++ standard allows you to drop the operator & while passing the fun
tion, and also
the dereferen
ing operator * while
alling the fun
tion. Unfortunately, this does not help in
the tri
kiest part, the de
laration of a fun
tion pointer parameter.
This
hapter dwelled on many te
hni
al aspe
ts of using fun
tions. However, there is an
important philosophi
al aspe
t whi
h we
onsider below.
We also dis
ussed the notion of fun
tion pointers. However, in C++ some additional
me
hanisms have been introdu
ed for similar fun
tionality. These will be dis
ussed in Se
tion 16.4 and Appendix H.
11.8.1 Fun
tion size and readability
We began this
hapter by proposing fun
tions as a me
hanism for avoiding
ode dupli
ation.
This is indeed a very important use of fun
tions. However, fun
tions
an also be used to
make your
ode easier to understand to other programmers. Ease of understanding is very
important espe
ially when programmers work in teams.
One way to improve understandability of anything is to present it in small
hunks. When
you write a book it is useful to break it up into
hapters. A
hapter forms an organizational
unit of a book. In a similar manner, a fun
tion forms an organizational unit of a program.
There are a few thumb rules for breaking long text into
hapters or se
tions. An example of
a thumb rule: every idea that is important should have its own
hapter, or its own se
tion.
There are similar thumbrules for splitting large programs into fun
tions.
When it
omes to programming, it is often believed that no fun
tion, in
luding main
should be longer than one s
reenful. Even with large displays, this gives us a limit of perhaps
201
40-50 lines on the length of a fun
tion. Basi
ally, you should be able to see the entire logi
of the fun
tion at a glan
e: that way it is easier to understand what depends upon what, or
spot mistakes. How do we break a program into smaller pie
es? So far you have not had the
o
asion to write programs longer than 40 lines, so this dis
ussion is perhaps a bit di
ult
to appre
iate. You will see later, however, that most programs
an be thought of as working
in phases. Then you should
onsider writing ea
h phase as a separate fun
tion, and give
it a name that des
ribes what it does. These fun
tions
ould be pla
ed in the same le as
the main program, but you will nd that this will make the program easier to understand.
Another idea is to make a fun
tion out any modestly
ompli
ated operation you may need
to perform. As an example of this,
onsider the apparently simple a
tion of reading in a
value from the keyboard. As noted in Se
tion 7.3, a user might type in an invalid value,
or the value may not stand for itself but in fa
t might indi
ate that the stream of values
has nished. One way is to pla
e the logi
for dealing with all this in a fun
tion that is
alled by the main program. This idea is partly explored in the read marks into fun
tion
of Se
tion 9.7.
1. Suppose the LCM
omputation program of Figure 11.1 has been written using a single
le, and it is noti
ed that only the fun
tion l
m has been de
lared and also dened,
all other fun
tions are dened but not de
lared. Show how the program
ould appear
in the le.
2. The fun
tion passed to the bise
tion fun
tion took a float and returned a float.
However, we might well need to nd the root of a fun
tion whi
h takes a double and
returns a double. Also, it would be ni
e if the types of the other arguments were
likewise made double. Turn bise
tion into a template fun
tion so that it works for
both double and float types. You
an of
ourse also do this by overloading the name
bise
tion.
Chapter 12
Pra
ti
e of programming: some tips
and tools
In theory, theory and pra
ti
e are the same. In pra
ti
e, they are not.
Albert Einstein
If you have been reading this book without solving any of the exer
ises, you may perhaps
ome to believe that the pro
ess of developing a program is neat and tidy, and generally
smooth sailing. If on the other hand, you have also been solving the exer
ises, your experien
e
might be dierent. After you write a program, you
ompile and run the program and you
test it by providing dierent inputs. You possibly dis
over that for some input, let us
all it
x, the program does not produ
e the
orre
t output. After some amount of dete
tive work
you gure out why the program is not produ
ing the
orre
t answer for x; so you modify
your program (often
alled \debugging") and rerun. You then dis
over that now the program
works
orre
tly for x, but it does not run
orre
tly for an input y, for whi
h the old program
was
orre
t! So you have to do more dete
tive work. And this
y
le
an go on for some time.
If you are writing large programs, this
y
le
an be extremely frustrating.
In this
hapter, we will dis
uss the entire programming pro
ess and oer suggestions with
a view to (a) in
rease the
onden
e that the program is doing what it is supposed to do,
and (b) make the pro
ess less tiresome.
The program development pro
ess starts with
learly understanding what is to be done,
i.e. the spe
i
ation for the program. Along with understanding the spe
i
ation abstra
tly,
it is useful to
onstru
t examples of inputs and required outputs. These help in ensuring
that the spe
i
ation is
orre
t, and
an also be used to test the program when it is written.
After dis
ussing spe
i
ation we will
omment brie
y on program design. Finally we will
also make some remarks on the debugging pro
ess. Along the way, we will also remark
on some general fa
ilities like input-output redire
tion and assertions that are available to
simplify the above steps. In some ways, this
hapter is an extended version of Chapter 4.
Some of the suggestions made in this
hapter may strike you as being too
autious, you
may think, \I
an go mu
h faster than this". In
ase you are one of the lu
ky few who
an
write large programs
orre
tly, in an intuitive manner, without apparent
areful planning,
more power to you! But if you nd you are spending more time debugging the program than
designing it in the rst pla
e, you are en
ouraged to try out the suggestions given in this
hapter.
202
203
The rst step in writing a
orre
t program is to
learly know what you want the program to
do. This might sound obvious, but often, programs dont work be
ause the programmer did
not
learly
onsider what needs to happen for some tri
ky input instan
e. How
an you be
sure that you
ompletely know what the program is expe
ted to do, that you have
onsidered
all the possibilities? The best way for doing this is to write down the spe
i
ations very
learly, in pre
ise mathemati
al terms if possible.
Typi
ally, in a spe
i
ation you will state that the inputs
onsist of numbers x; y; z; : : :,
and the output
onsist of the numbers p; q; r; : : :. Then you will give
onditions that these
numbers must satisfy. The spe
i
ation is not expe
ted to indi
ate how the output is to
be a
tually generated, that is to be de
ided by your program, sometimes referred to as
the implementation. If the output produ
ed by your implementation happens to satisfy
the
onditions des
ribed in the spe
i
ation for every input, then and only then
an your
implementation be
ertied as
orre
t. It is a good idea to write the spe
i
ation as a
omment in your program, and also to use the same variable names in the spe
i
ation as
in your program.
Let us take an example. What are the spe
i
ations for the program to
ount digits of
a number? We think that we understand de
imal numbers, whi
h we indeed do. But su
h
intuitive understanding does not
onstitute a spe
i
ation. The intuitive understanding
must be explained in more pre
ise terms. Here is the spe
i
ation we used earlier.
Input: A non negative integer n.
Output: Smallest positive integer d su
h that 10d > n.
This is a good spe
i
ation be
ause it gives the pre
ise
onditions that we want the
output to satisfy, nothing more, nothing less.
It is
ustomary in writing spe
i
ations to state
onditions in the form \smallest/largest...
satisfying... ". Formulating spe
i
ations in this manner requires some pra
ti
e. Also a
lot of
are is needed. Should the
ondition be 10d > n or 10d n? You may
onsider
these questions to be tedious, but if you
annot answer them
orre
tly while writing the
spe
i
ation, you are unlikely to write the program
orre
tly. You may be making the same
mistake in your program!
Let us
onsider another problem. Suppose you are given n points in the plane, p ; : : : ; pn.
Find the smallest
ir
le that
ontains all the points. It might be tempting to rewrite just
what is stated in the problem statement:
Input: n points p ; : : : ; pn in the plane.
Output: Smallest
ir
le that
ontains all points.
In this we have not spe
ied how a
ir
le is to be represented using numbers. That is
a
eptable, if our audien
e knows how to represent
ir
les using numbers and translate the
phrases su
h as \smallest
ir
le", and \
ontains all points", into
onditions on numbers. For
the present, however, we will prefer the following des
ription of the output.
1
204
Real numbers x; y; R su
h that the distan
e between ea
h point pi and the point
(x; y) is at most R, and R is smallest possible.
In this, we have not dened what \distan
e" means. If that is not expe
ted to be
ommonly
understood, then we should spell it out too.
Consider another example.
Input: n points p ; : : : ; pn in the plane spe
ifying the verti
es of a polygon in
lo
kwise or
ounter
lo
kwise order.
Output: Area of the polygon.
This seems like a good spe
i
ation. Although we have not given a formula to
ompute
the area, the notion of area is
ommon knowledge. Or is it? As you think more about
the problem, you will realize that there is no standard notion of area, if the line segments
interse
t, i.e. if the polygon is not simple. If we allow non-simple polygons as input, the
problem statement needs to dene what area means for non-simple polygons. Suppose the
user expe
ts to supply only simple polygons as input. In that
ase, the input must be
des
ribed as su
h.
Input: n points p ; : : : ; pn in the plane spe
ifying the verti
es of a simple polygon in
lo
kwise or
ounter
lo
kwise order.
This spe
i
ation doesnt state what is expe
ted to happen if the input spe
ied during
exe
ution is invalid, say the polygon a
tually given as input is not simple. Indeed, that is
not a part of the
ontra
t between the implementer and the user of the program. If the
input polygon is non-simple, the program may give junk values, or may not even terminate.
If any other behaviour is expe
ted, then it should be made a part of the spe
i
ation.
The points to note are as follows. First, write down spe
i
ations as pre
isely as possible,
using mathemati
al notation if it is reasonably obvious. Se
ond, you may re
eive a spe
i
ation that looks ne, but really is not properly dened. In this
ase, you should modify it.
Finally, it might be possible to supply input whi
h does not adhere to the spe
i
ation, e.g.
the user
an supply a non-simple program as input. If this happens, the implementer need
not guarantee any spe
i
output.
Output:
Along with writing the spe
i
ations, you should
onstru
t sample input instan
es, and
work out what output you want for those. As dis
ussed in Se
tion 4.1.1, it is good to have
examples in your mind for any abstra
t statements you make. Another reason is that the
input-output examples you work out will serve later as test
ases for your program.
For the digit
ounting program, it is easy to work out examples. For example you might
de
ide to have your rst input instan
e be the number 34, for whi
h the output must be 2
sin
e that is the number of digits in 34. This might appear too easy, but even so it should be
Note that a more
omplex spe
i
ation will typi
ally lead to a slower program. Hen
e in this
ase it
might be better to have two programs: one for simple polygons and another for possibly non-simple polygons.
1
205
written down. You should also
he
k whether the input (34) and the output (2) agree with
the what you have written down in the spe
i
ation: Is 2 indeed the smallest number su
h
that 10 > 34? These may sound like trivial
he
ks, but your program
an go wrong be
ause
of trivial mistakes, and so su
h
he
ks are useful. For the
ir
le
overing problem, working
out examples is harder, sin
e it might take
onsiderable
al
ulation to nd the smallest
overing
ir
le by hand. In su
h
ases, the least you
an do is to
onstru
t a few simple
ases, e.g. just two points, say (0,0) and (1,0), for whi
h the best
overing
ir
le must have
radius 0.5 and must be
entered at (0.5,1).
If you want to design examples that will serve as test
ases later, you should think about
what examples are \good". For this there are a few strategies. One idea is to generate
what you think might be \usual" instan
es whi
h somehow you think might be \
ommon
in pra
ti
e". For example, for the
overing
ir
le problem, the instan
e in whi
h all points
are randomly pla
ed in the plane is perhaps more
ommon than the instan
e in whi
h they
are all
ollinear. It is possible that for some other problem (say
ounting digits) there is
no notion of \
ommon in pra
ti
e". Even in this
ase you
an think of using random input
values. You may wonder how you
an feed random numbers to a
omputer. We will dis
uss
this in Se
tion 12.7.
Another possiblity is to
onsider if some input instan
es are \harder" than others, and
hen
e might test the program better? The notion of hard is of
ourse informal. But here is
how you might
onsider
ertain inputs more interesting, say for the digit
ounting problem.
If you look at the number of digits d as a fun
tion of the input n, you will see that d
hanges at powers of 10. At 9 the output value is 1, but it goes up to 2 at 10. The value
is 3 at 999 but goes up to 4 at 1000. So you might want to pay more attention to these
input values: perhaps the program has to be \keenly attentive" and distinguish between 999
and 1000 (even though they are
onse
utive), but not between 578 and 579 (whi
h are also
onse
utive). So
he
king the inputs 999, 1000 and so on might be more likely to show up
the errors, than say
he
king 578 or 579. Another
ase of
ourse is to
he
k for the smallest
and largest input values allowed. In
ase of digit
ounting 0 is the smallest value allowed,
and whatever the largest value allowed is for representing n on your
omputer. The smallest,
largest, and the values at whi
h the output
hanges are informally
alled \
orner
ases", and
you should
ertainly test around these values.
For the polygon area problem, the simplest input instan
es
ould be re
tangles, for whi
h
it should be easy to
al
ulate the area by hand. You
ould again ask, what input instan
es
are easy and whi
h are hard? The polygons need to be simple, but of
ourse they need
not be
onvex. So if you plan to allow non-
onvex polygons as input, then
ertainly they
should be a part of your test instan
es. If you de
ide that you dont want to allow non-
onvex
instan
es, then you should amend the spe
i
ation to de
lare this. Note that it is better
that your program
orre
tly implements a weaker spe
i
ation than wrongly implementing
a stronger one.
The length of the input is not xed for the polygon area problem. Very likely the
program will rst read in n the number of verti
es, and then the
oordinates of the points.
An important question to answer is how your program will handle
orner
ases, e.g. n = 2
or n = 1 or even n = 0. Either you should return 0, or you state
learly in the spe
i
ation
that these
ases will not be handled by your program.
2
206
When you write programs professionally, you are required to keep a re
ord of the testing
you have done. This
an be done ni
ely by using a feature
alled input redire
tion. Most
operating systems support input redire
tion.
In Chapter 1 we told you that
in represents the keyboard and whenever your program
exe
utes a statement of the form
in >> ... you are expe
ted to type in an appropriate
value. This is an oversimpli
ation. If fa
t,
in represents an abstra
t, standard input
devi
e, whi
h is the keyboard by default, but this default
an be
hanged. If you wish, you
an make the standard input devi
e be a le, say named file1. Thus, instead of waiting for
you to type in input, the program would take input from le file1 whenever it exe
utes a
in >> ... statement. This is
alled input redire
tion. To redire
t input, you spe
ify the
name of the le on the
ommand line, pre
eded by the
hara
ter <, after the name of the
exe
utable. Thus to redire
t input to
ome from file1, you will type the following on the
ommand line.
% a.out <file1
As you
an see, input redire
tion is very
onvenient. Even before you write the program,
ea
h test instan
es you
reate as dis
ussed above
an be pla
ed in a le. When the program
is ready, you run it, merely redire
ting input so that the data
omes from the le of your
hoosing.
Thus we
an suggest the following pro
ess for
reating and using test
ases. Even before
you write the program
reate test
ases, pla
ing the input in les, one le per instan
e.
Thus you will
reate several les, say input1.txt, input2.txt, ... . Also
reate les
output1.txt, output2.txt, ... whi
h
ontain the outputs as you expe
t for the
orresponding input instan
es. After the program is ready, simply redire
t input, say from
input3.txt in order to test it on the third instan
e you
reated. Che
k that the output you
get is indeed what you had written down in output3.txt.
Note by the way that input redire
tion is also useful if your program does not run
orre
tly
on the very rst run. If you have pla
ed the data in a le then you
an redire
t input from
it, and thus do not have to type the data again and again. This saves typing eort, and is
espe
ially useful for programs for whi
h the input is large.
Note nally that the standard output stream,
out
an also be redire
ted. For this you
an exe
ute your program by typing
% a.out >file2
If you do this, whatever you print by exe
uting
out << ... in your program will be pla
ed
in the le file2, rather than being shown on the s
reen.
This is useful for programs whi
h the output is large. If the output is put into a le, you
an examine it at leisure. Also, the le thus
reated
an serve as a re
ord of your testing
a
tivities.
The next step after the development of the spe
i
ations and test
ases is algorithm design.
By algorithm we mean the abstra
t ideas we need to solve a problem. For example, how do
207
we nd the smallest
ir
le
overing a set of points? This problem has a puzzle like
avour,
and seems to require some
reative thinking. You might even wonder whether
reativity
an be taught. But there do exist strategies for designing algorithms. As we have been
mentioning frequently, re
ursion is one strategy. However, algorithm design is really outside
the s
ope of this book.
For the most part, whatever algorithms you need to know in order to write programs
des
ribed in the text and the exer
ises, we will either tell you dire
tly, or they will be minor
modi
ations of algorithms you somehow know already. And do realize that you know a lot
of algorithms already. Indeed from
hildhood you have been learning a lot of algorithms,
how to multiply two numbers, how to
ook, how to ride a bi
y
le, and so on. You will need
to express some of these algorithms using C++. This will not ne
essarily easy, you may be
exe
uting the algorithms sub
ons
iously, out of habit, but you will have to introspe
t on your
a
tions and identify the patterns in them and express them in C++. This may be possible
for some problems, e.g. multiplying one number with many digits by another number, but
very di
ult for others, e.g. riding a bi
y
le. In any
ase, for the most part you should not
need serious algorithm design, but you should
ertainly be able to introspe
t over skills you
have learnt sin
e
hildhood, verbalize them and express them in C++. In addition to that,
some amount
How to generate su
h ideas is dis
ussed elsewhere in the book. Our
on
ern for now is
how to organize your program assuming you understand the spe
i
ations, have
reated the
test
ases, and know all the relevant mathemati
al/algorithmi
ideas. That is what we mean
by program design.
We remarked in Se
tion 11.8.1 that any large program is best written by dividing it into
small fun
tions. Later we will see other ways of dividing programs into pie
es, but the key
point is that some su
h division will need to be there. On
e you de
ide to divide a large
program into pie
es, we really need to apply the program design pro
ess (re
ursively!!) to
ea
h pie
e. We must write out the spe
i
ation for ea
h pie
e (fun
tion), and design test
ases for ea
h fun
tion too. It is tempting to not test the individual fun
tions, but to put
together the entire program, and see if the whole thing behaves
orre
tly. But if the whole
thing does not behave
orre
tly, it is tri
ky to gure out whi
h fun
tion is not working right.
So it is useful to rst test ea
h fun
tion separately. This means simply that if your program
needs to
al
ulate GCD very often, do write a GCD fun
tion, and then test it before you put
it together with the rest of the program. Quite likely this will save you time in debugging
later.
The idea of writing spe
i
ations applies even in implementing a fun
tion itself. Say
when you design a loop, you should be
lear in your mind as to what the loop is intended
to a
omplish (spe
i
ation), and be able to reason about it by writing invariants and a
potential fun
tion. It is also a good idea to put these down as
omments. Basi
ally these are
all \defensive programming strategies" intended to minimize the
han
e of making mistakes.
Another important program strategy is as follows. Suppose you are writing a program
whi
h solves a somewhat
omplex problem. The natural plan might be to write a program
to solve the entire problem in the very rst attempt. Another idea is:
onsider a weaker
spe
i
ation rst. Write the program to solve the weaker spe
i
ation, testing it
ompletely.
Then try your hand at the original spe
i
ation. The idea behind this weaker spe
i
ation
2
The exer ises give you some hints about the overing ir le problem.
208
rst strategy is as follows. You may think that you understand the grand spe
i
ation.
But often you may not, espe
ially if you are inexperien
ed. As you try to implement the
simpler spe
i
ation you may realize the problem needs more thought. Thus it is best to
get on with writing
ode reasonably qui
kly { that experien
e will help you understand the
di
ulties. The other alternative is to develop the full spe
i
ation rst and only then start
writing the program, and start testing only after the entire program is ready. In this you
deny yourself the feedba
k (not to mention the satisfa
tion!) that you get from doing some
testing. Be
ause of the early feedba
k, the weak spe
i
ation rst approa
h might end up
saving time and eort.
As an example
onsider the polygon area problem. You may know Heron's formula for
the area of a triangle given the lengths of the sides:
p
Area = s(s a)(s b)(s
)
where a; b;
are the lengths of the sides of the triangle, and s = a b
. Using this you may
onsider it easier to
al
ulate the area of a
onvex polygon, than that of non-
onvex one.
So in this
ase, the weak spe
i
ation rst strategy would en
ourage you to rst write the
program for the
onvex
ase. However the key point is that whatever you do, you should
work to a spe
i
ation, weak or strong. In other words, there must be truth in advertising!
+ +
2
12.5 Assertions
The import of the previous dis ussion is: you should know your program well.
209
Your knowledge of the program is often of the form: \at this point in the program, I expe
t
the value of this variable to be at least 0". Why not a
tually
he
k su
h expe
tations during
exe
ution? If your program is not running
orre
tly, it might well be be
ause something that
you
ondently expe
t is not a
tually happening.
C++
ontains a fa
ility whi
h makes it easy to
he
k your expe
tations and produ
e
error messages if the expe
tations are in
orre
t. Suppose you express a
ertain
ondition
ondition to hold at a
ertain point in your program, you simply pla
e the statement
assert(
ondition);
Here
ondition must be a boolean expression. When
ontrol rea
hes this statement,
ondition is evaluated, and if it is false, the program halts with a message, typi
ally stating
\Assertion failed", and the line number and the name of the program le
ontaining the
assertion is also given. If the expression
ondition is true (as you expe
ted it to), then
nothing happens and the
ontrol just moves to the next statement.
To use assert you must in
lude the line
#in
lude <
assert>
as the rst line of the fun
tion body. If during exe
ution g
d is
alled with the se
ond
argument 0, you would get an error message saying that this assertion failed.
Here is another example. Suppose you know that a
ertain variable v will only take values
1 or 2. The you might originally have written:
...
if(v == 1) ...
else if(v == 2) ...
...
You might not write the else statement following the if else thinking that it is not ne
essary. But \the statement is not ne
essary" is only your expe
tation. If the value of the
variable v has arisen after a
ompli
ated
al
ulation, it is
on
eivable that something might
have gone wrong in your dedu
tion. If you are debugging your program, then you want to
be sure that your \
ondent dedu
tion" is a
tually
orre
t, before you start suspe
ting the
rest of the program. So you
ould a
tually make a
he
k by writing an assertion.
...
if(v == 1) ...
else if(v == 2) ...
else assert(false);
...
If this assertion did not fail during exe
ution, you know that the problem must be elsewhere.
We will see more examples of assertions later, e.g. for array bounds
he
king (Se
tion 13.8).
210
at the top of ea
h le
ontaining assertions you want to disable. The above line is to be pla
ed
before the in
lusion of <
assert>. The above line will dene the prepro
essor (Appendix G)
variable NDEBUG. This will have the ee
t of turning the assert statement into a
omment.
12.6 Debugging
Suppose you follow the above dire
tions and are generally very
areful, and yet things go
wrong: your program produ
es an answer dierent from what you expe
t. What do you do?
The most natural response is to try and nd out when the program starts behaving
dierently from what you expe
t. For this, you
an print out the values of the important
variables at some
onvenient halfway point, and
he
k if the values are as you might expe
t.
If the values printed by these statements are as you expe
t (or not), then the error must
be happening later (earlier), so you put print statements at later (earlier) points in your
program. By examining the values in this manner, you try to get to a single statement until
whi
h the values are as you expe
t, but after whi
h the values are dierent. At this point,
you are usually in a position to determine what is going wrong. The pro
ess of examining
the values taken by variables during exe
ution
an be made mu
h easier if you use programs
alled debuggers or IDEs. We dis
uss them below.
We do note one important sour
e of errors: it might be the
ase that your program is
not working
orre
tly not be
ause it has a logi
al
aw, but be
ause it is not being fed the
orre
t data. This
an happen espe
ially if the input is
oming from a badly designed input
le whi
h you have redire
ted. We dis
uss how to deal with this.
12.6.1 Debuggers and IDEs
There exist spe
ialized programs,
alled debuggers or IDEs, Intera
tive Development Environments, whi
h are modern versions of debuggers, whi
h
an substantially help in the
pro
ess of debugging.
Debuggers or IDEs oer many ways of exe
uting your program. For example, you
an
ask that the program be stepped, i.e. run one statement at a time. You
an see where the
ontrol is after the exe
ution of the statement in question ends, and you
an also examine
the values of the dierent variables. You
an also ask that the program exe
ute until a
ertain statement is rea
hed, exe
uting freely till that statement. On
e that statement is
211
rea
hed, you
an again examine variable values if you wish. Essentially, this enables you to
investigate how your program exe
utes without having to put in print statements in it.
Unfortunately, most IDEs are fairly
omplex, and it is signi
ant work to just understand
how to use them. That is the reason we have not dis
ussed IDEs in this book. But if you
plan to write programs with thousands of lines of
ode, you should learn to use IDEs.
12.6.2 End of le and data input errors
C++ behaves in a somewhat unintuitive manner in data input. Suppose you exe
ute
in
>> x; where x is of type int. Suppose the value typed in response to this (or read from
the le from whi
h
in is redire
ted) happens to be the
hara
ter 'a'. If this happens you
might expe
t that the program will halt with an error message. However the program does
not halt! Instead, some junk value is supplied to you and the program
ontinues merrily.
The only indi
ation that an error has happened is that the value of
in be
omes 0, or
NULL. So ideally, after reading every value you should
he
k if
in is not NULL. For this you
an write an assertion:
assert(
in != NULL);
This is be
ause NULL or 0 also stands for the logi
al value false.
The main point to note is as follows. Suppose your program is not working
orre
tly.
It
ould be be
ause of a data input error. You may be feeding it an illegal value. This is
not likely to happen if you are a
tually typing in values from the keyboard in response to
messages from the program. However, if the program is reading data from a le (be
ause
of redire
tion or otherwise) it may well happen. So it is best to
he
k for input errors by
asserting
in.
12.6.3 Aside: Input-Output expressions
Finally, we note that in C++ the phrase
in >> value
auses a value to be read into the
variable value, and in addition itself is an expression that has a value: the value of the
expression is the value of the variable
in. This should not
ome as a surprise to you, this
is in fa
t the reason you
an write statements su
h as
in >> a >> b; whi
h you should
really read as (
in >> a) >> b; where the rst expression
auses a value to be read into
a, and then the expression evaluates to
in, from whi
h another value is read into b.
This fa
t allows us to write some rather
ompa
t loops. Suppose you want to nd the
sum of a sequen
e of numbers stored in a le. You
an do this by exe
uting the following
program with
in redire
ted from that le.
int main(){
int val, sum=0
while(
in >> val){
sum += val;
212
}
out << sum << endl;
The reading happens in the loop test, and if there is an error or end of le, the reading
expression returns false, and the loop ends. Thus the above loop will end when the le ends,
and after that the sum will be printed.
Note that you
an also use the above program to sum values that you type in from the
keyboard. Just type in the values, and follow them with a
trl-d (type 'd' while the
ontrol
key is pressed), whi
h signals an end of le.
C++ provides you with the fun
tion rand whi
h takes no arguments whi
h returns a random
number. This statement should puzzle you { a
omputer is an orderly deterministi
ma
hine,
indeed we did not say anything about randomness in our dis
ussion of
omputer hardware
(Chapter 2). How
an then a
omputer generate random numbers?
Indeed, a
omputer does not generate truly random numbers. Instead, a
omputer merely
generates su
essive numbers of a perfe
tly deterministi
omputable sequen
e whose elements seem to be resemble a sequen
e whi
h
ould have been generated randomly. Su
h
sequen
es and their elements are said to be pseudo-random. Indeed a simple example is the
so
alled linear
ongruential sequen
e, given by say, xi = a xi + b mod M , where a; b; M
are suitably
hosen integers. Say we
hoose, just for the purpose of dis
ussion, a = 37,
b = 43, M = 101. Then starting with x = 10, the next few terms are: 9, 73, 17, 66, 61, 78,
0, 43, 18, 2, 16. Perhaps you will agree informally that this sequen
e looks random, or at
least more random than the sequen
e 0, 1, 2, 3, 4 and so on. It is possible to formalize what
pseudo-random means, but that is outside the s
ope of this book. So we will just assume that
pseudo-random merely gets the best of both worlds: it is a sequen
e that
an be generated
by a
omputer, but
an be
onsidered to be random for pra
ti
al purposes.
Fun
tions su
h as rand whi
h return (pseudo) random numbers do use the general idea
des
ribed above: the next number to be returned is
omputed as a
arefully
hosen fun
tion
of the previous. So the exa
t sequen
e of numbers that we get on su
essive
alls to rand
depends upon how we started o the sequen
e, what x we
hose in the example above. This
rst number of the sequen
e is often
alled the seed. C++ allows you to set x to any value
v you wish by
alling another fun
tion srand whi
h takes a single integer argument whi
h
you must spe
ify as v. To use rand and srand, you would normally need to in
lude the line
1
213
for
ertain (randomly generated) data. Say you modify the program and you wish to
he
k
if it is now
orre
t. Had the data been truly random, it would be unlikely that the same
sequen
e would get generated during the exe
ution. However, sin
e you use a pseudo random
sequen
e, you are guaranteed to get the same sequen
e if you set the same seed!
Of
ourse, you might also want to the program to run dierently on ea
h o
asion. In
su
h
ases, you
an use the
ommand time to set the seed, i.e. write
srand(time());
The time
ommand returns the
urrent time in se
onds sin
e some midnight of January 1,
1970, or some su
h moment. Clearly, you
an expe
t it to be dierent on ea
h run.
12.7.1 The randuv fun
tion in simple
pp
In simple
pp we have provided the fun
tion randuv whi
h takes two double arguments
u,v and returns a random double in the range u through v. Our
ommand
alls the C++
supplied fun
tion rand, and returns the following value:
u + (v-u)*rand()/(1.0 + RAND_MAX)
As you
an see this value will be between u and v and uniformly distributed to the extent
rand is uniformly distributed.
If you want random numbers between integers i,j, you must
all randuv(i,j+1) and
onvert it to an integer. This will give you uniformly distributed integers between i and j.
You
an use srand to set the seed as before.
We began the
hapter by stressing the need to
learly understand the spe
i
ation; indeed
many errors happen be
ause the spe
i
ations are not properly understood by the programmer. We also dis
ussed some strategies for developing test
ases.
We dis
ussed a few tools for helping the pro
ess of program development: input/output
redire
tion, and assertions.
As to debugging, the main idea suggested was to put in print statements to see whether
the program was exe
uting as per your expe
tation. We also pointed out the possibility of
errors in data input, and how to deal with them. As an aside we dis
ussed the notion of
input expressions, using whi
h you
an read till the end of the le, without knowing how
many values are present in the le.
We also dis
ussed (pseudo) random number generation, whi
h will be useful for generating
random input instan
es. But (pseudo) random numbers are also useful used in general, e.g.
Chapter 25.
You may nd many suggestions in this
hapter to be very
autious, if not paranoid.
But when it
omes to serious programming, it is better in the long run to be humble and
paranoid.
214
1. For the digit
ounting problem
ould the
ondition 10d > n be 10d n instead? What
if we did not require d to be a positive integer? Give a
risp answer, i.e. give inputs
for whi
h the new spe
i
ations would require an answer dierent (wrong!) from that
required by the old one.
2. Here is a \
lever" observation about the digit
ounting problem. Suppose a number
n has d digits. Then bn=10
has d 1 digits. Thus we simply
ount the number of
times we
an divide by 10 till we get zero and that will be the number of digits of the
number. So the program is:
main_program{
int n, d=0;
in >> n;
while(n>0){
n = n/10;
++d;
}
}
Is this program
orre
t? Would you have written this program if you had followed the
pro
ess suggested in this
hapter? For what values of the input would you test the
program?
3. Write the program that nds the smallest
ir
le
overing a given set of points. Allow
the user to supply the points by
li
king on the s
reen, and show the smallest
ir
le
also on the s
reen. Hint: Argue that the smallest
overing
ir
le must either have as
diameter some two input points, or must be a
ir
um
ir
le of some three input points.
Now just
onsider all possible
andidate
ir
les, and pi
k the one that a
tually
overs
all points.
4. What are good test
ases for the smallest
overing
ir
le problem?
5. Design input instan
es to test the in
ome tax
al
ulation problem of Chapter 6.
Chapter 13
Arrays
Here are some real life problems that we may want to solve using
omputers.
Given the marks obtained by students in a
lass, print out the marks in non-de
reasing
order, i.e. the smallest marks rst.
Given a road map of India nd the shortest path from Buldhana to Jhumri Talaiya.
Given the positions, velo
ities and masses of stars, determine their state 1 million years
from today.
In prin
iple, we
ould write programs to solve these problems using what we have learned so
far; however there will be some di
ulties be
ause of sheer size: our programs might have
to deal with thousands of stars or hundreds of students or roads. Even writing out distin
t
names for variables to store data for ea
h of these entities will be tiring.
Most programming languages provide
onvenient me
hanisms using whi
h we
an tersely
deal with large
olle
tions of obje
ts. In C++ there are two su
h me
hanisms.
1. Arrays: This is an older me
hanism whi
h was also present in the C language, and as
a result
an also be used in C++. In this
hapter we will
onsider arrays at length.
2. Ve
tors: This is a newer me
hanism, whi
h is only present in C++. In C++ programs,
we will re
ommend that you use ve
tors rather than arrays. We dis
uss ve
tors in
Chapter 20.
Even though we would like you to use ve
tors eventually, we will study arrays at some length
for two reasons. First, the basi
features of ve
tors are also present in arrays. So what you
learn in this
hapter will be useful later too. In addition, the working of an array is easier
to understand. Ve
tors on the other hand,
ontain some \hidden parts" so to say. Finally,
arrays are used substantially even today, and a knowledge of arrays is therefore useful.
215
216
This single statement denes 1000 variables! The rst of these is referred to as ab
[0,
next as ab
[1, and so on till ab
[999. The
olle
tion of these 1000 variables is said to
onstitute the array named ab
, and ab
[0, ab
[1, ..., ab
[999 are said to
onstitue the
elements of the array. Any identier (Se
tion 3.1.1)
an be used to name an to array. What
is inside [ is said to be the index of the
orresponding element. The term subs
ript is also
used instead of index. It is important to note that indi
es start at 0, and not at 1 as you
might be in
lined to assume. The largest index is likewise one less than the total number of
elements. The total number of elements (1000 in the above example) is referred to as the
length or the size of the array. As we know, an int variable needs one word of spa
e, so the
statement above reserves 1000 words of spa
e in one stroke.
The spa
e for an array is allo
ated
ontiguously in the memory of the
omputer, in the
order of the index, i.e. ab
[1 is stored in memory following ab
[0, ab
[2 following
ab
[1 and so on.
You may dene arrays of other kinds also, e.g.
float b[500;
You
an mix up the denitions of ordinary variables and arrays, and also dene several arrays
in the same statement.
double
, x[10, y[20, z;
This statement denes variables
, z of type double, and two arrays x, y also of type
double, respe
tively having lengths 10, 20. Note that one variable of type double requires
2 words of spa
e, so this statement is reserving 2 words ea
h for
, z, and respe
tively
2 10; 2 20 words for x,y.
You may dene arrays in the main program or inside fun
tions as you wish. Note however,
that variables dened inside fun
tions are destroyed on
e the fun
tion returns. This applies
to arrays dened in fun
tions as well.
As per the C++ standard, the length of the array should be spe
ied in the denition
using a
onstant. However, also see Se
tion 13.7.
13.1.1 Array element operations
Everything that
an be done with a variable
an be done with the elements of an array of
the same type.
int a[1000;
in >> a[0;
a[7 = 2;
// stores 2 in a[7.
217
In the rst statement after the denition of a, we are reading into the zeroth element a[0
of a, just as we might read into any ordinary variable. You
an also set the value of an
array element by assigning to it, as in the statement a[7=2;. The statement following
that, b=5*a[7;, uses the element a[7 in an expression, just as you might use an ordinary
variable. This is also perfe
tly ne. Note that just like ordinary variables, an element must
have a value before it is used in an expression. In other words, it would be improper in the
above
ode to write int b = 5*a[8; be
ause a[8 has not been assigned a value.
Elements of an array behave like ordinary or s
alar variables of the same type; so they
an be passed to fun
tions just like s
alar variables. Hen
e we
an write g
d(a[0,a[7);
if we wish, assuming g
d is a fun
tion taking two int arguments.
In the last line in the
ode the index is not given dire
tly as a number, but instead an
expression is provided. This is a
eptable. When the
ode is exe
uted, the value of the
expression will be
omputed and will be used as the index. In the present
ase, by looking
at the pre
eding
ode we know that b will have the value 10, and hen
e a[b*2 is simply
a[20. So 234 will be stored in a[20.
13.1.2 A
eptable range for the index
When using arrays in your programs, it is very important to keep in mind that the array
index must always be between 0 (in
lusive) and the array size (ex
lusive). For example, for
the array a are dened above, a referen
e a[1000 would be in
orre
t, be
ause it is not in
the range 0 to 999. Likewise, a referen
e a[b*200 would also be in
orre
t, be
ause it is
really the referen
e a[2000 given that b has value 10 in the
ode above.
If su
h referen
es are present in your program, its behaviour
annot be predi
ted. The
program may generate wrong values, fail to terminate, or terminate with an error message.
Any one of these out
omes is possible, and C++ does not say whi
h will happen.
Simply put, it is vital that you, the programmer, make sure that array indi
es are in the
required range. This is an extremely important requirement.
13.1.3 Initializing arrays
It is possible to
ombine denition and initialization. Suppose we wish to
reate a 5 element
float array
alled pqr
ontaining respe
tively the numbers 15, 30, 12, 40, 17. We
ould do
this as follows.
float pqr[5 = {15.0, 30.0, 12.0, 40.0, 17.0};
in whi
h the size of the array is not expli
itly spe
ied, and it is set by the
ompiler to the
number of values given in the initilizer list. You
an of
ourse mix denitions of arrays with
or without initialization, and also the denition of variables.
int x, squares[5 = {0, 1, 4, 9, 16},
ubes[={0, 1, 8, 27};
218
This will
reate a single int variable x, and two initialized arrays, squares of length 5, and
ubes of length 4.
Of
ourse, it might be more
onvenient to initialize arrays separately from their denitions, espe
ially if they are large. So if we wanted a large table of squares, it might be more
onvenient to write:
int squares[100
for (int i=0; i<100; i++)
squares[i = i * i;
The
ommon use of arrays is to store values of the same type, e.g. velo
ities of parti
les,
marks obtained by students, lengths of roads, times at whi
h trains leave, and so on. You
ould also say that an array is perfe
t to store any sequen
e x ; x ; : : : ; xn. Of
ourse, sin
e
array indi
es start at 0 in C++, it is more
onvenient to
all the sequen
e x ; x ; : : : ; xn ,
and then store xi in ith element of a length n array. As will be dis
ussed in Se
tion 14.1,
an array
an be used to store text. An array
an also be used to store a ma
hine language
program: the ith element of the array storing the ith word of the program (Se
tion 2.8). We
will see many su
h uses in the rest of this
hapter and the following
hapters.
In this se
tion we give some typi
al examples of programs that use arrays. You will see
some standard programming idioms for dealing with arrays.
1
Many might not like the idea of displaying marks in publi
. An exer
ise asks you to add a password so
that ea
h student
an only see her marks.
1
219
Clearly we should use an array to store the marks. It is natural to store the marks of the
student with roll number 1 in the 0th element of the array, the marks of student with roll
number 2 in the rst element, and in general, the marks of the student with roll number i
in the element at index i 1. So we
an dene the array as follows.
float marks[100; // marks[i stores the marks of roll number i+1.
You are probably wondering whether we need to
hange the program if the number of
students is dierent. Hold that thought for a while, we will dis
uss this issue in Se
tion 13.7.
Next we read the marks into the appropriate array elements.
for(int i=0; i<100; i++){
out << "Marks for roll number " << i+1 << ": ";
in >> marks[i;
}
Remember that when the statement
in >> marks[i; is exe
uted, the then
urrent value
of i is used to de
ide whi
h element gets the value read. Thus in the rst iteration of the
loop i will have the value 0, and so what is read will be stored in marks[0. In the se
ond
iteration i will have the value 1 and so the newly read value will be stored in marks[1, and
so on. Thus indeed we will have the marks of a student with roll number i+1 be stored in
marks[i as we want.
In the last part of the program, students enter their roll numbers and we are to print
out the marks for the entered roll number. Sin
e this is to happen till -1 is given as the roll
number, we
learly need a while loop. There are various ways to do this, we
hoose one with
a break, similar to Se
tion 7.2
while(true){
out << "Roll number: ";
int rollNo;
in >> rollNo;
if(rollNo == -1) break;
}
Clearly, if you typed 35 in response to the query \Roll number: \, then you would want the
marks for roll number 35, and these would be stored in marks[34. But this is exa
tly the
same element as what is printed, marks[rollNo-1.
The program given above will work ne, so long as the roll number given is either -1 or in
the range 1 through 100. If a number other than these is given, say 1000, the program will
attempt to read marks[999. As we said, this may result in some irrelevant data to be read,
or worse, the program may a
tually halt with an error message. Halting is not a
eptable
in this situation, be
ause students
oming later will then not be able to know their marks.
Fortunately we
an easily prevent this. If the roll number is not in the given range, then we
an say so and not print any marks. So the
ode should really be as follows.
220
while(true){
out << "Roll number: ";
int rollNo;
in >> rollNo;
if(rollNo == -1) break;
The next step is to print the roll numbers of those students who got marks equal to maxSoFar.
This is easily done, we examine ea
h marks[i, for all i as i goes from 0 to 99, and whenever
we nd marks[i equalling maxSoFar, we print out i+1, be
ause we stored the marks of roll
number i+1 at index i.
for(int i=0; i<100; i++)
if(marks[i == maxSoFar)
out << ``Roll number `` << i << `` got maximum marks.'' << endl;
221
We
onsider the marks display problem in this new setting. We will use an additional
array rollno in whi
h to store the roll number, in addition to the array marks used above.
The tea
her rst types in 100 pairs of number, ea
h pair
onsisting of a roll number and the
marks obtained by the student having that roll number. Our program must read in the roll
number and marks and store them in the arrays rollno and marks. In the se
ond phase,
when a student types in a roll number, we must rst look for it in the array rollno. If it is
found, then we print the
orresponding marks.
int rollno[100;
double marks[100;
for(int i=0; i<100; i++)
in << rollno[i << marks[i;
while(true){
int r;
in >> r; // roll number whose marks are requested
if(r == -1) break;
for(int i=0; i<100; i++)
if(rollno[i == r)
out << marks[i << endl;
}
This idea, s
anning an array from the beginning to the end in order to determine if a
ertain
element is stored in the array, is sometimes
alled linear sear
h.
The
ode above is unsatisfa
tory in two ways. First, if the given value r is not present
in the array, it would be polite to print a message to that ee
t. Se
ond, on
e we nd r
at some index, there is no need to s
an the remaining elements. Both these goals
an be
a
heived by repla
ing the for loop above with the following.
int i;
for(i = 0; i<100; i++){
if(rollno[i == r){
out << marks[i << endl; break;}
}
if(i >= 100)
out << "Invalid roll number.\n";
Note rst that we break out of the loop upon nding a mat
h. Thus, if a mat
h is found
the variable i (whi
h has now been dened outside the loop) will have a value less than
100. The
he
k at the end su
eeds only if all 100 iterations were exe
uted without nding
a mat
h, i.e. if the roll number r is invalid.
13.2.5 Histogram
Our next example is tri
kier, and it illustrates an important powerful feature of arrays.
Again, we have as input the marks of students in a
lass. Assume for simpli
ity that
the marks are in the range 0 through 99. We are required to report how many students
got marks between 0 and 9, how many between 10 and 19, how many between 20 and 29
and so on. As you might know, what we are asked to report is often
alled a histogram in
222
Statisti
s .
We are required to report 10 numbers. So it
ould seem natural to use an array of 10
elements. The 0th element of the array
an be used to
ount the number of marks in the
range 0-9, the rst element for the range 10-19 and so on. So in general we
ould say ith
element of the array should
orrespond to the range i*10 to (i+1)*10-1 (both in
lusive).
So we
all the array
ount and dene it as:
2
int
ount[10; //
ount[i will store the number of marks in the range
// i*10 through (i+1)*10 -1.
Clearly, we should set the
ounts to 0 at the beginning, and
hange them as we read in the
marks.
for(int i=0; i<10; i++)
ount[i=0;
When we read the next mark, how do we de
ide whi
h
ount to in
rement? It is natural to
write something like the following.
for(int i=0; i< 100; i++){
float marks;
in >> marks;
if(marks <= 9)
ount[0++;
else if(marks <= 19)
ount[1++;
else if(marks <= 29)
ount[2++;
else if(marks <= 39)
ount[3++;
else if(marks <= 49)
ount[4++;
else if(marks <= 59)
ount[5++;
else if(marks <= 69)
ount[6++;
else if(marks <= 79)
ount[7++;
else if(marks <= 89)
ount[8++;
else if(marks <= 99)
ount[9++;
else
out << "Marks are out of range." << endl;
}
This works, but there is a better way! Suppose we read a mark m, whi
h
ount should we
in
rease? For this we simply need to know the tens pla
e digit of m. As you might observe,
this is simply bm=10
, i.e. the integer part of m=10. But we
an get the integer part by
storing into an integer variable! This is what the following
ode does.
for(int i=0; i< 100; i++){
float marks;
in >> marks;
int index = marks/10;
In general a histogram is a
ount of number of observations (marks, in our
ase) falling in various ranges
of values (in our
ase the intervals 0-9, 10-19 and so on). The
ounts are often depi
ted as a bar
hart, in
whi
h the height of the bars is proportional to the
ount and width to the size of the range.
2
223
Note that this works only be
ause all the ranges are of the same size. But this is very often
the
ase when
omputing histograms.
13.2.6 A taxi dispat
h program
Suppose you are the Mumbai dispat
her for the Mumbai-Pune taxi servi
e. Your job is as
follows. Drivers of taxis that are willing to take passengers to Pune report to you and give
you their driver ID number and wait. Passengers who want taxis also report to you. When
a passenger reports, you
he
k if there are any waiting taxis. If there are, you assign the
taxi of the driver that reported to you the earliest. Clearly, on
e a taxi has been given
to a passenger, you need not keep the
orresponding ID number on your list. If no taxis
are available, you let the passenger know. You are not expe
ted to keep tra
k of waiting
passengers, though an exer
ise asks you to do pre
isely this. You may assume that at any
given point there will not be more than 100 taxis waiting for passengers. You are to write a
program whi
h will help you dispat
h taxis as required.
Let us make this more spe
i
. Suppose that the dispat
her will type 'd' when a driver
arrives, followed by the driverID. Likewise when a
ustomer arrives, the dispat
her will type
'
', and expe
t the program to print the ID of the assigned driver. Finally, to terminate the
program, we will have the dispat
her type 'x' (
ommonly used as abbreviation of eXit).
Next we de
ide what variables we might need and how we should be using them.
Clearly, we will need to store the IDs of the waiting drivers. It seems natural to use an
array, say driverID, to store these. Assume for simpli
ity that the IDs are integers with 9
or fewer digits, i.e. that they will t in int. The size of the array
an be a number larger
than the number of drivers we expe
t will be waiting with us at any time. Most of the time
there will be fewer drivers waiting with us than the size of the array, so we presumably need
a variable nWaiting whi
h will denote the number of waiting drivers.
We also need to somehow re
ord the order in whi
h the drivers arrived, be
ause we want
to assign the next
ustomer to the driver who has registered with us the earliest. A natural
way to do this is to store the earliest waiting driver at index 0, the next earliest at index 1,
and so on. The ID of the driver that arrived last would be at index nWaiting - 1.
If a new driver arrives, we
an store his ID at the index nWaiting, and in
rement
nWaiting. If a
ustomer arrives, we
an assign the driver at driverID[0. However, on
e
we assign the driver, we must shift up all the other entries in the array, sin
e we have de
ided
that the waiting drivers must be stored starting at index 0. This is expressed in the following
ode.
onst int n = 100; // estimate of max waiting drivers.
int driverID[n, nWaiting = 0;
while(true){
224
Note that we have added
he
ks to see if the array driverID is already full when a driver is
to be entered, and to see if there is at least one element in it when a
ustomer arrives.
You might think that perhaps there should be a way to write the program without having
to shift up the entries in driverID when we assign the driver at index 0. Instead of moving
up the drivers in the array,
ould we not adjust our notion about where the front of the
queue is? Indeed this will work. But it will need a bit more
are. To do this right, perhaps
it is worth
onsidering how we might have dispat
hed taxis without
omputers.
Dispat
hing without
omputers
It is always worth thinking about how any problem, in
luding taxi dispat
hing, might be
solved without
omputers. Say the dispat
her writes the driverID numbers on a bla
kboard,
top to bottom, as the drivers report. When a driver arrives, we put down the number at the
bottom of the list. When a passenger
omes in, the number at the top of the list is given to
the passenger, and then the number is erased.
For simpli
ity, let us assume that our bla
kboard
an only hold 100 phone numbers.
Managing this spa
e on the bla
kboard turns out to be slightly tri
ky. Suppose 60 drivers
report, and you write down their numbers, starting at the top. Suppose you next have 50
passengers, so you mat
h them to the top 50 numbers, whi
h you erase. At this point you
have only 10 numbers on the board, however, they are not at the top of the board, but
they start halfway down the board. Suppose now 60 more drivers report. You would pla
e
40 of these numbers below the 10 you have on the board, and that would take you to the
bottom of the board. Where should you pla
e the remaining 20? It is natural to start writing
numbers from the top again, as if the bottom of the board were joined to the top. Think of
the bla
kboard as forming the
urved surfa
e of a
ylinder! Thus at this point, you have 70
225
front + nWaiting % n
0
1
0
1
0
1
Occupied
by numbers
Unused
front + nWaiting % n
front
Occupied
by numbers
Unused
Unused
front+nWaiting % n
front
Unused
n 1
n 1
Occupied
by numbers
n 1
Our program will mirror the a
tions given above. We do not shift drivers up when a driver
is assigned, instead we have a variable front, whi
h will always
ontain the index of the
element of driverID
ontaining the earliest waiting unassigned driver. This performs the
fun
tion of the mark that the dispat
her pla
es on the board.
If there are nWaiting drivers waiting for
ustomers, their IDs will appear at positions
starting at front. We need to be slightly
areful as we say this: how do we des
ribe what
happens when the board lls to the bottom and the dispat
her is for
ed to write the numbers
starting at the top again? We would like to somehow say that index that
omes \next" after
the last index n-1 (i.e. the \bottom \ of the board) is index 0 (i.e. the \top" of the board).
So instead of saying that the next index after front is front+1, we will say that it is
(front+1) % n. If front has some value i<n-1, then (front+1) % n is just i+1. However,
if front equals n-1, then (front+1) % n will indeed be
ome 0 as we wish. Thus we will
simply require that if there are nWaiting drivers that are waiting, their IDs will be at indi
es
front, (front + 1) % n, : : :, (front + nWaiting - 1) % n.
Next we
onsider what a
tions to exe
ute when a driver arrives. As before, we a
ept
the ID only if driverID is not full. The ID of the arriving driver must be added at the so
226
as to not violate the property mentioned above, i.e. at position (front + Waiting) % n.
After that we must in
rement nWaiting.
When a
ustomer arrives, as before we rst
he
k if there are any waiting drivers. If there
are, we assign the driver at the front of the queue, i.e. driverID[front. We add one to
front to get to the next element of driverID. however, sin
e we want to
onsider the queue
to start again from the top, the addition is done modulo n. Finally, we must de
rement
nWaiting.
onst int n = 100; // estimate of max waiting drivers.
int driverID[n, nWaiting = 0, front = 0;
while(true){ /* Invariants: nWaiting denotes the number of waiting drivers.
0 <= nWaiting <= n. IDs of waiting drivers are in driverID,
from driverID[front to driverID[(front + nWaiting - 1) %n .
0 <= front < n
*/
har
ommand;
in >>
ommand;
if(
ommand == 'd'){
// driver arrives
if(nWaiting >= n)
out << "Queue full.\n";
else{
in >> driverID[(front + nWaiting) % n;
nWaiting++;
}
}
else if(
ommand == '
'){
//
ustomer arrives
if(nWaiting == 0)
out << "Nothing available. Try later.\n";
else{
out << "Assigning " << driverID[front << endl;
front = (front + 1) % n;
nWaiting--;
}
}
else if(
ommand == 'x') break;
else
out << "Illegal
ommand.\n";
}
You might have noted that it was easy to write this program on
e we de
ided
learly how
our variables would be used. This is often a good strategy in writing programs.
13.2.7 A geometri
problem
Suppose we are given the positions of the
enters of several
ir
les in the plane as well as
their radii. Our goal is to determine whether any of the
ir
les interse
t. Let us say that the
ith
ir
le has
enter (xi ; yi ) and radius ri , for i = 0; : : : ; n 1.
Whether a pair of
ir
les interse
t is easy to
he
k: the
ir
les interse
t if and only if the
distan
e between their
enters is smaller than or equal to the sum of their radii. In other
227
(xi
xj )2 + (yi
yj )2 r i + r j
int n=5;
float x[n, y[n;
float r[n;
for(int i=0;i<n;i++)
// read in all data.
in >> x[i >> y[i >> r[i;
// Find interse
tions if any.
for(int i=0; i<n; i++){
for(int j=i+1; j<n; j++){
if(pow(x[i-x[j,2)+pow(y[i-y[j,2) <= pow(r[i+r[j,2))
// built in fun
tion pow(x,y) = x raised to y.
out << "Cir
les " << i << " and " << j << " interse
t." <<endl;
}
}
Thus in the rst iteration of the outer for loop, we
he
k for interse
tions between
ir
le 0
and 1; 2; 3; : : : ; n 1. In the se
ond iteration, we
he
k for interse
tions between
ir
le 1 and
ir
les 2; 3; : : : ; n 1, and so on. Is this
lear that we
he
k all pairs of
ir
les in this pro
ess?
Consider the kth
ir
le and the lth
ir
le, k 6= l. Can we be sure that the interse
tion
between them is
he
ked? Clearly, if k < l, then in the iteration of the outer for loop in
whi
h i takes the value k, we will
he
k interse
tions with
ir
les k + 1; k + 2; : : : ; n 1.
This sequen
e will
ontain l be
ause k < l. Alternatively, suppose l < k. Then
onsider the
iteration of the outer for loop in whi
h i= l. In this iteration we will
he
k the interse
tion
of
ir
le l with
ir
les l +1; : : : ; n 1. Clearly k will be in this sequen
e be
ause l < k. Thus
in either
ase we will
he
k the interse
tion between
ir
le k and
ir
le l, for every k; l.
We now dis
uss some details regarding arrays and array a
esses. This will spe
ially be useful
for understanding how we dene fun
tions for operating on arrays. To make the dis
ussion
more
on
rete, suppose we have the following denitions.
int p=5, q[5={11,12,13,14,15}, r=9;
float s[10;
228
Say ea
h variable of type int is most
ommonly given 4 bytes of memory, and so is a float.
Thus we know the above denitions will
ause 4 bytes of memory to be reserved for p,
4 5 = 20 bytes for q, 4 for r, and 4 10 = 40 bytes for s. We have also said that the
memory given for an array is
ontiguous. Thus, the memory for q will start at a
ertain
address, say Q, and go on to address Q +19. The notion of addresses is as per our dis
ussion
in Chapter 2 and Se
tion 9.8. Consistent with this des
ription, Figure 13.3 shows how spa
e
might have been allo
ated for these variables.
Next we
onsider what happens when during exe
ution we en
ounter a referen
e to an
array element, e.g. q[expression. How does the
omputer know where this element is
stored? Of
ourse, rst the expression must be evaluated. Suppose its value is some v.
Then we know that we want the element of q of index v. But be
ause the elements are
stored in order, we also know that the element with index v is stored at Q + 4v, where Q
is the starting address for q. Thus if v = 3 then we would want q[3, whi
h is stored from
Q + 12. In general, the v th element of an array whi
h is stored starting at address A would
be at A + kv, where k is the number of bytes needed to store a single element.
So the important point is, that to get to an array element, the
omputer must evaluate
the index expression, and even after the expression is evaluated it must perform the multipli
ation and addition to get the address A + kv. This is in
ontrast to how the
omputer
gets the address for an ordinary variable su
h as p. In this
ase, the
omputer already knows
where it stored p, and so it
an get to it dire
tly. Do note however that the extra work
needed to gure out where the element is stored is independent of the length of the array.
13.3.1 Out of range array indi
es
Suppose now that our program has a statement q[5=17;. Using the formula A + kv given
above, the
omputer would try to store 17 in the int beginning at the address Q + 4 5 =
Q + 20. Noti
e that this is outside the range of memory allo
ated for q . In fa
t, it is quite
possible, as shown in our layout of Figure 13.3, that r is given the memory Q + 20 through
Q + 23. Then the statement q[5=17 might end up
hanging r! Likewise it is
on
eivable
that a statement like q[-1=30; might end up
hanging p.
Suppose on the other hand, we wrote q[10000=18;. This would require us to a
ess
address Q + 40000. It is
on
eivable that there isnt any memory at this address. Many
omputers have some
ir
uits to sense if an a
ess is made to a non-existent address or even
some forbidden addresses. The details of this are outside the s
ope of this book, but if
this happens, then the program might halt with an error message. In any
ase, it is most
important to ensure that array indi
es are within the required range.
13.3.2 The array name by itself
So far we have not said whether the name of an array
an be used in the program by itself,
i.e. without spe
ifying the index. It turns out that C++ allows this.
In C++, the name of an array by itself is dened to have the value equal to the starting
address from where the array is stored. This is an important pla
e where arrays work
dierently from ve
tors, as we will see in Chapter 20.
Continuing our example of Figure 13.3, sin
e the array q is stored starting at address Q,
229
230
the value of the name q itself would thus be Q, and the value of s, Q +24. Sin
e the variable
at address Q is q[0, of type int, it is natural to dene the type of q to be pointer to int,
or address of int, or int*. In general, if an array
ontains elements of type T, then its name
will have type T* or address of T or pointer to T.
Thus s would be of type address of float or pointer to float or float*, and would have
the value Q + 24.
It seems strange that the name of an array is only asso
iated with the starting address,
and that the length of the array is not asso
iated with the name. This is merely a matter of
onvenien
e, and its utility will be
ome
lear in Se
tion 13.4.
An important point to note is that the value asso
iated with the name of an array, say q,
annot be
hanged; it always means the address of q[0. In other words, you
annot write
an expression su
h as
q = ...;
The rst point to note is that the assignment s = speed is very mu
h legal, sin
e speed is
of type double*, just like s. Thus after the assignment, s will have the same value as speed.
But then, the expressions s[j will mean the same as speed[j.
Put dierently, the expression s[0 denotes the double value stored at the address s +
k*i, where k is the size of double, and i the value of index, whi
h are respe
tively 8 and 0.
Yes, this is an unusual way of writing a binary expression. But do note that there are other operations
whi
h are not written in the order operand1 operator operand2. For example, we often write ab rather than
.
3
231
In other words, s[0 means the double stored at address s whi
h is the same as speed, and
hen
e is the same as the value stored at address speed, and so is speed[0. Thus the rst
print statement will print 1.25. The statement s[1 is likewise equivalent to speed[1, i.e.
to 3.9, whi
h is what the se
ond print statement will print.
Fun
tions are
onvenient with ordinary, or s
alar variables, and indeed we
an imagine that
they will be
onvenient with arrays as well. Suppose we have an array of
oats dened as
float a[5; and somewhere in the program we need to
al
ulate the sum of its elements.
It is not di
ult to write the
ode to
ompute the sum of the elements of an array, however,
if the sum is needed for several su
h arrays in our
ode, then will have to repli
ate the
ode
that many times. So it would be very
onvenient to write a fun
tion whi
h takes the array
as the argument and returns the sum.
As it happens, we have told you everything you need to write the fun
tion! Here is what
you
ould write.
float sum(float* v, int n){
float s = 0;
for(int i=0; i<n; i++)
s += v[i;
}
return s;
We will explain this shortly. First we show how this fun
tion might be
alled from a main
program.
int main(){
float a[10 = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0}, asum;
asum = sum(a, 5); // se
ond argument should be array length
}
Let us rst
he
k whether the fun
tion
all is legal, i.e. whether the types of the arguments
mat
h those of the parameters in the denition in the fun
tion sum. The rst argument to
the fun
tion
all is the array name a. We said that the type asso
iated with the array name
is T*, if the elements in the array are of type T. Thus the type of a is float*. This indeed
mat
hes the type of the rst parameter, v, in the fun
tion denition. The se
ond argument,
5,
learly has type int whi
h mat
hes the type of the se
ond parameter n. Thus the
all is
legal and we now think about how it exe
utes.
When the
all length(a, 5) is en
ountered, as usual an area is
reated for exe
ution of
the fun
tion sum. The values of the non-referen
e arguments are
opied. In the present
ase,
none of the parameters are referen
e parameters, and so the values of both arguments are
opied. The value of the rst argument, a, is the starting address, say A, of the array a in
memory. Thus v gets the value A. The value of the se
ond argument is 5. Thus n gets the
232
value 5. It is very important to note here that the
ontent of all the lo
ations in whi
h the
array is stored are not
opied, but only the starting address is
opied.
The
ode of the fun
tion is then exe
uted. The only new part is the expression v[i.
This is pro
essed essentially a
ording to the rule given earlier. We know that v has type
address of
oat, and its value is A. So now the expression v[i is evaluated as dis
ussed in
the previous se
tion, by
onsidering [ to be an operator and so on. Instead of doing the
pre
ise
al
ulation again, we merely note that the value of v[i evaluated in sum must be
the same as the value of a[i evaluated in the main program, be
ause v has the same value
and type as a. Hen
e, v[i will in fa
t denote the ith element of a. Be
ause n has value
5, in the loop i will take values from 0 to 4. Thus a[0 through a[4 will be added as we
desired.
Some remarks are in order.
1. Another syntax
an also be used to de
lare parameters that are arrays, in this
ase
arrays of float variables: float v[ { this dire
tly suggests that v is like an array
ex
ept that we do not know its length.
2. Fun
tion sum does not really know that it is operating on the entire array. For example
the
all sum(a,3) is also allowed. This would return the sum of the rst 3 elements of
a, sin
e the loop in the fun
tion will exe
ute from 0 to 2.
3. Modifying the passed array is also possible. If your fun
tion had a line at the end su
h
as v[0=5.0;, that would indeed
hange a[0. This is
onsistent with the me
hanism
we have dis
ussed for evaluating expressions involving [.
13.4.1 Examples
Shown below are two simple examples of fun
tions on arrays. The next se
tions gives more
involved examples.
Our rst fun
tion merely prints the values of the elements of a float array.
void print(float *a, int n){
for(int i=0; i< n; i++)
out << a[i << endl;
}
This will print out the rst n elements of the array. Note that it is the responsibility of the
alling program to ensure that the an array is passed, and that the array has length at least
as mu
h as the se
ond argument.
Next, we present a fun
tion whi
h returns the index of an element whose value is the
maximum of all the elements in the array. Note the
areful phrasing of the last senten
e:
when we say \an element", we a
knowledge the possibility that there
ould be many su
h
elements, and we are returning the index of only one of them.
The idea of the fun
tion is very similar to what we did for nding the maximum marks
from the marks array in Se
tion 13.2.3. We have a variable maxIndex whi
h will return the
position of an element with the maximum value. We start by initializing it to 0, whi
h is
equivalent to
onje
turing that the maximum appears in position 0. Next, we
he
k if the
233
subsequent elements of the array are larger, if we nd an element whi
h is larger, then we
assign its index to maxIndex.
int argmax(float marks[, int L)
// marks = array
ontaining the values
// L = length of marks array. required > 0.
// returns maxIndex su
h that marks[maxIndex is largest in marks[0..L-1
{
int maxIndex = 0;
for(j = 1; j<L; j++)
if( marks[maxIndex > marks[j) // bigger element found?
maxIndex = j;
// update maxIndex.
return maxIndex;
}
We have given the name marks so that it is easy for you to see the similarity between this
ode and the
ode in Se
tion 13.2.3. But by itself this fun
tion does not have anything
to do with marks. So if you write it independently some more appropriate name su
h as
datavalues should be used instead of the name marks.
13.4.2 Summary
The most important points to note are as follows.
To pass an array to a fun
tion, we must typi
ally pass 2 arguments, the name of the
array, and the length of the array. This is to be expe
ted, the name only gives the starting
address of the array, it does not say how long the array is. So the array length is needed.
The
alled fun
tion
an read or write into the array whose name is sent to it. This is like
sending the address of one friend A to another friend B, Surely then B will be able to write
to A or visit A just as you
an!
Finally, it is worth noting an important point. When we write a fun
tion on arrays, it
may be
onvenient to allow it to be
alled with length spe
ied as 0. What should a fun
tion
su
h as sum to when presented with an array of zero length? It would seem natural to return
the sum of elements as 0. This is what our sum fun
tion does. On the other hand, our
argmax fun
tion requires that the length be at least 1. Su
h (pre)
onditions on a
eptable
values of parameters should be
learly stated in the
omments.
We will
onsider a problem dis
ussed at the beginning of the
hapter: given the list of marks,
print them out in the order lowest to highest. We
ould ask that along with the marks, we
also print out the roll numbers, however, this is left for the exer
ises.
We
an a
omplish our task in two phases. In the rst phase, we rearrange the element
values in a non-de
reasing order, i.e. so that the values appearing at lower indi
es are no
larger than those appearing at larger indi
es. This operation is often
alled sorting. This
is one of the most important operations asso
iated with an array. We will present a simple
234
algorithm
alled Sele
tion sort for this. Better algorithms will be given later. On
e the
elements are arranged in a non-de
reasing order, we
an simply print out the array elements
by index, i.e. element 0, then element 1 and so on. This will ensure that the marks are
printed in non-de
reasing order. For this, we
an simply use the fun
tion print dened
earlier.
We use a fairly natural idea for sorting. We begin by looking for the largest value in
the array, and we move it to the last position, i.e. index n 1, where n is the length of
the array. Of
ourse, position n 1 itself
ontains a value, and we
annot destroy that. So
we instead ex
hange the two: the maximum value moves to the n 1th position and the
value in the n 1th position moves to wherever the maximum was present earlier. Next,
we nd the maximum value amongst elements in positions 0 through n 2. This maximum
is ex
hanged with the element in position n 2. Thus we have the maximum and se
ond
maximum at positions n 1 and n 2. In general, we pro
eed in this manner, in a typi
al
iteration, we will nd the maximum from the rst i values, and then ex
hange that with
the value at the i-1th index. We will have i begin with the value n, and
ount it down in
su
essive iterations till we rea
h 2.
For nding the maximum, it is
onvenient to use the argmax fun
tion dened in Se
tion 13.4.1. If we want the maximum from the rst i elements, we simply invoke it using i
as the se
ond argument. As we noted there, argmax need not be passed the a
tual length of
the array; if it is passed a smaller value i it will merely nd a maximum in the rst elements
and return its index. So the
ode is quite obvious.
void SelSort(float data[, int n)
// will sort in NON-DECREASING order. different from above.
{
for(int i=n; i>1; i--){
int maxIndex = argmax(data,i); // Find index of max in data[0..i-1
float maxVal = data[maxIndex; // Ex
hange elements at
data[maxIndex = data[i-1;
//
index maxindex
data[i-1 = maxVal;
//
and index i-1.
}
}
It should be
lear that the
ode above is doing what we des
ribed. It is instru
tive to write a
loop invariant also: At the beginning of the iteration, the subarray data[i..n-1
ontains
largest values. At the beginning i=n and hen
e the invariant is va
uously true.
Suppose the invariant is true at the beginning of some iteration, we will prove that it
will also hold for the next. The rst statement of the loop nds the maximum in the rst i
elements, i.e. elements 0 through i-1. The last 3 statements ex
hange this with the element
at index i-1. Thus at the end of the iteration, the subarray data[i-1..n-1 will
ontain
the largest values. This establishes the invariant for the beginning of the next iteration.
13.5.1 Estimate of time taken
We will try to get a rough estimate of the time needed by Sele
tion sort. By rough estimate
we merely mean whether the time is proportional to n, the number of elements being sorted,
235
or their square and so on. Su
h estimates
annot be used to de
ide how many se
onds
will be needed to exe
ute the program. However, if we know that one program sorts in
time proportional to n, and another in time proportional to n , then for large n, the rst
algorithm will be better. Usually, we
are about the time taken only when the problem size,
n in this
ase, is large. So our qualitative analysis, whether the time is proportional to n or
n is quite useful.
To analyze the time for SelSort we must rst analyze the time for argmax. The fun
tion
argmax simply goes over the subarray on whi
h it is
alled and nds the maximum. It
examines every element and thus its time
an be
onsidered to be proportional to L, the
se
ond argument. In sele
tion sort, we merely
all argmax several times, with the value of
the se
ond argument being n; n 1; n 2 and so on till 2. Thus we
an see that the time is
proportional to
i n
X
i n =2
n + (n 1) + (n 2) + : : : + 2 =
2
i=2
Thus we estimate the time taken by Sele
tion sort as being proportional to n , where n is
the length of the array being sorted.
2
A program will deal with real life obje
ts su
h as stars, or roads, or a
olle
tion of
ir
les.
It might also deal with mathemati
al obje
ts su
h as polynomials. How to represent polynomials on a
omputer andPiperform
operations on them are therefore important questions.
n
i
A polynomial A(x) = i aix is
ompletely determined if we spe
ify the
oe
ients
a ; : : : ; an . Thus to represent the above polynomial we will need to store these
oe
ients.
This most
onveniently done in an array. We use an array a of length n and store ai in
a[i.
Next
omes the question of how we operate on polynomials. It is natural to ask; suppose
we have two arrays representing two polynomials A(x); B (x). Can we
onstru
t the representation of the polynomials C (x); D(x) obtained by adding and multiplying A(x); B (x)
respe
tively?
First we know that the sum will have degree n 1 be
ause the addends A(x); B (x) have
degree n 1. Thus the array
that we
an use to represent the polynomial C (x) must
be dened as float
[n. We also know that
i = ai + bi. Thus we know how to set the
elements of the array
as well.
=
=0
[i = a[i + b[i;
Can we write this as a fun
tion addp whi
h adds two polynomials? The polynomials to be
added will be passed as arguments. What about the result polynomial? We
ould allo
ate a
new array inside the fun
tion addp, but this array
annot be returned ba
k { it gets destroyed
There is a simple rule here { if a
olle
tion of obje
ts is des
ribed using one subs
ript, use a one
dimensional array, whi
h is what we have studied so far. If a
olle
tion of mathemati
al obje
t is des
ribed
using two subs
ripts, say the entries of a matrix, then we will need two dimensional arrays, whi
h we will
see later.
4
236
as soon as addp nishes exe
ution. The
orre
t way to write this pro
edure is to pass the
result array as well. Here is a program whi
h in
ludes the fun
tion addp.
void addp(float a[, float b[, float
[, int n){
// addends, result, length of the arrays.
for(int i=0; i<n; i++)
[i = a[i + b[i;
}
We have assumed in the above program that the addend polynomials have the same degree.
This need not be the
ase in general. But you should be able to modify the
ode to handle
the general
ase.
We next
onsider the problem of
omputing the produ
t polynomial D(x) of our polynomials A(x); B (x). As before, we will assume that both A(x); B (x) have degree n 1, and are
stored in arrays a, b of length n. The produ
t will have degree 2n 2, and must therefore
be stored in an array d of length at least 2n 1.
To determine D(x),
onsider how its
oe
ients relate to those of A(x); B (x). When
A(x) and B (x) are multiplied, ea
h term aj xj in the former will be multiplied with bk xk
in the latter, produ
ing terms aj bk xj k . Thus, this will
ontribute aj bk to dj k . Thus we
start by setting every
oe
ient of D to 0, and then for all j; k
ompute aj bk and add it the
oe
ient dj k . This gives us the fun
tion.
+
To
omplete the example, here is a main program whi
h
alls these fun
tions.
int main(){
float a[5, b[5,
[5, d[9;
for(int i=0; i<5; i++)
in >> a[i >> b[i;
addp(a,b,
,5);
prodp(a,b,d,5);
for(int
out <<
for(int
out <<
In the examples given above, we have expli
itly written out numbers su
h as 500,1000 to
spe
ify the array length. Arrays will often be used in programs for storing a
olle
tion of
237
values, and the total number of values in the
olle
tion will not be known to the programmer.
So you might
onsider it more
onvenient if we are allowed to write:
int n;
in >> n;
int a[n;
This
ode is not allowed by the C++ standard. The C++ standard requires that the length
be spe
ied by an expression whose value is a
ompile time
onstant. A
ompile time
onstant
is either an expli
itly stated number; or it is an expression only involving variables whi
h
are dened to be
onst, e.g.
onst int n = 1000;
The prex
onst is used to say that n looks like a variable, and it
an be used in all pla
es
that a variable
an be used, but really its value
annot be
hanged. So using a
onst name,
arrays might be dened as follows.
onst int NMAX = 1000; //
onvention to
apitalize
onstant names.
int a[NMAX, b[NMAX;
So how do we use this in pra
ti
e? Suppose we want to dene an array whi
h will store the
marks of students. In this
ase, the C++ standard will require us to guess the maximum
number of students we are likely to ever have, dene an array of that size, and only use a
part of it. So we might write:
onst int NMAX = 1000;
int a[NMAX, b[NMAX, na
tual;
in >> na
tual;
assert(na
tual <= NMAX);
In the rest of the
ode, we remember that only the rst na
tual lo
ations of a and b are used,
and so write loops keeping this in mind. Note that it is possible that the user will type in a
value for na
tual that is larger than NMAX. In this
ase we
annot run the program. If this
happens, the assert statement will
ause the program to stop, and you will need to
hange
NMAX, re
ompile and rerun.
13.7.1 Why
onst de
larations?
The above
ode
ould also dire
tly dene int a[1000,b[1000; instead of using the
onst
denition. However, the
ode as given is preferable if we ever have to
hange the required
size, say we want arrays of size 2000 rather than 1000. If we had not used NMAX we would
have to
hange several o
urren
es of 1000 to 2000; with the
ode as given, we just need to
hange the rst line to
onst int NMAX = 2000;
238
Arrays provide an easy way to store sets of obje
ts of the same type.
It is worth thinking about how the index of an element gets used. Sometimes the index
at whi
h an element is stored has no signi
an
e, as in the
ir
le interse
tion problem. Or
sometimes we
an make a part of the data be the index, as we did for the roll number in the
marks display problem. Similar was the
ase for the histogram problem. In the taxi dispat
h
problem, we used the index to impli
itly re
ord the arrival order of the taxis.
Suppose we want to look for elements satisfying a
ertain property. One way to do so is
to s
an through the array, one element at a time, and
he
k if the element has the required
property. We did this in the problem of printing roll numbers of students who had the
highest marks. This is a
ommon idiom.
The idea of s
anning through the array starting at index 0 and going on to the largest
index is also useful when we want to perform the same operation on every element, e.g. print
it. We used a somewhat
ompli
ated version of this in the
ir
le interse
tion problem, where
we wanted to perform a
ertain a
tion not for ea
h
ir
le, but for ea
h pair of
ir
les.
Finally, in the taxi dispat
h problem we built a so
alled queue so that the elements left
the array in the same order that they arrived in. For this we maintained two indi
es: where
the next element will be stored and whi
h element will leave next. This is a very
ommon
idiom, and you will see it, for example, in Exer
ise 14.
Finally, remember that the index used for an array should be in range, i.e. between 0
(in
lusive) and the array length (ex
lusive). Having the index out of range is a
ommon
ause of errors in programs involving arrays. So you should make sure that the index is in
the range. You
ould also
onsider
he
king this using assertions. For example if you have
an array x of length 200, whi
h you are about to index using an index i, you
ould
onsider
pla
ing an assertion:
assert((i >= 0) && (i < 200));
1. Suppose the roll numbers in the
lass do not go from 1 to the maximum number of
students, but are essentially arbitrary numbers (be
ause perhaps they identify the year
in whi
h the student enters, or the program that the student belongs to, and so on).
239
2.
3.
4.
5.
6.
Write the marks display program for this
ase. Assume that for ea
h student rst the
roll number is typed in, and then the marks. Also assume that at the beginning the
number of students is given.
Write the program to display who got the maximum marks for the
ase above, i.e.
when the roll numbers are arbitrary integers.
Suppose we want to nd a histogram for whi
h the width of the intervals for whi
h we
want the
ounts are not uniform. Say ea
h value is a real number between 0 (in
lusive)
and 1 (ex
lusive). Between 0 and 0.25, our intervals are of width 0.05, i.e. we want
a
ount of how many values are between 0 and 0.05, then 0.05 and 0.1, and so on.
Between 0.25 and 0.75 our intervals are of width 0.025, i.e. we want to know how
many values are between 0.25 and 0.275, then 0.275 and 0.3, and so on. Finally,
between 0.75 and 1, our intervals are of width 0.05. Write a program that provides the
histogram for these ranges.
You are to write a program whi
h takes as input a sequen
e of positive integers. You
are not given the length of the sequen
e before hand, but after all the numbers are
given, a -1 is given, so you know the sequen
e has terminated. You are required to
print the 10 largest numbers in the sequen
e. Hint: use an array of length 10 to keep
tra
k of the numbers that are
andidates for being the top 10.
Suppose in the previous problem you are asked to report whi
h are the 10 highest
values in the sequen
e, and how frequently they appear. Write a program whi
h does
this.
Suppose we are given the x; y
oordinates of n points in the plane. We wish to know
if any 3 of them are
ollinear. Write a program whi
h determines this. Make sure
that you
onsider every possible 3 points to test this, and that you test every triple
only on
e. The
oordinates should be represented as floats. When you
al
ulate
slopes of line segments, be
ause of the
oating point format, there will be round-o
errors. So instead of asking whether two slopes are equal, using the operator ==, you
should
he
k if they are approximately equal, i.e. whether their absolute dieren
e is
small, say 10 . This is a pre
aution you need to take when
omparing
oating point
numbers. In fa
t, you should also ask yourself whether the slope is a good measure to
he
k
ollinearity, or whether you should instead
onsider the angle, i.e. the ar
tangent
of the slope.
Write a program whi
h takes as input two ve
tors (as dened in mathemati
s/physi
s)
{ represent them using arrays { and prints their dot produ
t. Make this into a fun
tion.
Suppose you are given the number n of students in a
lass, and their marks on two
subje
ts. Your goal is to
al
ulate the
orrelation. Let xi ; yi denote the marks in the
two subje
ts. Then the
orrelation is dened as:
5
7.
8.
p P
x2i
xi yi
xi yi
p P
P
2
( xi ) n yi2
(P yi)
240
Write a program that al ulates this. Note that a positive orrelation indi ates that
x in
reases with y (roughly) { whereas negative
orrelation indi
ates that x in
reases
roughly as y de
reases. A
orrelation around 0 will indi
ate in this
ase (and often in
9.
general) that the two variables are independent. You may use the dot produ
t fun
tion
you wrote for the previous exer
ise.
Suppose you are given the maximum temperature registered in Mumbai on Mar
h 21
of ea
h year for the last 100 years. You would like to know whether Mumbai has been
getting warmer over the years, as is generally believed. You would like to know from
your data whether this might be a reasonable
on
lusion. If you merely plot the data,
you will see that the temperatures
u
tuate apparently errati
ally from year to year.
The weather is expe
ted to behave somewhat randomly; what you want to know is
whether there is any upward trend if you
an somehow throw out the randomness.
One way to redu
e the randomness is to smooth the data by taking so
alled moving
averages. Given a sequen
e of numbers x ; : : : ; xn , a 2k +1-window size moving average
is a sequen
e of numbers yk ; : : : ; yn k, where yi is the average of xi k ; : : : ; xi k . Write
a program whi
h takes a sequen
e and the integer k as input, and prints out the 2k +1
window-size moving average. Also plot the original sequen
e and the moving average.
A sequen
e x ; : : : ; xn is said to be a palindrome if xi = xn i for all i. Write
a program whi
h takes a sequen
e whose length is given rst and says whether the
sequen
e is a palindrome.
The Eratosthenes' Sieve for determining whether a number n is prime is as follows. We
rst write down the numbers 2; : : : ; n on paper. We then start with the rst un
rossed
number, and
ross out all its proper multiples. Then we look for the next un
rossed
number and
ross out all its proper multiples and so on. If n is not
rossed out in
this pro
ess, then it must be a prime. Write a program based on this idea. Earlier in
the
ourse we had a primality testing algorithm whi
h
he
ked whether some number
between 2 and n 1 divided n.
Suppose we are given an array marks where marks[i gives the marks of student with
roll number i. We are required to print out the marks in non-in
reasing order, along
with the roll number of the student who obtained the marks. Modify the sorting
algorithm developed in the
hapter to do this. Hint: Use an additional array rollNo
su
h that rollNo[i equals i initially. As you ex
hange marks during the
ourse of
the sele
tion sort algorithm, move the roll number along with the marks.
Suppose you are given a sequen
e of numbers, pre
eded by the length of the sequen
e.
You are required to sort them. In this exer
ise you will do this using the so
alled
Insertion sort algorithm. The idea of the algorithm is to read the numbers into an
array, but keep the array sorted as you read. In other words, after you read the rst i
numbers, you must make sure that they appear in the rst i elements of the array in
sorted (say non-in
reasing) order. So when you read the i +1th number, you must nd
where it should be inserted. Suppose you dis
over that it needs to be pla
ed between
1
+1
10.
11.
12.
13.
241
14.
15.
16.
the numbers that are
urrently at the j th and j + 1th position, then you should move
the numbers in positions j + 1 through i 1 (note that the indi
es or positions start
at 0) forward in the array by 1 step. Then the newly read number
an be pla
ed in
the j + 1th position. Write the program that does this.
Suppose you are given two arrays A,B of lengths m,n. Suppose further that the arrays
are sorted in non-de
reasing order. You are supposed to ll an array C of length m+n so
that it
ontains pre
isely the same numbers whi
h are present in A,B, but they must
appear in non-de
reasing order in C. In other words, if A
ontains the sequen
e 1,2,3,3,5
and B
ontains 2,4,6,7, then C should
ontain 1,2,2,3,3,4,5,6,7. Hint: Clearly, elements
must move out of A and B into C. Can you argue that for the purpose of this movement
all arrays behave like queues, i.e. elements always move out from the front of A,B, and
move into the ba
k of C?
A friend (\the magi
ian") shows you a de
k of
ards. He pi
ks up the top
ard, turns
it fa
e up, and it is seen to be the a
e. He puts the
ard away. He then takes the next
ard and puts it at the bottom of the de
k without showing it to you. Then he shows
you the
ard now at the top of the de
k, whi
h turns out to be the 2. He repeats the
following pro
ess until the de
k has no
ards. It turns out (magi
ally!) that you see
the
ards in in
reasing fa
e value, i.e. the rst
ard to be exposed is the a
e, then the
2, then the 3, then the 4, and so on until the King. Of
ourse, the \magi
" is all in the
order in whi
h the
ards were pla
ed in the de
k at the beginning. Write a program
that explains the magi
, i.e. gures out the initial order of the
ards and prints it.
Hint: Reverse the pro
ess.
Write a fun
tion whi
h given polynomials P (x); Q(x) returns their
omposition R(x) =
P (Q(x)). Say P (x) = x + 3x + 5 and Q(x) = 3x + 5x + 9. Then R(x) = (3x + 5x +
9) + 3(3x + 5x + 9) + 5.
Write the binary sear
h
ode without re
ursion.
Consider a long railway tra
k divided into some n parts of possibly unequal lengths.
For ea
h ith part, you are given its length Li and a maximum speed si with whi
h
trains
an run on it. You are also given the data for a
ertain lo
omotive: its maximum
speed s and the maximum a
ereration a it is
apable of (assume this is independent
of the speed, for simpli
ity), the maximum de
eleration d (again independent of the
speed) it is
apable of. Suppose the train starts at rest at one end of the tra
k and
must
ome to rest at the other end. How qui
kly
an the train
omplete this journey?
Make sure your
ode works for all possible values of the parameters.
A permutation is simply an ordering of the set of obje
ts, say the numbers between 0
and n 1 for some n. It is often desirable to generate all possible permutations for a
given n. For example, for n = 3, we would like to generate a sequen
e su
h as
012
021
102
2
17.
18.
19.
242
120
201
210
The above sequen
e is isn lexi
ographi
order, i.e. if you
onsider ea
h element to be
a digit in radix n, and
on
atenate the digits
on
atenate the sequen
e and
onsider
Let p be a permutation of integers from 0 to n 1 for some n. Dene V (p) to be the
integer obtained by
on
atenating the sequen
e p. We will say that a permutation p
is lexi
ographi
ally smaller than another permutation q if V (p) < V (q). Modify it to
do so.
20. Write a program that takes a permutation p of integers from 0 to n 1 and returns
the lexi
ographi
ally next permutation. Hint: try out a few permutations to dedu
e
the relationship between a permutation and the lexi
ographi
ally next permutation.
21. Write a program that takes in two numbers with 100 digits ea
h, and prints out their
produ
t. Adapt the polynomial multipli
ation algorithm dis
ussed in the text.
Chapter 14
More on arrays
We begin by
onsidering the problem of representing textual data. In
hapter 3 we dis
ussed
the
har datatype for storing
hara
ters. However, we rarely work with single
hara
ters.
More often, we will need to manipulate full words, or strings/sequen
es of
hara
ters. A
hara
ter string is
ustomarily represented in C as an array of
hara
ters. This representation
is not quite re
ommended in C++, as we will see in Se
tion 20.1. But it is worth knowing this
representation be
ause you will en
ounter it be
ause of lega
y reasons (e.g. Se
tion 14.3.1)
and be
ause the C++ re
ommended representation builds upon this.
Next, we dis
uss multidimensional arrays. An ordinary (one dimensional) array
an
be thought of as a sequen
e of values. A two dimensional array
an be thought of as a
matrix or a table (rows and
olumns) of values. Two dimensional arrays are very useful,
espe
ially in s
ienti
omputation. We will dis
uss an important use of two dimensional
arrays: representing and solving linear systems of equations. C++ allows us to build our
own representations for two dimensional arrays whi
h have all features we dis
uss in this
hapter, and some additional ones. This is dis
ussed in Se
tion 20.2.6.
So far we have been exe
uting C++ programs by writing a.out or ./a.out. However,
it is possible to supply input to the program on the
ommand line itself, e.g. by writing a.out input-text. This requires the use multidimensional arrays, and is dis
ussed in
Se
tion 14.3.1.
Finally, we dis
uss the use of re
ursion in problems involving arrays. Suppose a problem
is given to you involving data stored in an array of length n. You
an solve it using re
ursion
if the following hold.
1. If n is small, say n = 1, then you have a way of solving the problem.
2. If n is not small, say n > 1, you have a way to
onstru
t problem(s) of the same kind,
on smaller arrays su
h that from the solution to the smaller problem(s) you will be
able to
onstru
t a solution to the original problem.
If you
an do these steps, then you have a re
ursive algorithm. Often, su
h re
ursive algorithms are very simple to state and
ode, and also fast. We will see two su
h algorithms, one
for the so
alled Binary sear
h algorithm, and another for Merge sort, whi
h is an algorithm
for sorting.
243
244
The above denes two arrays, name and residen
e of lengths 20 and 50, ostensibly for
storing the name and the residen
e. Sin
e we will usually not know the exa
t number of
hara
ters in a name or in an address, it is
ustomary to dene arrays of what we guess
might be the largest possible length. This might seem wasteful, and it is, and we will see
better alternatives in later
hapters.
So if we want to store a
hara
ter string \Shivaji" in the array, we will be storing 'S' in
name[0, 'h' in name[1 and so on. The string is 7
hara
ters long, and you would think that
we should store this length somewhere. While printing the string for example, we
learly do
not want the name[7 through name[19 printed. The
onvention used in the C language,
and inherited into C++ from there, is that instead of storing the length expli
itly, we store
a spe
ial
hara
ter at the end of the a
tual string. The spe
ial
hara
ter used is the one
with ASCII value 0, and this
an be written as 'n0'. Note that 'n0' is not printable, and is
not expe
ted to be a part of any real text string. So it unambiguously marks the end of the
string.
Spe
ial
onstru
ts are provided for initializing
hara
ter arrays. So indeed we may write
har name[20 = "Shivaji";
har residen
e[50 = "Main Pala
e, Raigad";
The
hara
ter string \Shivaji" has 7
hara
ters. So these will be pla
ed in the rst
7 elements of name. The eighth element, name[7 will be set to 'n0'. Similarly only 20
elements of residen
e will be initialized, in
luding the last 'n0'. Note by the way that
apital and small letters have dierent
odes.
Here is an alternative form.
har name[ = "Shivaji";
har residen
e[ = "Main Pala
e, Raigad";
In this, C++ will
al
ulate the lengths of name and residen
e. Following the previous
dis
ussion, these will be set to 8 and 20 respe
tively.
We
an manipulate strings stored in
har arrays by going over the elements in a for loop,
for example.
14.1.1 Output
Printing out the
ontents of a
hara
ter array is simple. Assuming name is a
hara
ter array
as before,
out << name;
245
would
ause the
ontents of name from the beginning to the 'n0'
hara
ter to be printed on
the s
reen.
The general form of the above statement is:
out <<
harptr;
Here
harptr
ould be the name of a
har array, or more generally, an expression of type
pointer to
har. The statement will
ause a whitespa
e delimited string typed by the user
to be read into the memory starting at the address denoted by
harptr. After storing the
string the 'n0'
hara
ter will be stored. Here is an example.
har name[20;
out << "Please type your name: ";
in >> name;
The se
ond statement asks you to type your name and the third,
in >> name; reads in
what you type into the array name. The following points are worth noting:
1. From what you type, the initial whitespa
e
hara
ters will be ignored. The
hara
ter string starting with the rst non-whitespa
e
hara
ter and ending just before the
following whitespa
e
hara
ter will be taken and pla
ed in name. Thus if I type
Abhiram Ranade
with some leading whitespa
e, the leading whitespa
e will be dropped and only "Abhiram"
would go into name. Next, following "Abhiram" a null
hara
ter, i.e. 'n0' would be
stored. Thus the letters 'A' through 'm' would go into name[0 through name[6,
and name[7 would be set to 'n0'. Note that all this is very
onvenient in that the a
single statement reads in all the
hara
ters, and further the 'n0' is also automati
ally
pla
ed after the last
hara
ter read in.
2. This way of reading in text is not useful if the text
ontains spa
es. Thus in the above
example, \Ranade" would not be read in into name. For that it is ne
essary to use the
getline
ommand dis
ussed below.
3. This statement is potentially unsafe. In the above example if the user had typed in
more than 20
hara
ters without a whitespa
e in between, all those would be stored
starting at name[0. Thus the
hara
ters read in would be stored past the end of the
designated array name, possibly writing into memory that might have been allo
ated
for some other variable.
246
where x must be a name of a
har array, or more generally a pointer to
har, and n an
integer. This will
ause whatever the user types, in
luding whitespa
e
hara
ters, to be
pla
ed starting from the address x, until one of the following o
urs
A newline
hara
ter is typed by the user. In this
ase all
hara
ters upto the newline
are
opied into memory starting from the address x. The newline
hara
ter is not
opied. It is dis
arded.
n-1
hara
ters are typed without a newline. In this
ase all the
hara
ters are pla
ed
into memory starting from address x, followed by a 'n0'
hara
ter.
As you may guess, it is
ustomary to use the length of x as the argument n. So for example
we
an write:
1
har name[20;
in.getline(name,20);
In this
ase at most 19
hara
ters that the user types will be
opied, and we will have no
danger of over
owing past the array limit.
14.1.3 Chara
ter array pro
essing
Chara
ter arrays behave like ordinary integer arrays, ex
ept when it
omes to reading and
printing, and in that they
ontain a 'n0'
hara
ter whi
h marks the end of the useful portion
of the array. So pro
essing them is reasonably straight forward. Note that
hara
ters are
a subtype of integers, and as su
h we
an perform arithmeti
on
hara
ters, and
ompare
them, just as we do for integers.
Our rst example is a fun
tion for determining the length of the text stored in a
har
array.
int length(
har *txt){
//pre
ondition: txt points to sequen
e of '\0' terminated
hara
ters.
int L=0;
while(txt[L != '\0') L++;
return L;
}
The fun
tion takes a single argument, say the array name (or the pointer to the zeroth
element of the array). Noti
e that the a
tual length of the array is not needed. This is
be
ause we a
ess elements only till the null
hara
ter. Indeed, the fun
tion simply steps
through the elements of the array, and returns the index at whi
h it nds the null
hara
ter,
'n0'. Sin
e the starting index is 0, the null
hara
ter will be at index equal to the length of
the text string.
1
On most modern keyboards this happens when you press the key labelled ENTER.
247
Our se
ond example is a fun
tion for
opying a string stored in an array sour
e to another
array destination. This is like
opying other arrays, ex
ept that we must only worry about
the useful portion of the sour
e array, i.e. till the o
urren
e of the 'n0'
hara
ter. The
fun
tion does not worry at all about the lengths of the 2 arrays as dened, it is assumed
that the
all has been made ensuring that indi
es will not ex
eed the array bounds.
void s
opy(
har destination[,
har sour
e[)
// pre
ondition: '\0' must o
ur in sour
e. destination must be long
// enough to hold the entire sour
e string + '\0'.
{
int i;
for(i=0; sour
e[i != '\0'; i++)
destination[i=sour
e[i;
destination[i=sour
e[i;
//
opy the '\0' itself
}
As an example of using this, note that a string
onstant
an be used any pla
e a pointer to
har is needed. Thus we
an write s
opy(name,"Einstein") whi
h would simply set the
name to \Einstein".
Here is a more interesting fun
tion: it takes two strings and returns whi
h one is lexi
ographi
ally smaller, i.e. would appear rst in the di
tionary. The fun
tion simply
ompares
orresponding
hara
ters of the two strings, starting at the 0th. If the end of the strings is
rea
hed without nding unequal
hara
ters, then it means that the two strings are identi
al,
in whi
h
ase we must return '='. If at some
omparison we nd the
hara
ter in one string
to be smaller than the other, that string is de
lared smaller. If one string ends earlier, while
the pre
eding
hara
ters are the same, then the string that ends is smaller.
This logi
is implemented in the
ode below. We maintain the loop invariant:
hara
ters
0 through i-1 of both arrays must be non null and identi
al. So if we nd both a[i and
b[i to be null,
learly the strings are identi
al and hen
e we return 0. If a[i is null but not
b[i, then a is a prex of b. Be
ause prexes appear before longer strings in the di
tionary,
we return '<'. We pro
eed similarly if b[i is null but not a[i. If a[i>b[i we return
'>', if a[i<b[i we return '<'. If none of these
onditions apply, then the ith
hara
ter
in both strings must be non-null and identi
al. So the invariant for the next iteration is
satised. So we in
rement i and go to the next iteration.
har
ompare(
har a[,
har b[)
// returns '<' if a is smaller, '=' if equal, '>' if b is smaller.
{
int i = 0;
while(true){
// Invariant: a[0..i-1 == b[0..i-1
if(a[i == '\0' && b[i == '\0') return '=';
if(a[i == '\0') return '<';
if(b[i == '\0') return '>';
if(a[i<b[i) return '<';
if(a[i>b[i) return '>';
i++;
248
If you exe
ute this program, it would expe
t you to type two lines. Say you typed:
Mathemati
s
Biology
then it would print out > and stop, be
ause \Mathemati
s" appears after \Biology" in the
di
tionary order.
14.1.4 Address arithmeti
C++ programs pro
essing
har arrays often use arithmeti
on addresses.
Suppose x is the name of an array of some type T. We have already said that x has value
equal to the address of the zeroth element of the array. Suppose further that i is an integer
expression. Then the expression
x+i
is valid in C++ programs, and has the value equal to the address of x[i. Note that in
general, a single element of type T may require some s bytes. Thus while x+i seems to be
adding i to the address x, the a
tual value added is i*s be
ause the address of x[0 and
of x[i dier by i*s and not just i. Indeed, in general, if x is of type T*, then x+i is the
address obtained by adding i*s to the address denoted by x. Sin
e this
an be somewhat
onfusing, we have not dis
ussed su
h address arithmeti
so far.
However, if x is of type
har*, then s equals 1. In that
ase, when we write x+i, we indeed
mean the address obtained by adding i. So perhaps for this reason, address arithmeti
is
quite
ommon in
hara
ter pro
essing. Thus the s
opy fun
tion would more
ommonly be
written as:
void s
opy(
har *destination,
har *sour
e){
while(*sour
e != '\0'){
*destination = *sour
e;
destination++;
sour
e++;
}
249
Sequen
es of numbers are naturally represented as arrays. However, we will run into obje
ts
like matri
es whi
h are
olle
tions of elements des
ribed using two indi
es. For su
h
ases,
C++ provides two dimensional arrays.
Here is an example of how a two dimensional array might be dened:
double a[m[n;
This
auses spa
e for m*n variables of type double to be allo
ated. These variables are
a
essed as a[i[j where we require 0 i < m, and 0 j < n. The variables are stored
in the so
alled row major order in memory, i.e. in the order a[0[0, a[0[1, ...
a[0[n-1, a[1[0, ... a[1[n-1, ... a[m-1[n-1. The numbers m,n are said
to be the rst and se
ond dimension of the array. We will also refer to them as the number
of rows and the number of
olumns respe
tively.
Manipulating two dimensional arrays is similar to one dimensional { we
ommonly use
a loop to go over ea
h dimension. As an example,
onsider the problem of multiplying two
matri
es. Remember that if A is an m n matrix, and B an n p matrix, then there produ
t
is an m p matrix C where
n
X
ij =
aik bkj
k =1
where we have let the array indi
es start at 1, as is
ustomary in Mathemati
s. The
ode
below, of
ourse, starts indi
es at 0. The
ode also shows how a two dimensional array
an
be initialized in the denition itself if you wish. The values for ea
h row must appear in
bra
es, and these in turn in an outer pair of bra
es.
double a[3[2={{1,2},{3,4},{5,6}}, b[2[4={{1,2,3,4},{5,6,7,8}},
[3[4;
for(int i=0; i<3; i++)
for(int j=0; j<4; j++){
[i[j = 0;
for(int k=0; k<2; k++)
[i[j += a[i[k*b[k[j;
}
// ompute = a * b.
250
1
2
3
1
2
3
251
1
2
3
21
31
1
2
3
Here the rst string, "India" is deemed to initialize the zeroth row, and so on for the six
strings.
Applying only one index to the name of a two dimensional array returns the address
of the zeroth element of the
orresponding row. For
hara
ter arrays, this is the way to
refer to one of the strings stored. Thus
ountries[i will return the address of the zeroth
hara
ter of the ith string stored in the array, in other words, the address of the ith string.
So if we write
ompare(
ountries[0,
ountries[1), where
ompare is as dened in
Se
tion 14.1.3, it would return '<' as the result be
ause India will pre
ede Sri Lanka in the
di
tionary order.
Here is a program whi
h has two arrays,
ountries whi
h lists
ountries, and
apitals
whi
h lists
orresponding
apitals. It takes as input a string from the keyboard. It prints
out the name of the
orresponding
apital if the string is in the list of
ountries stored in
ountries. This
he
k is made using our
ompare fun
tion.
252
int main(){
onst int wordLength = 20;
har
ountries[6[wordLength = {"India","China","Sri Lanka","Nepal",
"Bangladesh","Pakistan"};
har
apitals[6[wordLength = {"New Delhi","Beijing","Colombo","Kathmandu",
"Dhaka","Islamabad"};
har
ountry[wordLength;
out << "Country: ";
in.getline(
ountry,wordLength);
int i;
for(i=0; i<6; i++){
if(
ompare(
ountry,
ountries[i) == '='){
out <<
apitals[i << endl;
break;
}
}
if(i == 6)
out << "Dont know the
apital.\n";
When the loop terminates, we know that i must be stri
tly less than 6 if the
ountry was
found in
ountries, and equal to 6 if not found. Hen
e we print the message that we dont
know the
apital only if i is 6 at the end.
14.2.3 Passing 2 dimensional arrays to fun
tions
It is possible to pass a two dimensional array to a fun
tion. However, in the
alled fun
tion,
the se
ond dimension of the array parameter must be given as a
ompile time
onstant. Thus
we might write:
void print(
har
ountries[[20, int noOfCountries){
for(int i=0; i<noOfCountries; i++)
out <<
ountries[i << endl;
}
This may be
alled as print(
ountries,6), where the se
ond argument is the rst dimension of the
ountries array. It will print out the
ountries on separate lines.
This is not too useful, be
ause any su
h fun
tion
an only be used for arrays in whi
h the
se
ond dimension is 20. For example, this makes it impossible to write a general matrix multipli
ation fun
tion for matri
es of arbitrary sizes. This is a fundamental problem
on
erning
two dimensional arrays in the language C, whi
h has been inherited into the language C++.
In Se
tion 20.2.6 we will see how it
an be over
ome quite elegantly using the
exible nature
of C++.
But if we do know the se
ond dimension, then the standard two dimensional arrays are
useful. Here is how they
an be used in drawing polygons in simple
pp graphi
s.
253
This will
reate a polygon named pName. The parameters
x,
y give the rotation
enter of
the polygon. The parameter n is an integer giving the number of verti
es, and Verti
es is
a two dimensional double array with n rows and 2
olumns, where ea
h row gives the x,y
oordinates of the verti
es, relative to the
enter (
x,
y). A polygon is a shape in the sense
of Chapter 5, so we may use all the
ommands for shapes on polygons.
The boundary of the polygon is tra
ed starting at vertex 0, then going to vertex 1 and
so on till vertex n-1 and then ba
k to vertex 0. Note that the boundary may interse
t itself.
Here is an example. We
reate a regular pentagon and a pentagonal star. Then we rotate
them.
int main(){
initCanvas("Pentagon and Star");
double pentaV[5[2, starV[5[2;
for(int i=0; i<5; i++){
pentaV[i[0 = 100 *
os(2*PI/5*i);
pentaV[i[1 = 100 * sin(2*PI/5*i);
starV[i[0 = 100 *
os(4*PI/5*i);
starV[i[1 = 100 * sin(4*PI/5*i);
}
Polygon penta(200,200,pentaV,5);
Polygon star(200,400,starV,5);
for(int i=0; i<100; i++){
penta.left(5);
star.right(5);
wait(0.1);
}
}
getCli k();
Note that there is a more natural ways of spe
ifying the star shape:
onsider it to be a
(
on
ave) polygon of 10 verti
es. Thus we
ould have given the
oordinates of the 10 verti
es
in order. Cal
ulating the
oordinates of the \inner" verti
es is a bit messy, though.
An array is really a sequen
e in memory of variables of the same type. We have seen arrays
of int, double,
har, but we
an have arrays of any type of variable. So you might ask,
an
we have arrays of pointers? It is
ertainly possible, and it turns out to be useful too.
254
You
an read this statement as saying \x[i is an int for i=0 to i=9." In a similar manner,
you should read the statement int *y[10; as saying \*y[i is an int for i=0 to i=9."
But if
ontent of y[i is an int, then y[i must be an int pointer.
On
e you have dened an array of pointers, you
an store addresses of appropriate variables in ea
h element of the array. For example, you might write something like:
int *y[10;
int z = 100;
y[0 = &z;
out << *y[0 << endl;
This will print 100, be
ause y[0
ontains the address of z, and hen
e *y[0 just means z,
and hen
e the value of z, 100, will be printed.
We next dis
uss an important use of arrays of pointers.
14.3.1 Command line arguments to main
So far, we have exe
uted C++ programs by spe
ifying the name of the exe
utable le,
usually a.out, on the
ommand line. Spe
i
ally, the program is exe
uted by typing a.out
or ./a.out on the shell
ommand line. This
auses the main fun
tion in your program to
be
alled. But you may exe
ute your program dierently. C++ does allow you to provide
additional text after a.out, and this text
an be pro
essed by your program. For example,
you may write:
./a.out Mathemati
s Biology
In this
ase your program
an be told that you have typed the words Mathemati
s and
after a.out. This
an be done using an alternative (overloaded) de
laration provided for main.
Biology
Thus main may take two arguments. The rst is an integer argument arg
. The se
ond
argument is an array (sin
e it ends in [) has name argv, and ea
h element is of type
har*.
In other words, argv is an array of pointers to
har.
Suppose you use this form of main. Then when you exe
ute your program, the Operating
System
alls the fun
tion main, but also passes some parameters. Spe
i
ally the following
are the values passed in the parameters:
255
1. The parameter arg
gives the number of words typed on the
ommand line, in
luding
the name of the exe
utable program (a.out or other). Note that by \word" we simply
mean white spa
e delimited sequen
e of
hara
ters.
2. The parameter argv is an array of arg
elements, with the ith element argv[i
being the address of the ith
ommand line word (typi
ally
alled ith
ommand line
argument).
Thus if you had invoked the main program by writing ./a.out Mathemati
s Biology the
value of arg
would be 3. The parameter argv would have 3 elements of type
har*,
and these would respe
tively be addresses of the text strings (null terminated) "./a.out",
"Mathemati
s", and "Biology" respe
tively.
Here is a simple program that just prints out the values of all its
ommand line arguments.
int main(int arg
,
har *argv[){
for(int i=0; i<arg
; i++)
out << argv[i << endl;
}
Mathemati s Biology
a.out
Mathemati
s
Biology
Of
ourse, you
an do more interesting pro
essing of the
ommand line arguments. See
Appendix F.
We often sort data be
ause it looks ni
e to print it that way. However, there is another
important motivation. Certain operations
an be performed very fast if the data is sorted.
Suppose we have an array in whi
h we have stored numbers. Suppose we are subsequently
given a number x and we are to determine if x is is present in the array. The natural strategy
is to go over ea
h element in the array and
he
k if it equals x. In the worst
ase we might
have to examine every array element.
We
an adopt a
leverer strategy if the array is sorted. Say our array is A and it
ontains
size elements. Say A is sorted in non-de
reasing order, i.e. A
an
ontain elements with the
same value, but they will o
ur at
onse
utive indi
es.
The basi
idea is: instead of examining elements from the beginning of the array, in the
rst step we examine the element that is roughly in the middle of the array. Thus in the
rst step we
he
k if x < A[size/2. Here we mean integer division when we write size/2,
i.e. the value of size/2 rounded down. There are 2
ases to
onsider.
The
he
k su
eeds i.e. x is smaller than A[size/2. Now be
ause the array is sorted,
we know that all elements in the subarray A[size/2+1..size-1 will also be larger
than x. Hen
e x, if present in the array, will be in the portion A[0..size/2-1. Thus
using just 1
omparison, we have narrowed our sear
h to the rst half of the array.
256
The he k fails
There is an extra parameter, start whi
h says where the subarray starts. So we are sear
hing
in the region A[start...start+size-1. The \middle" element now is A[start+size/2
whi
h is the same as A[start+half in the
ode. The \rst half" starts at A[start and
has size equal to half. The \se
ond half" starts at A[start+half and has size size half. Thus we have the re
ursive
alls in the fun
tion.
Our
ode might look \obviously
orre
t", but this is de
eptive. Folklore has it that even
experien
ed programmers make mistakes while writing binary sear
h. So it is a good idea
to
he
k that our fun
tion indeed works
orre
tly.
There are two aspe
ts to working
orre
tly: the fun
tion must terminate, and on termination return the
orre
t answer. We rst
he
k that the fun
tion will indeed terminate.
Clearly, when size be
omes 1, the fun
tion will return. But note that if size > 1, the value
half = size/2 (integer division) is stri
tly between 0 and size. Thus we
an
on
lude that
half as well as size - half are both smaller than size. Hen
e we have established that the
se
ond parameter to Bsear
h always redu
es, and hen
e must eventually be
ome 1, where
upon the fun
tion will return. That the
orre
t value is returned follows in the manner we
have argued above.
Here is a main program whi
h tests our fun
tion.
int main(){
onst int size=10;
int A[size={-1, 2, 2, 3, 10, 15, 15, 25, 28, 30};
for(int i=0; i<size; i++)
out << A[i << " ";
out << endl;
257
We sear
h the array for the presen
e of every integer between -10 and 40. You will see that
1 is returned only for those integers that are present.
Noti
e that the array is sorted, but
ontains repeated values.
14.4.1 Estimate of time taken
Let us analyze a bigger example. Suppose we are
he
king for the presen
e of a number in
an array of size 1024. How many array elements do we
ompare in the pro
ess?
The fun
tion binsear
h will rst be
alled with the size parameter equal to 1024. When
we re
urse, no matter how the
omparison
omes out, we will next
all binsear
h with size
512. Subsequently we
all binsear
h with size 256 and so on. Thus a total of 10
alls will
be made: in the last
all size will be
ome 1 and we will return the answer. In ea
h
all
we make only one
omparison x < A[start+half, and hen
e only 10
omparisons will be
made!
Compare this with the
ase in whi
h the array is not sorted: then we might have to make
as many as 1024
omparisons! Even if we agree that it takes a bit longer to
all a fun
tion,
alling binsear
h 10 times (in
luding the re
ursion) will be mu
h faster than having to
possibly
ompare x with ea
h of the 1024 elements. A
tually, our binary sear
h
an be
written out as a loop, without re
ursion, the exer
ises ask you to do this.
In general you
an see that for the re
ursive algorithm the number of
omparisons made
is simply the number of times you have to divide the size of the array so as to get the
number 1. This number is log n, if n denotes the size of the array. In other words, the time
is proportional to log n when we do Binary sear
h.
In
ontrast, if the array is not sorted, we are for
ed to do linear sear
h, in whi
h
ase the
we may need to
ompare x to all the elements in the array, i.e. there
ould be as many as n
omparisons.
Binary sear
h is a simple but important idea. You will see that it will appear in many
pla
es, perhaps slightly disguised, as it did in the Bise
tion algorithm (Se
tion 8.3) for nding
roots.
3
In Se
tion 13.5 we saw the sele
tion sort algorithm for sorting an array. In the worst
ase,
sele
tion sort will take time O(n ). In this se
tion we will see the Merge sort algorithm whi
h
will take time O(n log n). As you
an see, log n is mu
h smaller than n, and hen
e n log n
is mu
h smaller than n . Indeed if you
ode up the two algorithms you will see that Merge
sort runs mu
h faster.
2
If x is not present in the array, we will know that only after
omparing it with all the 1024 elements.
If x is present, we will stop after we nd it. So in this
ase, you
ould say that \on the average" we will
ompare x with half the elements, i.e. we will do 512
omparisons.
3
258
Merge sort is a re
ursive algorithm. If we want to sort the sequen
e S, we divide it into
two sequen
es U and V of roughly equal size. We sort U,V, and then
ombine the results to get
a single sorted sequen
e. This is our nal result, the result of sorting S. Su
h an algorithm is
also often
alled a divide-and-
onquer algorithm, be
ause we divide S into smaller sequen
es
whi
h we sort (
onquer!) separately.
The division of S into U,V is simple: we just put the rst half of S into U and the se
ond
half into V. The key question, of
ourse, is how to
ombine, or merge, the results of sorting
U and V. We will dis
uss this rst, and then dis
uss the entire algorithm.
14.5.1 A merging algorithm
Suppose we are given two rows of students, in ea
h of whi
h the students are arranged in
non-de
reasing order of their height. Can we put them into a single row su
h in whi
h the
students will still be arranged in non-de
reasing order? This problem is perhaps easier to
visualize, but as you
an see it is the same problem as that of merging two sorted sequen
es.
Here is a pro
edure to merge the two rows u; v of students into a single row s. The rst
student in s should be the shortest of all students. The shortest in u is the student at the
front of u, and the shortest in v the student at the front of v. Thus the shortest overall must
be the shorter of the students at the front of u and front of v. So we
an ask the shorter
of the two to leave his/her row, and join row s. Next, we have to nd the se
ond shortest
student. Sin
e the shortest student has moved to s already, the se
ond shortest must be the
shortest from those that remain. So we again pi
k the shorter of the students at the front of
the rows u; v, and send that student to the ba
k of row s. For the third shortest, we merely
repeat the pro
edure! Eventually, it might so happen that all the students in one of the rows
u; v have left for s. On
e this happens, we ask the students from the remaining row to join
s, in the order they are standing in their row.
The analogy to the sorted sequen
es U,V should be
lear. In fa
t, we will think of ea
h
of the sequen
es S,U,V as a queue, like the queue of drivers we had for the taxi dispat
h
problem (Se
tion 13.2.6). Drivers were joining that queue at the end, just as students/keys
will join S at the end. Drivers left from the front of the queue, and similarly in this
ase
students/keys will leave U,V from the front. Thus the algorithm
an be
oded up as shown
in Figure 14.1. The
omments in the
ode explain the algorithm fully. As you
an see, it
mat
hes the student row merging pro
edure dis
ussed above. Also, you should be able to
prove the
orre
tness of the invariant given.
The fun
tion Merge exe
utes uLength + vLength iterations, i.e. as many as the total
number of keys. In ea
h iteration a xed number of instru
tions is exe
uted. Hen
e we
an
say that the total time is at most some
onstant times the number of keys, i.e. proportional
to the total number of keys.
14.5.2 Mergesort algorithm
Given a merge algorithm, the mergesort algorithm is easy. For sorting a sequen
e S of length
n we pro
eed as follows.
1. Create two smaller arrays of roughly half the size. Say array U of size n/2, and array
V of size n - n/2.
void merge(int U[, int uLength, int V[, int vLength, int S[){
// arrays U,V of length uLength and vLength respe
tively
ontain the
// sequen
es that are sorted. The result of merging is to be pla
ed
// in the array S. The length of S is not spe
ified expli
itly, but
// it is assumed (pre
ondition) to be uLength + vLength.
for(int uFront=0, vFront=0, sBa
k=0; sBa
k<uLength+vLength; sBa
k++){
// INVARIANT: sBa
k = uFront + vFront. Keys U[0..uFront-1 will
// have been moved to S, and and also keys V[0..vFront-1. S will
//
ontain these keys in S[0..sBa
k-1, in non-de
reasing order.
259
260
mergesort(U, n/2);
mergesort(V, n - n/2);
merge(U, n/2, V, n-n/2, S);
Note that we wrote the number of elements to be
opied to U as n - n/2 and not n/2 in
order to a
ount for the possibility that n might be odd.
We will now estimate the time T (n) taken by mergesort to sort a sequen
e of length
n. Initially, we
opy the elements of S to U,V. As dis
ussed above this takes total time
proportional to at most n. After that we
all mergesort re
ursively. This takes time T (n=2)
and T (n n=2). Finally we
all merge. The time taken by merge, we said is at most
proportional to n the total number of keys. Thus we have
T (n) (Time proportional to n) + T (n=2) + T (n n=2)
where we have
lubbed together the time to
opy and time to merge as a single entry,
proportional to n. Suppose the
onstant of proportionality is
. Then we have
T (n)
n + 2T (n=2)
Here we have assumed for simpli
ity that n is a power of 2, and hen
e n n=2 = n=2.
Note that our inequality for T (n) is also valid if we substitute n=2 instead of n. Thus we
have T (n=2)
(n=2) + 2T (n=4), whi
h we
an substitute into itself! Thus we get
T (n)
n + 2T (n=2)
n + 2(
(n=2) + 2T (n=4) = 2
n + 4T (n=4)
But we
an
ontinue in this manner till for k steps, where n = 2k . Thus we will get
T (n) k
n + 2k T (n=2k )
Sin
e n=2k = 1 and k = log n, we have
T (n)
n log n + nT (1)
Noting that and T (1)
0 for some
0 , we get T (n)
n log n +
0n
00n log n for some
onstant
00 . Thus we have shown that T (n) is at most proportional to n log n.
2
261
1. Write a program that reads in an integer from the keyboard and prints it out in words.
For example, on reading in 368, the program should print \three hundred and sixty
eight".
2. For this exer
ise it is important to know that the
odes for the digits are
onse
utive,
starting at 0. Further '8' - '0' is valid expression and evaluates to the dieren
e in the
ode used to represent the
hara
ters, and is thus 8. To
larify, if we exe
ute
har text[10 = "1729";
int d = text[1 - '0';
Then d will have the value 7. Use this to write a fun
tion that takes a
har array
ontaining a number and return an integer of the
orresponding magnitude.
3. Suppose destination and sour
e are of type
har*. What do you think the following
statement does?
while(*destination++ = *sour
e++);
4.
5.
6.
7.
Note: it uses several programming idioms you have been warned not to use. The point
of this exer
ise is not to en
ourage the use of these idioms but to warn you how dense
C++
ode
an be.
Extend the marks display program of Se
tion 13.2.2 to use names rather than roll
numbers. At the beginning, the tea
her enters the number of students. Then the
program must prompt the tea
her to enter the name of the student, followed by the
marks. After all names and marks have been entered, the program then gets ready
to answer student queries. Students enter their name and the program prints out the
marks they have obtained.
Write a fun
tion whi
h takes a sequen
e of parentheses, open and
losed, of all types,
and says whether it is a valid parenthesization. Spe
i
ally, parentheses should be
in mat
hing pairs, with the opening parenthesis before the
losing, and if a pair of
parentheses
ontains one parenthesis from another pair, then it must also
ontain the
other parenthesis from that pair.
Write a \
al
ulator" program that takes 3
ommand line arguments in addition to the
name of the exe
utable: the rst and third being double values and the se
ond being
a single
har. The se
ond argument must be spe
ied as an arithmeti
operator, i.e.
+, -, * or /. The program must perform the required operation on the two numbers
and print the result.
Write a program that solves a system of n linear equations in n unknowns, based on
the dis
ussion of Se
tion 14.2.1. You should write
onst int n = 4;
262
8.
9.
10.
11.
in your program and then subsequently dene the arrays using n as dened above.
This way it should be possible to use your program to solve systems of dierent size
simply by
hanging the value of n.
Write a program that reads in a square matrix and prints its determinant. As above,
make the dimension of the matrix a
onst int. You should NOT use the re
ursive
denition of determinant, but instead use the following properties:
Adding a multiple of one row to another leaves the determinant un
hanged.
Ex
hanging a pair of rows
auses the determinant to be multiplied by -1.
The deteminant of an upper triangular matrix (all zeros below the diagonal) is
simply the produ
t of the elements on the diagonal.
If the rst element of the rst row is a zero, then ex
hange rows so that it be
omes non
zero. Then add suitable multiples of the rst row to the other rows so that the rst
olumn is all zeros ex
ept the rst row. Similarly produ
e zeros below the diagonal in
the se
ond
olumn, and so on.
Design an input instan
e for the mergesort algorithm su
h that every line of
ode in
the merge algorithm of Se
tion 14.5.1 will exe
ute in one of the
alls to merge.
Suppose you are given two sorted sequen
es S; T of lengths m; n. Write a program that
nds the median of their union. You may nd it easier to write a program that nds the
ith smallest in the union, for general i. Hint: Compare the medians of the sequen
es
S; T . What does the
omparison tell you about the position of the ith smallest?
A very popular and elegant algorithm for sorting is the so
alled Qui
ksort. If A is the
sequen
e to be sorted, this works as follows.
(a) Pi
k a random element r of A.
(b) Constru
t a sequen
e S
onsisting of all elements smaller than r.
(
) Constru
t a sequen
e L
onsisting of the remaining elements.
(d) Sort the sequn
es S; L (re
ursively!) to produ
e sequen
es S 0; L0.
(e) Return the
on
atenation of sequen
es S 0; L0.
Write the program for Qui
ksort. By and large, Qui
ksort works very fast. More
pre
isely, it is possible to show that the expe
ted time taken by Qui
ksort (expe
tation
al
ulated over all random
hoi
es of r in all
alls) is O(n log n). The proof of this is
outside the s
ope of this book.
An interesting tri
k is employed to make Qui
ksort run fast. If the original sequen
e A
is stored in the array A, then it is possible to ensure that steps 2,3 above will
onstru
t
S; L in A itself, with S pre
eding L. This will ensure that the sorting step will also
produ
e the result in-pla
e, i.e. S 0; L0 will be produ
ed in the same (sub)arrays as
were o
upied by S; L. Thus the last step,
on
atenation, does not have to be done
expli
itly. Here is how we
an
reate S; L inside A itself. Start s
anning from A[0
2
12.
263
towards higher indi
es. Stop when you nd a number A[i larger than or equal to r.
Now start s
anning ba
kwards from the end, A[n-1. Stop when you nd A[j smaller
than r. Ex
hange the elements A[i,A[j. Clearly, A[0..i and A[j..n-1
an be
onsidered parts of S,L. We
an extend these by repeating the pro
ess on the subarray
A[i+1..j-1.
Code up this idea. Write
lear invariants to guide your
ode. There is great potential
here for making silly mistakes!
13. You might have observed that most physi
al obje
ts are designed to have smoothed
rather than sharp
orners. One way to smooth a
orner is to lo
ally ins
ribe a
ir
ular
ar
that is tangential to edges forming the
orner. However, other
urves are also often
used. One su
h family of
urves are the Bezier
urves, whi
h have been used in the
design of automobile bodies, for example. A Bezier
urve of order n is a parametri
urve dened by using n
ontrol points p ; : : : ; pn. You will see that the
urve is begins
at p and ends at pn and is smooth. The other
ontrol points \attra
t" the
urve
towards them, but the
urve need not pass through them.
The points on the
urve are dened using a parameter t whi
h varies between 0 and
1, and for ea
h value of t we get one point Bp ;:::;pn (t). This point
an be determined
re
ursively as follows. First, the base
ase:
Bp (t) = p
To
ompute Bp ;:::;pn (t), we rst determine points q ; : : : ; qn , where
qi = tqi + (1 t)qi
i.e. qi is the point dividing the line segment pipi in the ratio t : 1 t. Now we have:
Bp ;:::;pn (t) = Bq ;:::;qn (t)
Write a program whi
h re
eives points p ; : : : ; pn on the graphi
s
anvas and plots the
Bezier
urve dened by them. Vary t in the interval [0,1 in small steps, say = 0:01,
and join Bp ;:::;pn (t) to Bp ;:::;pn (t + ) to get a
urve. Experiment for dierent values
of n and positions of pi.
1
+1
+1
Chapter 15
Stru
tures
An important problem in programming is how to represent the entities that are of interest
in the program. For example, in a program to manage a library, the important entities
might be the books and the library users. In a program about astronomi
al
al
ulations,
the important entities might be the stars and the planets. Of
ourse, you know in prin
iple
how these entities are to be represented. Ea
h entity has
ertain attributes, e.g. books have
titles and authors, or stars have size, position, luminosity and possibly other attributes. You
simply need to dene variables to hold all these attributes. However, for large programs,
there will be many, many variables, and managing them
an be tiresome. Something like a
ling system might be useful. Presumably, we would like to organize related variables into
groups, and perhaps related groups into larger groups and so on. This
ould
ontrol the
lutter in our programs. This is indeed very desirable. In this
hapter, we will see how it
an be done.
The fa
ility we desire is super
ially like an array; an array name does refer
olle
tively
to lots of elements; ex
ept that now we want a name to refer to a
olle
tion of elements
whi
h might be of dierent types. For example, for a book we might want the
olle
tion
to
ontain the title, the name of the author, the pri
e, the a
ession number (the number
under whi
h it is led in the library), information about who has borrowed it and so on. A
stru
ture, as we will dis
uss in this
hapter, provides us what we want: it allows us to group
together data of dierent kinds into a single
olle
tion whi
h we
an
olle
tively refer to by
a single name. Using stru
tures will turn out to be very natural for many appli
ations.
We begin by dis
ussing the basi
ideas of stru
tures. We will show several examples, and
then dis
uss at length a stru
ture using whi
h 3 dimensional ve
tors
an be ni
ely represented
and elegantly manipulated in programs. We will also revisit the taxi dispat
h problem of
Se
tion 13.2.6 and show how its program
an be improved using a stru
ture for representing
queues.
In C++ the word stru
ture is used to denote a
olle
tion of variables. The variables in
the
olle
tion are said to be members of the stru
ture. You
an dene dierent types of
stru
tures, as per your need. For example, to store information about books, you might
dene a stru
ture type Book; you
an spe
ify that every stru
ture of type Book should
264
265
This statement says that the name stru
ture-type will denote a
olle
tion of variables or
members whose names and types are as given. The rules for
hoosing names for stru
ture
types or members are the same as those for ordinary numeri
al variables, but it is often
ustomary to
apitalize the rst letters of stru
ture names, whi
h is a
onvention we will
follow.
As an example, here is how we might dene a stru
ture to store information about a
book.
stru
t Book{
har title[50;
har author[50;
double pri
e;
int a
essionNo;
bool borrowed;
int borrowerNo;
};
Note that a stru
ture denition does not by itself
reate variables or reserve spa
e. But we
an use it to dene variables (sometimes
alled instan
es, or obje
ts) as follows.
Book pqr, xyz;
This statement is very similar to a statement su
h as int m,n;. The statement int m,n;
reates variables m,n of type int. Likewise, the statement Book pqr, xyz; also
reates
variables pqr,xyz, of type Book. As you might expe
t, ea
h of these variables is used for
storing the asso
iated
olle
tion of members. Thus, ea
h su
h variable is allo
ated as mu
h
spa
e as is needed to store the
olle
tion. Assuming 4 bytes are used to store an int and
8 for a double, we will need 16 bytes to store the members a
essionNo, borrowerNo,
and pri
e, and 50+50 bytes to store the members title and author. A bool data type
will typi
ally be given 1 byte. So a total of 117 bytes has to be reserved ea
h for pqr and
xyz. The number of bytes that ee
tively get used might be larger, be
ause there may be
restri
tions, e.g. on many
omputers it is ne
essary that the starting address of a variable
must be a multiple of 4.
The word stru
ture, or its short form stru
t is often used to denote (a) a spe
i
variable
of a spe
i
stru
ture type, e.g. the variable xyz above, or (b) a spe
i
stru
ture type, e.g.
Book as dened above, or (
) the entire
ategory of variables of any stru
ture type. This is
similar to how we might use the word
ower in every day
onversation. It might mean the
spe
i
lotus whi
h you have just plu
ked, or a spe
i
type of
ower, as in \a lotus is a
266
ower", or the entire
ategory of
owers, as in \every
ower is pretty in its own way". The
pre
ise meaning will be
lear from the
ontext.
A member of a stru
ture variable
an be referred to by joining the variable and the
member name with a period, e.g. xyz.a
essionNo. Su
h referen
es behave like variables
of the same type as the member, and so we may write:
xyz.a
essionNo = 1234;
out << xyz.a
essionNo + 10 << endl;
in.getline(pqr.title,50);
The rst statement will store the number 1234 in the member a
essionNo of the variable
xyz. The se
ond statement will add 10 to xyz.a
essionNo, in whi
h we just stored 1234.
Thus this statement will
ause 1244 to be printed. In the third statement, the referen
e
pqr.title refers to the rst of the two
har arrays in pqr. Just as we
an read a
hara
ter
string into a dire
tly dened
har array, so
an we into this member of pqr.
We
an initialize stru
tures in a manner similar to arrays. Assuming Book dened as
above we might write
Book b = {"On Edu
ation", "Bertrand Russell", 350.00, 1235, true, 5798};
This will
opy elements of the initializer list to the
orresponding members in the stru
ture.
Here is a stru
ture for representing a point in two dimensions.
stru
t Point{
double x;
double y;
};
We may
reate instan
es of this stru
ture in the manner des
ribed before, i.e. by writing
something like Point p1;. We are allowed to have one stru
ture be
ontained in another.
Here for example is a stru
ture for storing information about a
ir
le,
stru
t Cir
le{
Point
enter;
double radius;
};
One stru
ture
an be
opied to another using an assignment. For example, assuming
1 is
as dened above, we
an further write:
267
Cir
le
2;
2 =
1;
The rst statement
opies every member of
1.
enter to the
orresponding members of p.
The se
ond
opies every member of p to the
orresponding member of
2.
enter.
We nally note that variables
an be dened in the same statement as the denition of
the stru
ture. For example, we
ould have written
stru
t Cir
le{
Point
enter;
double radius;
}
1;
We
an refer to members of the elements of the arrays in the natural manner. For example,
[5.
enter.x refers to the x
oordinate of the
enter of the fth
ir
le in
. Similarly
library[96.title[0 would refer to the starting letter in the title of the 96th book in
library.
268
The rst statement de
lares
ptr to be of type Cir
le*, i.e. pointer to Cir
les. The se
ond
statement stores the address of
1 in
ptr, whi
h we often refer to as \sets
ptr to point
to
1". The third statement dereferen
es it so that *
ptr really means
1, and then the
radius member of this is set to 5.
C++ provides the operator -> where x->y means (*x).y. Hen
e (*
ptr).radius
ould
instead be written as
ptr->radius, whi
h you will agree is easier to read.
15.1.4 Pointers as stru
ture members
It is possible to have pointers as members of a stru
t. For example, we might have an
alternate way to represent stru
tures as follows.
stru
t Cir
le2{
double radius;
Point *
ptr;
}
Thus we have
reated
1 and
2 to be
ir
les of radii 5, 6 respe
tively, both
entered at
the point p1. Say we wanted to get the x
oordinate of the
enter. For this we would write
1.
ptr->x, whi
h would evaluate to 10.0 as you would expe
t. Note further that if you
write
2.
ptr->y = 25;
it would hange the y oordinate of p1, and hen e of the enter of both the ir les.
269
The intention of the denition should be
lear; in ea
h student stru
ture, we wish to store a
pointer to the best friend of that student. But for this we have had to use the name Student
inside its own denition! However it does not
ause a problem. A pointer to a stru
t
needs the same amount of memory no matter what is inside the stru
t. Thus using the
new denition we
an allo
ate memory for a Student obje
t easily: we just need to allo
ate
whatever is needed for an int and for a pointer.
We
an use this to link students to their best friends as in the program below.
1
int main(){
Student s1, s2,
s1.rollno = 1;
s2.rollno = 2;
s3.rollno = 3;
s1.bestFriend =
s2.bestFriend =
s3.bestFriend =
s3;
&s2;
&s3;
&s2;
Thus after
reating instan
es s1, s2, s3, we set them respe
tively to have roll numbers 1,
2, 3. Then we set s1's best friend to be s2, s2's best friend to be s3, and s3's best friend to
be also s2. Thus the rst print statement will print 2, while the se
ond will print 3.
We will see detailed examples of su
h linked stru
tures later.
It is possible to pass stru
ture variables to fun
tions. The key point to be noted is that the
name of a stru
ture variable denotes the
ontent of the asso
iated memory, like ordinary
numeri
al variable names, and unlike the name of an array, whi
h denotes the address of
the asso
iated memory. Thus stru
tures behave like ordinary variables as far as passing to
fun
tions and being returned from fun
tions.
270
We rst
onsider an example for our Point stru
ture as dened above. The fun
tion
below returns a Point that is the midpoint of the line joining two given points. It is followed
by a main program that uses it.
// Point as defined earlier
Point midpoint(Point a, Point b){
Point mp;
mp.x = (a.x+b.x)/2;
mp.y = (a.y+b.y)/2;
return mp;
}
int main(){
Point p1 = {0.0, 0.0}, p2 = {100.0, 200.0}, p3;
p3 = midpoint(p1, p2);
out << midpoint(midpoint(p1,p2), p2).x << endl;
}
The fun
tion
alls above exe
ute essentially as per the des
ription in Se
tion 9.1. Consider
the
all midpoint(p1,p2). First, an a
tivation frame is
reated. Then the values of the
arguments p1,p2 are
opied to the
orresponding parameters a,b in the a
tivation frame.
Then the lo
al variable mp is
reated. Then its members x,y respe
tively are set to the
averages of the
orresponding members of a,b. Thus mp.x, mp.y will get the values 50,
100. Then, mp will be returned. Note that returning a stru
ture means
opying its value
to the main program. To re
eive this value, in the main program a temporary variable of
type Point will be
reated. After the returned value is pla
ed in the temporary variable,
we are free to do whatever we like with it. In the se
ond statement of the main program,
we are merely
opying the value of the temporary variable to the variable p3. However, we
an do essentially everything with this variable that
an be done using any variable. We
see this in the last statement. The
all midpoint(p1,p2) will return the point (50,100).
The temporary variable whi
h holds this is then passed as an argument to another
all to
midpoint. This
all will return the midpoint of the points (50,100) and (100,200). Thus it
will return the point (75,150). This point will be stored in a temporary stru
ture, and nally
we take its x member, whi
h gets printed. Thus 75 will get printed.
We
an also use
all by referen
e (Se
tion 9.7). Suppose we want to shift a given point
by some amount dx,dy in the x; y dire
tions respe
tively. Then the following fun
tion is
natural to write.
2
Noti
e that we are passing the rst argument, the point, by referen
e. Thus the point p in
the body will be deemed to refer to same variable that is passed as the rst argument in the
alling program. Thus the x,y members of that variable will get modied.
Note that temporary variables are
reated also with ordinary numeri
al variables. For example, when
you write a = z + sin(x)*y, temporary variables will be
reated to hold the value of sin(x).
2
271
We have not only made the parameters be referen e parameters, but also de lared them
onst, i.e. asserted that they will not
hange during exe
ution. Sin
e the fun
tion midpoint
does not modify the points a,b, it is
learly a good idea to de
lare our intent expli
itly
and mark the parameters
onst. Indeed, if a parameter is marked
onst, then it improves
readability be
ause a human reader
an tell at a glan
e that it will not be modied.
However, the
onst keyword serves another important purpose. Suppose we did not mark
the parameters
onst. Then the rst
all in our main program above, midpoint(p1,p2)
would be
ompiled ne, but for the
all midpoint(midpoint(p1,p2),p2) the
ompiler would
ag an error! If a parameter is a non-
onst referen
e parameter, the
ompiler assumes that
you wish to modify it in the
ode. But if you wish to modify it, then the
orresponding
argument must not be a
ompiler generated temporary stru
ture whi
h is
onsidered to
be a
onstant. So if you do indeed pass a temporary stru
ture su
h as midpoint(p1,p2),
the
ompiler suspe
ts that you have perhaps made a programming error (over paranoidly,
perhaps) and tells you so. But if the parameter is marked
onst, then the
ompiler knows
that you are passing the parameter by referen
e only to avoid
opying.
15.2.2 Passing pointers to stru
tures
Note nally that you
an pass stru
tures to fun
tions using a pointer and then dereferen
e
the pointer in the body to a
ess the members of the stru
ture. But it is
onsidered better
to use referen
es (
onst if appropriate).
In Chapter 17 we will see a program whi
h deals with motion in 3 dimensional spa
e. This
program will deal
onsiderably with 3 dimensional ve
tor quantities su
h as positions, velo
ities, and a
elerations. So we will design a stru
ture whi
h makes it
onvenient to represent
su
h quantities.
A ve
tor in 3 dimensions
an be represented in many ways. For example, we
ould
onsider it in Cartesian
oordinates, or in so
alled spheri
al
oordinates, or
ylindri
al
In addition, the a
tivation frame of the
alled stru
ture will also have to have memory allo
ated to store
the stru
ture. This in
reases the total memory requirement of the program.
3
272
oordinates. For simpli
ity, we
onsider the rst alternative: Cartesian
oordinates. Thus
we will have a
omponent for ea
h spatial dimension. Clearly our stru
ture must hold these
3
oordinates. We will
all our stru
ture V3 and it
an be dened as:
stru
t V3{
double x,y,z;
};
We should put this outside the main program. If the program is organized into many les,
this should go into a header le, whi
h
an then be in
luded in all other les whi
h need this
stru
ture.
If our program uses 3 dimensional ve
tors, very likely it will need to add su
h ve
tors, or
multiply su
h a ve
tor by a number. Here is a fun
tion to add two ve
tors. The resulting
ve
tor is returned.
V3 sum(V3
onst &a, V3
onst &b){
V3 v;
v.x = a.x + b.x;
v.y = a.y + b.y;
v.z = a.z + b.z;
return v;
}
Noti
e that we have made the parameters be referen
e parameters to avoid
opying, but also
made them
onst sin
e the parameters are not altered by the fun
tion.
Next we have a fun
tion to s
ale up a ve
tor by a numeri
al fa
tor.
V3 s
ale(V3
onst &a, double fa
tor){
V3 v;
v.x = a.x*fa
tor;
v.y = a.y*fa
tor;
v.z = a.z*fa
tor;
return v;
}
It is also useful to have a fun
tion that
omputes the length of a ve
tor.
double length(V3
onst &a){
return sqrt(a.x*a.x + a.y*a.y + a.z*a.z);
}
We
an now use these fun
tions to
ompute the distan
e s
overed by parti
le having initial
velo
ity u, moving under
onstant a
eleration a after time t, as per the formula s = ut+ at ,
where it should be noted that s; u; a are ve
tor quantities.
1
2
int main(){
V3 u,a,s;
double t;
273
in >> u.x >> u.y >> u.z >> a.x >> a.y >> a.z >> t;
s = sum(s
ale(u,t), s
ale(a,t*t/2));
out << length(s) << endl;
A stru
t enables you to group together related variables and give them a name. A dierent
way to state this is: if some variables in your program are related,
onsider putting them
into a suitable stru
ture! By doing this, you are more
learly expressing the relationships
between your variables, and thereby making your program easier to understand.
In the taxi-dispat
h problem of Se
tion 13.2.6, we mimi
ked a bla
kboard on whi
h IDs
of waiting drivers would be written in real life. The bla
kboard was not present in the
statement of the problem. But it was an important entity in the solution of the problem and
hen
e, it is a good idea to use a stru
t to represent it. We do this next.
The bla
kboard was really doing the work of a queue in whi
h we put in IDs of waiting
drivers. The term queue has a real life meaning: people wait in it and people leave from
it in the order in whi
h they arrive. Likewise, IDs enter our bla
kboard and then leave in
the same order. So we will
all our stru
t a Queue, whi
h suggests its fun
tion, rather
than
alling it a bla
kboard. Inside the stru
t we will have as members all related variables,
driverID, front, nWaiting. Note however that queues
an be used to represent other
entities besides waiting drivers, so for the array whi
h held the IDs of the drivers we will
use the name elements rather than driverID. Likewise we will use the name QUEUESIZE to
denote the size of the array rather than the name MAXWAITING.
stru
t Queue{
int elements[QUEUESIZE, nWaiting, front;
};
QUEUESIZE
Grouping of related operations into a ni
ely named fun
tion also helps des
ribe the intent.
Thus we should have fun
tions whi
h perform the work of inserting into the queue and also
removing from the queue.
bool insert(Queue &q, int value){
if(q.nWaiting == QUEUESIZE) return false; // queue is full
q.elements[(q.front + q.nWaiting) % QUEUESIZE = value;
q.nWaiting++;
return true;
}
int remove(Queue &q){
if(q.nWaiting == 0) return -1; // queue is empty
274
Note that we have passed the queue q by referen
e, so that modi
ations made to it are
visible ba
k in the main program. Given these fun
tions, the main program
an be written
in a ni
er manner than in Se
tion 13.2.6.
int main(){
Queue q;
q.front = 0;
q.nWaiting = 0;
while(true){
har
ommand;
in >>
ommand;
if(
ommand == 'd'){
int driver;
in >> driver;
if(!insert(q, driver))
out << "Cannot register.\n";
}
else if(
ommand == '
'){
int driver = remove(q);
if (driver == -1)
out << "No taxi available.\n";
else
out << "Assigning: " << driver << endl;
}
}
}
This main program is easier to understand as
ompared to the main program of Se
tion 13.2.6. This is be
ause it does not
ontain mu
h detail about how exa
tly the waiting
IDs are stored in the queue. That detail is moved to the fun
tions insert and remove.
These fun
tions on the other hand are not
on
erned with how the queue is being used.
The two fun
tions together guarantee that so long as the queue is a
essed only using these
fun
tions, we will get the expe
ted behaviour: (a) whatever we insert into the queue will
be given ba
k to us in a rst in rst out order, (b) we will not insert something when the
queue is already full, (
) our a
esses to the array q.driverID will not be out of range. So
although our
ode has be
ome a bit longer, we
an see that ea
h pie
e is easier to understand
than the
ompa
t main program of Se
tion 13.2.6.
We
ould think of a stru
ture as merely a me
hanism for managing data; we organize data
into a
olle
tion rather than have lots of variables lying around. However, on
e you dene
a stru
ture, it be
omes natural to write fun
tions whi
h manipulate the data
ontained in
the stru
tures. You might say that on
e we dened V3, it is almost inevitable that we write
fun
tions to perform ve
tor arithmeti
and
ompute the Eu
lidean length. On
e we dened
275
Queue, it seemed quite natural to dene fun
tions insert and remove as well. Had we
dened a stru
ture to represent some other entity, say a book (in a library), we might have
found it useful to write a fun
tion that performs the re
ord-keeping needed when a book is
borrowed.
Indeed, you might
onsider su
h fun
tions to be as important to the stru
ture as are
members of the stru
ture. So perhaps, should we make the fun
tions a part of the stru
ture
itself?
The denition of stru
tures you have seen so far really
omes from the C language. In
the more modern denition of stru
tures, as it is in the C++ language, the denition of
stru
tures has been extended so that it
an also in
lude fun
tions. At a high level, the more
general denition of a stru
ture is the same as before.
stru
t stru
ture-type {
member-des
ription1
member-des
ription2
...
}
But, now, a member-des
ription may dene a member-fun
tion, in addition to being able
to dene a data member as before.
We begin with an example. Here is an alternate way to write our stru
t V3 and its main
program.
stru
t V3{
double x,y,z;
double length(){
// member fun
tion length
return sqrt(x*x + y*y + z*z);
}
V3 sum(V3 b){
// member fun
tion sum
V3 v;
v.x = x + b.x; v.y = y + b.y; v.z = z + b.z;
return v;
}
V3 s
ale(double t){
// member fun
tion s
ale
V3 v;
v.x = x*t; v.y = y*t; v.z = z*t;
return v;
}
void joker(double q){
// member fun
tion, in
luded for fun.
x = q;
out << length() << endl;
}
};
int main(){
V3 u, a, s;
276
double t;
in >> u.x >> u.y >> u.z >> a.x >> a.y >> a.z >> t;
s = u.s
ale(t).sum(a.s
ale(t*t/2));
out << s.length() << endl;
We explain next how this
ode works, i.e. how member fun
tions
an be dened and used.
In general, the member-des
ription of a member fun
tion has the following form.
return-type fun
tion-name (parameter1-type parameter1, parameter2-type
parameter2, ...) {body}
As you
an see, the denitions of sum, s
ale and length all t in this form.
A member fun
tion is expe
ted to be
alled on an obje
t of the given stru
ture type,
using the same \." notation used for a
essing data members. We will use the term re
eiver
to denote the obje
t on whi
h the member fun
tion is
alled. A simple example of a member
fun
tion
all is the expressions u.s
ale(t) in the main program above, with u the re
eiver.
The general form of the
all is:
re
eiver.fun
tion-name(argument1, argument2, ...)
When the
all p.length() exe
utes an a
tivation frame is rst
reated. Sin
e there are no
arguments, there is nothing to be
opied. So the body of the fun
tion will start exe
uting. In
the body, the names x,y,z will refer to the
orresponding members of the re
eiver, p. Thus,
the statement return
sqrt(x*x + y*y + z*z); will return sqrt(1.0*1.0 + 2.0*2.0 +
p
3.0*3.0), i.e. 14. This will get printed.
277
When the
all p.joker(5) exe
utes, an a
tivation frame will again be
reated. Then
the value of the argument, 5, will be
opied to the
orresponding parameter, q in the body
of the member fun
tion joker. Then the assignment x=q will
ause the member x of the
re
eiver, in this
ase, p, to be set to 5. Then there is a
all to length. Sin
e the fun
tion
name appears by itself, i.e. not as r.length() for any r, it is deemed to refer to the re
eiver
itself. Thus the length of p is
al
ulated. Note that
the value of p.x has
hanged
p to 5, and
hen
e the length
al
ulated and printed will be p5 5 + 2 2 + 3 3 = 38. After this
the exe
ution of p.joker will nish.
Finally, in the last statement, we will print the value of p.x. Note that 5 will get printed,
be
ause this is what we set it to in the
all p.joker(5).
The
ode we have given in the main program at the beginning should now be understandable, ex
ept perhaps for the expression u.s
ale(t).sum(a.s
ale(t*t/2)). This expression
should really be read as
(u.s
ale(t))
.sum(
(a.s ale(t*t/2))
Sin
e u.s
ale(t) returns a V3 obje
t, we
an invoke the member fun
tion sum on it, passing
as the argument the V3 obje
t returned by a.s
ale(t*t/2).
15.5.1 Referen
e parameters and
onst
It is possible to make the parameters to member fun
tions be referen
e parameters. Indeed,
it is re
ommended to do so, espe
ially when the stru
ture being passed is large. By passing
a stru
ture by referen
e, we avoid the overhead of
opying it.
It is also good to add
onst qualiers wherever possible. First, if any of the arguments
is not modied by the fun
tion, then the
orresponding parameter should also be de
lared
onst. Se
ond, if the re
eiver is not modied by the fun
tion, we
an indi
ate as mu
h by
adding the keyword
onst after the parameter list but before the body. The fun
tion sum
above modies neither its argument, nor its re
eiver. Hen
e it is better written as:
V3 sum (V3
onst &b)
onst{
V3 v;
v.x = x + b.x; v.y = y + b.y;
return v;
}
We should similarly modify the member fun tions s ale and length.
278
First, we need to de
ide where to pla
e the
ounter. It would seem natural that the
ounter be somehow asso
iated with the Point type. This is indeed possible through the
use of stati
data members, as follows.
stru
t Point{
double x,y;
stati
int
ounter;
// only de
lares
Point(){
ounter++;
}
Point(double x1, double y1) : x(x1), y(y1){
ounter++;
}
};
int Point::
ounter = 0;
// a
tually defines
int main(){
Point a,b,
(1,2);
out << Point::
ounter << endl;
}
A stati
data member is a variable asso
iated with a stru
t type. It is de
lared by prexing
the keyword stati
to the de
laration. Note that while there will be a member x and a
member y in every Point stru
ture
reated through either of the
onstru
tors, there is only
one
opy of the variable
ounter. Inside the denition of Point, the variable
ounter
an
be referred to by using the name
ounter, outside the denition a stati
variable must be
referred to by prexing its name by the stru
t name and ::. So in this example we have
used Point::
ounter.
There is a subtlety asso
iated with stati
data members. The denition of the stru
ture
Point does not a
tually
reate the stati
data variables; a stru
t denition is merely expe
ted to
reate a type, without allo
ating any storage. Hen
e we need the statement marked
\a
tually defines" in the
ode above.
15.6.2 Stati
member fun
tions
You
an also have stati
member fun
tions. For example, in the above
ode we
ould have
added the following stati
fun
tion denition of resetCounter to the denition of Point.
stati
void resetCounter(){
ounter = 0; } // note keyword ``stati
''
Stati
member fun
tions
an be referred to by their name inside the stru
ture denition,
and by prexing the stru
ture name and :: outside the denition. Further, stati
member
fun
tions are not invoked on any instan
e, but they are invoked by themselves. So we
an
write Point::resetCounter() in the main program if we wish to set Point::
ounter to 0.
Note that in non-stati
member fun
tions we use the names of the non-stati
members
by themselves to refer to non-stati
members of the re
eiver, i.e. the obje
t on whi
h the
279
non-stati
member fun
tion is invoked. However, for a stati
member fun
tion, there is no
re
eiver. Thus it is an error to refer to non-stati
members by themselves in the body of a
stati
member fun
tion.
15.6.3 The this pointer
Inside the denition of any ordinary member fun
tion, the keyword this is a pointer to the
re
eiver. Normally, we do not need to use this pointer, be
ause we
an get to the members
of the re
eiver by using the names of the members dire
tly. However, it should be noted that
we
ould use this too. Thus we
ould have written the length member fun
tion in V3 as
double length(){
return sqrt(this->x*this->x + this->y*this->y + this->z*this->z);
}
We must return the re
eiver if its radius is bigger than the radius of the argument
ir
le.
Thus we return *this.
15.6.4 Default values to parameters
Default values
an be given, as for ordinary fun
tions by spe
ifying them as =
the
orresponding parameter.
value
after
280
5. Dene a stru
ture for representing
omplex numbers. In addition to having a
onstru
tor whi
h takes the real and imaginary parts as arguments, write a
onstru
tor whi
h
will take as arguments r; and returns a
omplex number rei = r
os + i sin . Note
that this
onstru
tor
annot have just two real arguments { that will
lash with the
onstru
tor taking real and imaginary parts as arguments. Add an optional argument,
say a bool type, whi
h if spe
ied says whether the pre
eding two arguments are to
be interpreted as real and imaginary parts or as r:.
6. Dene a stru
ture for representing axis parallel re
tangles, i.e. re
tangles whose sides
are parallel to the axes. An axis parallel re
tangle
an be represented by the
oordinates of the diagonally opposite points. Write a fun
tion that takes a re
tangle (axis
parallel) as the rst argument and a point as the se
ond argument, and determines
whether the point lies inside the re
tangle. Write a fun
tion whi
h takes a re
tangle
and double values dx,dy and returns a re
tangle shifted by dx,dy in the x and y
dire
tions respe
tively.
7. Dene a stru
ture
lass for storing information about a book for use in a program dealing with a library. The
lass should store the name, author, pri
e, a library a
ession
number for the book, and the identi
ation number of a library patron (if any) who
has borrowed the book. This eld, patron identi
ation number
ould be 0 to indi
ate
that the book is not borrowed.
Read information about books from a le into an array of book obje
ts. Then you
should enable patrons to issue and return books. When a patron issues/returns a book,
the patron identi
ation number of the book should be
hanged. Write fun
tions for
doing this. The fun
tions should
he
k that the operations are valid, e.g. a book that
is already re
orded as borrowed is not being borrowed without rst being returned.
8. Write a program to answer queries about an
estry. Your program should read in a le
that
ontains lines giving the name of a person (single word) followed by the name of
the father (single word). Assume that there are at most 100 lines, i.e. 200 names. After
that, your program should re
eive a name from the keyboard, and print all an
estors
of the person, in the order father, grandfather, great grandfather and so on as known.
Adapt the ideas from Se
tion 15.1.5.
Chapter 16
Classes: stru
tures with a personality
When we design a stru
ture-type, we often have a
lear idea as to how the instan
e stru
tures
will be used. For example,
onsider the Queue stru
ture-type dis
ussed in the last
hapter.
We expe
t that a Queue obje
t will be
reated, and we will set the data members nWaiting,
front to 0. Subsequently the member fun
tions insert and remove will be
alled to insert and remove elements as needed. We also expe
t that the data members will not be
independently modied, i.e. if q is an obje
t of type Queue, you will not write something
like q.nWaiting = 50;. The member q.nWaiting will
hange, but this will happen only
as a part of the exe
ution of the member fun
tions insert or remove. As designers of a
stru
ture, it is perhaps desirable if we
learly state how we expe
t the stru
ture to be used,
and perhaps also prohibit inappropriate uses. If we
an do this, perhaps it would redu
e
programming errors.
The situation is a
tually quite similar to how ele
tri
al devi
es are designed. For example,
a television
omes with a
ontrol panel on the front (or a remote
ontrol) whi
h helps you to
ontrol it. If you wish to
hange the
hannel or in
rease the volume, you press the appropriate
buttons provided for that purpose. You do not need to open the ba
kside and manipulate
any ele
tri
al
omponent dire
tly! In a similar manner, the users of the Queue stru
ture
should be given an interfa
e (like the
ontrol panel) whi
h tells them the fun
tions using
whi
h they
an use the stru
ture. Anything else, they should not be allowed to do. Users of
Queue should be
on
erned with the interfa
e just as the users of television sets need only
know how to use the
ontrol panel. The users of television sets need not know what is inside
the box; similarly, the users of Queue need not know pre
isely how the fun
tions provided do
their job, so long as they do what they promise. We dis
ussed similar ideas in the
ontra
t
model for designing fun
tions (Se
tion 9.3).
C++ indeed allows designers to build stru
tures whi
h users must a
ess only through
a
arefully
hosen set of fun
tions (the interfa
e), and whose internal details su
h as data
members are hidden. In fa
t, C++ allows stru
ture designers glorious
ontrol over the entire
life
y
le of the stru
ture variables. Designers
an pre
isely
ontrol how their stru
ture
variables will be (a)
reated, (b) used in assignments, (
) used with dierent operators, (d)
passed to fun
tions, (e) returned from fun
tions, (f) and nally destroyed when needed. As
we have seen in the previous
hapter, C++ already provides default me
hanisms for (a), (b),
(d), (e), (f). But it turns out that we
an
hange those me
hanisms to suit our needs.
Te
hni
ally, the term
lass is essentially synonymous with the term stru
ture, as we will
281
282
see in Se
tion 16.8. However in more
ommon parlan
e, the term stru
ture is typi
ally used
to mean what we dened in Se
tion 15.1. This is how stru
tures originated in the C language.
The notion got extended in C++ to in
lude member fun
tions as dis
ussed in Se
tion 15.5,
and as will be further extended in this
hapter. In
ommon parlan
e, the term
lass is used
to mean the extended modern notion.
A
onstru
tor is a spe
ial member fun
tion that you
an write in order to make it easier
to initialize, or even automate the initialization of a stru
ture. We will show two examples
before dis
ussing how
onstru
tors work in general.
stru
t Queue{
int nWaiting, front, elements[QUEUESIZE;
Queue(){
//
onstru
tor begins
front = nWaiting = 0;
}
//
onstru
tor ends
}
The above
ode denes the stru
ture Queue and a
onstru
tor member fun
tion for it. Given
this denition, suppose we write
Queue q1, q2;
in the main program. It turns out that this statement will not only allo
ate memory for
but also initialize q1.front, q1.nWaiting, q2.front and q2.nWaiting all to 0! As you
an see, this is very
onvenient be
ause we will never use a Queue without rst setting the
members front and nWaiting to 0. Given the above
onstru
tor, we dont have to worry
about forgetting to initialize the members.
Before we explain how
onstru
tors work, another basi
motivation behind
onstru
tors
should be noted: as mu
h as possible, outside of a stru
ture denition, we should a
ess only
the member fun
tions, and not refer to the data members dire
tly. This is be
ause fun
tions
are dened
arefully by the designer having
onsidered the proper ways of manipulating the
stru
ture. So it is best to not dire
tly a
ess the data members. Also see Se
tion 16.7.1. If
data members are not to be a
essed outside the denition, then the only way they
an be
initialized is using a fun
tion. A
onstru
tor is meant to be su
h a fun
tion.
In our next example we show two
onstru
tors for our
lass V3 and their use.
q,
stru
t V3{
double x,y,z;
V3(double p, double q, double r){
x = p;
y = q;
z = r;
}
V3(){
x = y = z = 0;
// onstru tor 1
// onstru tor 2
};
283
}
// des
ription of other member fun
tions omitted.
int main(){
V3 ve
1(1.0,2.0,3.0);
V3 ve
2;
}
The rst statement in the main program will
reate a variable ve
1 of type V3, with its
x,y,z members set to 1.0, 2.0, 3.0 respe
tively. The se
ond statement will
reate a variable
ve
2 of type V3 with its members set to 0, 0, 0. As you might guess, the initialization of the
two variables has somehow happened using our two
onstru
tors respe
tively. We dis
uss
the pre
ise me
hanism of all this next.
In general, a
onstru
tor is spe
ied as a member fun
tion of the same name as the
stru
ture type. Further, there is no return type. Here is the general form.
stru
ture-type (parameter1-type parameter1, parameter2-type parameter2,
...){ body }
You
an spe
ify as many
onstru
tors as you want, so long as the list of parameter types are
dierent for ea
h
onstru
tor.
Whenever a variable of stru
ture-type is dened in the program, memory is allo
ated
for the variable, and then an appropriate
onstru
tor gets
alled on the
reated variable to
initialize it. Whi
h
onstru
tor gets
alled depends upon whether the name of the variable
in the denition is followed by a list of arguments. If there is an argument list, then a
onstru
tor with a mat
hing list of parameters is sele
ted. Thus, in
ase of our denition of
ve
1 above, there is a list with 3 double arguments. Hen
e
onstru
tor 1 is sele
ted. If no
argument list is given following the variable name, then a
onstru
tor
all will be made with
no arguments, and so a
onstru
tor whi
h
an be
alled without arguments is sele
ted. In
the denition of q1,q2 and ve
2 above, there is no argument list, and hen
e the
onstru
tor
taking no arguments (
onstru
tor 2 in
ase of ve
2) is sele
ted for initializing.
Next the sele
ted
onstru
tor is
alled on the variable to be initialized, using arguments
as appropriate. In other words, the variable to be initialized serves as the re
eiver for the
all.
This
all exe
utes like any member fun
tion
all, as des
ribed in Se
tion 15.5. Spe
i
ally,
an a
tivation frame is
reated and the argument values are
opied to the parameters. Then
the body of the
onstru
tor is exe
uted, with the re
eiver being the variable to be initialized.
Let us now see what happens for the statement V3 ve
1(1.0,2.0,3.0); in our program
above. As we said, this would
ause
onstru
tor 1 to be
alled on ve
1 using the arguments
1.0, 2.0, 3.0. Thus in the exe
ution an a
tivation frame is
reated and the argument values,
1.0, 2.0, 3.0 are
opied to the
orresponding formal parameters p,q,r. Then the body of
onstru
tor 1 starts exe
ution. The rst statement of the body, x = p; sets the x member
of the re
eiver, ve
1, to the value of the parameter p. Similarly, the members y and z are
set to the values q and r respe
tively. After this the
onstru
tor
all ends. Thus at the end,
ve
1 will have its members x,y,z set to 1.0, 2.0, 3.0 respe
tively. The statement V3 ve
2;
is exe
uted similarly. It
auses the se
ond
onstru
tor to be invoked on ve
2. As you
an
see, it will set all 3 members to 0. Similarly for the initialization of q1,q2.
284
Note that if you want the
onstru
tor without arguments to be
alled, you must not
supply any list of arguments; it is not
orre
t to supply an empty argument list. This
is be
ause V3 ve
2(); is not the same as V3 ve
2;. The former means something quite
dierent: it de
lares ve
2 to be a fun
tion that takes no arguments and returns a result of
type V3, as we dis
ussed in Se
tion 11.2.1.
If one stru
ture is nested inside another, then the
onstru
tor exe
utes slightly dierently.
This and other nuan
es are dis
ussed in Se
tion 16.1.4.
16.1.1 Calling the
onstru
tor expli
itly
If a
onstru
tor is
alled expli
itly by supplying required arguments, it does result in the
reation of a temporary stru
ture. Su
h stru
tures
an be used in subsequent pro
essing, as
dis
ussed in Se
tion 15.2.
V3 ve
3, ve
4;
ve
3 = V3(1.0, 2.0, 3,0);
ve
4 = V3();
In this
ase, the
all V3(1.0, 2.0, 3.0) will exe
ute as follows. First a temporary (nameless) variable of type V3 is
reated. Then the
onstru
tor is
alled on it with the given
arguments. As a result we have a temporary V3 stru
ture whose members are set to 1.0,
2.0, 3.0 respe
tively. This temporary stru
ture
an be used as we wish, in the above
ode
its value is
opied to the variable ve
3. The next statement likewise
reates a temporary
stru
ture with all members 0. This is
opied to ve
4. Thus, after the exe
ution of the
ode
above, the variables ve
3 and ve
4 will have the same values as ve
1 and ve
2 earlier,
respe
tively.
Using an expli
it
onstru
tor
all, we
an write the sum member fun
tion of Se
tion 15.5
more
ompa
tly as follows.
stru
t V3{
...members and
onstru
tors 1 and 2...
V3 sum (V3 b){
return V3(x+b.x, y+b.y, z+b.z);
}
}
285
Now you
ould
all the
onstru
tor with either no argument, or upto 3 arguments { parameters
orresponding to arguments that have not been given will get the default values, in this
ase 0. Note that if you in
lude our new
onstru
tor in the denition, you
annot in
lude
any of the
onstru
tors we gave earlier. Say you spe
ied the new bundled
onstru
tor and
also
onstru
tor 2. Then a
all V3() would be ambiguous, it would not be
lear whether
to exe
ute the body of
onstru
tor 2, or the body of the new
onstru
tor in whi
h all 3
parameters are initialized to their spe
ied defaults.
16.1.3 \Default" Constru
tor
We have said that C++ supplies a
onstru
tor, if no
onstru
tor is given in the denition of
a stru
t. This
onstru
tor takes no arguments, and its body is empty. Su
h
onstru
tor is
alled a default
onstru
tor, however the term is a
tually used more generally: it has
ome to
mean a
onstru
tor that
an be
alled with no arguments, even if su
h a
onstru
tor has been
expli
itly dened by the programmer. Thus for V3 our
onstru
tor 2 is a default
onstru
tor.
Likewise, the bundled
onstru
tor dened above would also be a default
onstru
tor.
A default
onstru
tor is needed if you wish to dene arrays of a stru
ture, be
ause ea
h
element of the array will be
onstru
ted only using the default
onstru
tor.
Note that C++ does not supply a default
onstru
tor if you give any
onstru
tor whatsoever. So if you dene a non-default
onstru
tor (i.e. a
onstru
tor whi
h must take at least
one argument), then the stru
ture would not have a default
onstru
tor. Thus you would
not be able to
reate arrays of that stru
ture.
The default
onstru
tor is important also when we nest a stru
ture inside another. We
dis
uss this next.
16.1.4 Constru
tors of nested stru
tures
Suppose a stru
ture X has other stru
tures Y,Z,... as members. Then during the
all to a
onstru
tor for X, the
onstru
tors of Y,Z,... are
alled before the body of the
onstru
tor
of X is exe
uted. This happens re
ursively, i.e. if Y in turn has members whi
h are stru
tures.
This rule sounds reasonable, but applying it
an sometimes be tri
ky. Consider the Point
and Cir
le
lasses as follows.
stru
t Point{
double x,y;
Point(double p, double q){x=p; y=q;}
};
stru
t Cir
le{
Point
enter;
double radius;
};
286
As dis
ussed above, the default
onstru
tor for Cir
le will be
alled. Sin
e we did not supply
a
onstru
tor, C++ will
reate one for us. Note however, that this
onstru
tor must rst
onstru
t all the members of Cir
le. To a
omplish this, the
onstru
tor
reated by C++
will
all default
onstru
tors of all the members as well. So in our
ase, the C++
onstru
ted
onstru
tor for Cir
le will
all the default
onstru
tor for Point. But the
onstru
tor of our
lass Point takes two arguments, and hen
e is not a default
onstru
tor. Further, be
ause a
onstru
tor is given for Point C++ will not
reate any
onstru
tors for Point. Thus writing
Cir
le
; as above would be a
ompiler error!
This problem
an be solved using initialization lists.
16.1.5 Initialization lists
When a Point member is
reated while
onstru
ting a Cir
le obje
t, we must somehow
indi
ate that a two argument
onstru
tor must be used. We
an do this if we write a
onstru
tor for Cir
le. Here is one possible way.
stru
t Cir
le{
Point
enter;
double radius;
Cir
le(double x, double y, double r) :
enter(Point(x,y)), radius(r)
{
// empty body
}
};
The text following the : to the end of the line in the above
ode is an initilization list.
The initialization list of a
onstru
tor says how the data members in the re
eiver should be
onstru
ted before the exe
ution of the
onstru
tor itself
an begin.
Thus in this
ase the
ode says that
enter should be
onstru
ted using the
onstru
tor
all Point(x,y), where x,y are from the parameter list of the Cir
le
onstru
tor. Similarly
the member radius of the Cir
le being
onstru
ted is assigned the value r. In general, the
initialization list
onsists of
omma separated items of the form
member-name(initializing-value)
This will
ause the member member-name to be initialized dire
tly using initializing-value.
If the initializing value
alls a
onstru
tor, then instead of writing out the
all, just the
omma separated arguments
ould be given. In our Cir
le
onstru
tor, the initialization
list of
enter happens by
alling the
onstru
tor Point. Thus the initialization list
an be
shortened as:
enter(x,y), radius(r)
Note that in our example, all the work got done in using the initialization lists, so the
body is empty. Note that we
ould
hoose to initialize only some of the members using the
initialization list and initialize the others in the body, if we wish.
1
Whenever possible you should perform initialization through initialization lists, be ause it is likely faster.
287
Noti
e that we have given values to members x,y using initialization lists. Thus the members
will be assigned values when the stru
ture is
reated. Later on, the values
annot be
hanged;
indeed, the
ompiler will
ag an error if you write a statement su
h as p.x = 5.0; where p
is of type Point.
C++ allows you to spe
ify how stru
tures are passed to fun
tions by value, and how they
an be returned from fun
tions. The model for this is as follows. For every stru
ture, C++
denes by default a so
alled
opy-
onstru
tor. The
opy
onstru
tor is used for
opying the
value of a stru
ture that is being passed to a fun
tion as an argument, and also to
opy ba
k
the value if a fun
tion returns a stru
ture. A
opy
onstru
tor takes a single argument, but
that argument has to be a referen
e argument, otherwise we will need to (re
ursively!) use
the
opy
onstru
tor to
opy that argument.
The default
opy
onstru
tor merely
opies ea
h data member of the sour
e stru
ture to
orresponding member of the destination.
As you have probably guessed, you
an yourself redene the
opy
onstru
tor to do what
you wish. A
onstru
tor whi
h takes a single parameter of type referen
e to the stru
ture
type, or
onstant referen
e to the stru
ture type is
onsidered to be a
opy
onstru
tor. If
you dene su
h a
onstru
tor, that will be used for passing arguments by value and returning
values, instead of the automati
ally generated
opy
onstru
tor.
Below we show a
opy
onstru
tor for our Queue stru
ture.
stru
t Queue{
int front, nWaiting, elements[QUEUESIZE;
Queue(){
front = nWaiting = 0;
}
Queue(
onst Queue &other):
front(other.front), nWaiting(other.nWaiting){
for(int i=front, j = 0; j<nWaiting; j++){
elements[i = other.elements[i;
};
288
i = (i + 1) % QUEUESIZE;
}
... members insert and remove...
As you
an see the above implementation of the
onstru
tor does not
opy the entire member
elements, but only the relevant portion of it. Clearly, this is more e
ient than
opying the
entire stru
ture.
The main use of the
opy
onstru
tor will arise in
onne
tion with dynami
memory
allo
ation. We will see this in Se
tion 19.3.2. Also see Se
tion 16.11.1.
We know that a variable is destroyed when
ontrol leaves the blo
k in whi
h the variable is
dened. By default, destru
tion of a variable simply means freeing the memory used for the
variable. However, we might wish to take other a
tions besides freeing up the memory. For
this we may spe
ify a destru
tor member fun
tion. If a variable of type T is being destroyed,
then the destru
tor for T is
alled on the variable, and only then the memory of the variable
is freed. The destru
tor for stru
t T is denoted as ~T, and is a spe
ial member fun
tion that
takes no arguments and has no return type. Here is an example.
stru
t Queue{
... other member definitions as before ..
~Queue(){
if(nWaiting > 0)
out <<"Warning: Non-empty Queue being destroyed.\n";
}
};
int main(){
Queue q;
{
Queue q2;
q2.insert(5);
}
}
Anytime a Queue type variable is destroyed, the fun
tion ~Queue will be automati
ally
alled.
This will print a warning if a queue
ontaining elements is being destroyed { presumably
you might expe
t that a queue should be destroyed only after all elements in it have been
pro
essed.
In the above main program, when
ontrol exits the inner blo
k, the variable q2 will be
destroyed. This will
ause the destru
tor to be automati
ally
alled on q2. Sin
e q2 will
not be then empty, the message will be printed. Just before the program terminates, the
289
variable q will get destroyed. This will also
ause the destru
tor to be
alled, on q. Sin
e q
will then be empty, this will not
ause a message to be printed.
Note that usually, it is an error to
all the destru
tor expli
itly. It will be
alled automati
ally whenever a variable is to be destroyed. For now we know only one way a variable
an be destroyed: when
ontrol leaves a blo
k. In Se
tion 19.1 we will see another way in
whi
h variables
an be destroyed, whi
h will produ
e
alls to the destru
tor.
In Se
tion 19.3.1, the more
ommon use of destru
tors is des
ribed. Also see Se
tion 16.11.1.
Consider the stru
t V3 that we dened in Se
tion 15.3 for representing 3 dimensional ve
tors.
In mathemati
s, it is natural to add two ve
tors, the result of whi
h is a third ve
tor, whose
omponents are the sums of the respe
tive
omponents of the rst two ve
tors. To get the
sum of two ve
tors, we dened the member fun
tion sum in Se
tion 15.3. However, it might
be more natural to get the sum of ve
tors by just using the addition operator. In other
words, suppose v,w are ve
tors, i.e. variables of type V3. Wouldn't it be ni
e if we
ould
write v+w whi
h would have the same ee
t as sum(v,w)?
This is indeed possible. In this se
tion we see how it
an be done. For this we rst need
to understand how C++ interprets expressions involving operators su
h as +.
If is an inx operator, i.e. the operator is written between the operands as in
x y
This is merely a
all to the member fun
tion named operator, invoked on the obje
t x,
with y as the argument. If su
h a member fun
tion is present, then the expression will be
a
ordingly evaluated! Note that operator is a reserved word.
Here is how you
ould dene the operators + and * to work with our stru
t V3.
stru
t V3{
// members and
onstru
tors as defined earlier
V3 operator+ (
onst V3 &b)
onst{
return V3(x + b.x, y+b.y, z+b.z);
}
V3 operator* (double t)
onst{
return V3(x*t, y*t, z*t);
}
};
Be
ause of the rst denition, we
an add two V3 obje
ts to produ
e a new V3 obje
t,
identi
al to what our member sum would have produ
ed. The se
ond denition allows us to
multiply a V3 obje
t by a double, exa
tly mimi
king the behaviour of the member fun
tion
s
ale. Thus, using these denitions, we
an write a mu
h ni
er looking main program:
290
int main(){
V3 u,a;
double t;
in >> u.x >> u.y >> u.z >> a.x >> a.y >> a.z >> t;
out << u*t + a*t*t*0.5 << endl;
}
We note that this ability to dene operator a
tion for stru
ture should be used with
are.
Be
ause of our familiarity of mathemati
s, the interpretation of dierent operators is very
rmly xed in our minds. If we dene operators re
klessly, in
onsistent with our intuition, it
is likely to produ
e
ode whi
h will be
onfusing. Indeed it is re
ommended that arithmeti
operators be redened only for mathemati
al quantities, where the operators are used in a
similar manner in mathemati
s. Our denition of + and * are
onsistent with this be
ause the
notion of adding mathemati
al ve
tors and multiplying a mathemati
al ve
tor by a number
are very standard.
When we dene an operator a
tion for a stru
ture, we are said to be overloading the
operator. The following binary operators
an be overloaded.
+
=
- * / %
+= -= *=
^ & | <
/= %= ^=
&&
||
We will see an example for the operator = in Se
tion 16.6 and in Se
tion 19.3.4, and for the
operator [ in Se
tion 19.3.3.
In C++, fun
tion
alls
an also be
onsidered operators! Indeed, C++ treats a fun
tion
all
f(a1,a2,...an)
as equivalent to
f . operator() (a1, a2, ..., an)
Thus, if f happens to be a stru
t, for whi
h the member fun
tion operator() is dened,
then it will get
alled! In other words, you treat stru
t instan
es just like fun
tions and
\
all" them if you wish. The stru
t instan
es whi
h
an be
alled are often termed fun
tion
obje
ts. We will see an example of this in Se
tion ??.
For overloading unary operators, see Appendix E.3.
291
Here we are assuming that member fun
tion operator* is already dened in the
lass V3.
For another example, suppose next that we wish to be able to print V3 obje
ts on the
standard output stream just as we
an print out the fundamental data types. This
an be
done as follows.
ostream & operator<< (ostream & ost, V3 &v){
ost << v.x <<' '<< v.y <<' '<< v.z <<' ';
return ost;
}
This will dene the operator << on left hand side operators of type ostream, whi
h is indeed
the type of
out, and right hand side operators of type V3.
As you
an see, this fun
tion will merely print out the x,y,z members separated by a
single spa
e
hara
ter. The fun
tion returns ost so that you
an
hain output operations,
i.e. so that you
an write
out << v1 << v2; (Se
tion 12.6.3). Note that throughout we
are passing ostream obje
ts by referen
e, be
ause ostream obje
ts do not allow
opying
(Se
tion 16.7.2).
Not all operators
an be overloaded as ordinary fun
tions. In parti
ular, the assignment
operator and various
ompound assignment operators, the indexing operator [, the fun
tion
all operator () and the arrow operator ->
an only be overloaded using member fun
tions.
The assignment operator is already dened for stru
tures: ea
h member of the right hand
side stru
ture is
opied to the
orresponding member of the left hand side stru
ture. But
you
an
hange that if you wish.
Here is how you might override the default denition of = for our stru
ture Queue.
stru
t Queue{
.. other members as before ..
Queue & operator=(
onst Queue& rhs){
front = rhs.front;
nWaiting = rhs.nWaiting;
};
292
We do a member by member
opy, ex
ept that we dont
opy the entire elements array but
just that part of it whi
h is in use. Just as we did for the
opy
onstru
tor of Queue. At
the end the fun
tion returns a referen
e to the
urrent obje
t on whi
h the assignment is
invoked, i.e. the left hand side of the assignment as the value of the assignment expression
(Se
tion 3.2.6). Thus we
an write multiple assignments in the same statement if we wish,
i.e. of the form q1 = q2 = q3;.
Like the
opy
onstru
tor, the main motivation for overloading assignment will be
ome
lear when we
onsider dynami
memory allo
ation, in Se
tion 19.2.4.
Finally we
onsider the last step in designing a produ
t: pa
kaging it so that only the
ontrol
panel shows on the outside and the internal
ir
uitry is hidden.
C++ provides a simple way to hide members. The designer of a stru
ture may designate
ea
h member of the stru
ture as either private, publi
, or prote
ted. Private members
an be a
essed only inside the methods of the
lass, and are not a
essible outside the
lass
denition (but also see Se
tion 16.7.3). Publi
members, on the other hand, are
onsidered
to be a
essible by all. In other words, they
an be used inside the
lass denition if needed,
but also outside of it. We will explain prote
ted members later.
To spe
ify a
ess, we divide the members in the
lass into groups, and before ea
h group
pla
e the labels publi
:, private: or prote
ted: as we want the members in the group
to be
onsidered. You may use as many groups as you wish. For example we may dene the
stru
ture queue as:
stru
t Queue{
private:
int front;
int nWaiting;
int elements[QUEUESIZE;
publi
:
Queue(){...}
bool insert(int value){...}
int remove(){...}
};
In this, we have made the data members private, and the fun
tion members publi
. Thus, in
this
ase if we wrote q.nWaiting = 7 outside the denition, say in the main program, the
ompiler would
ag it as an error. Be
ause the
onstru
tor and the fun
tions insert and
remove are publi
, outside the denition of Queue we
an only use those.
Making the data members private is a very
ommon idea. Typi
ally, a
arefully
hosen
set of fun
tion members is made publi
.
16.7.1 A
essor and mutator fun
tions
Sometimes some data members are dire
tly useful outside of an obje
t. In su
h
ases, it is
onsidered appropriate to make them private, and allow a
ess to them by dening a
essor
293
//
//
//
//
With this denition, we
ould a
ess and modify (or mutate) the
oordinates of a point, even
though the
orresponding data members are private.
Note however that the above strategy has an advantage as
ompared to making the
members x,y publi
. Suppose that tomorrow we de
ide to represent a point using its polar
oordinates, say using members r and theta. Then we
an still retain the member fun
tions
dened above, but only
hange the bodies appropriately. For example, the fun
tion getx
would now have to return r*
os(theta). We would have to make
hanges to the Point
denition, however, we may not need to
hange the
ode that uses Point, sin
e the user
ode does not dire
tly a
ess the data members.
16.7.2 Prohibiting
ertain operations
Note that if we dene a
opy
onstru
tor or an assignment operator with either publi
,
private or prote
ted a
ess
ontrol, C++ will not generate the default versions for these. If
we make any of these operators non-publi
, then it will be equivalent to saying that they
annot be used at all outside the stru
ture denition. Thus if we make the assignment
operator private, then ee
tively we are forbidding assignment for the stru
ture. If we make
the
opy
onstru
tor private, then we are ee
tively saying that the stru
ture
annot be
passed by value, and also
annot be returned.
In the
ase of the
lass Queue, there might be some reason to forbid the assignment as
well as passing by value. This is be
ause intuitively we might think: an element
an only
be in one queue, if we make a
opy we are perhaps inviting errors. Note that even if we
make the
opy
onstru
tor private, the obje
t
an still be passed to fun
tions, but only by
referen
e.
16.7.3 Friends
If you make some members of a stru
t private, then they
an only be a
essed inside the
stru
t denition.
Sometimes this is too restri
tive. For example, if you make data members private in stru
t
V3, then using what you have seen so far, you will not be able to dene the << operator as
we did in Se
tion 16.5. This is be
ause the fun
tion operator<< in Se
tion 16.5 refers to
the members x,y,z whi
h we made private.
294
C++ allows you to over
ome this di
ulty. You go ahead and dene the operator<<
fun
tion as you wish, a
essing the private members also. To enable the fun
tion operator<<
to a
ess the private members, you put a line de
laring the fun
tion as a friend in the
stru
ture denition.
stru
t V3{
...
friend ostream & operator<< (ostream &ost,
onst V3 &v);
...
}
This will de
lare operator<< to be a friend, whi
h means that it is allowed to a
ess the
private members of V3. In general, the line will read friend fun
tion-de
laration.
Noti
e that you
ould have a
heived the same ee
t by de
laring all members to be
publi
. However, that would allow all fun
tions a
ess; by making a fun
tion a friend, you
provide sele
tive a
ess.
The same fun
tion
an be a friend of several stru
tures, and several fun
tions be a friend
of the same stru
ture. In fa
t, you
an have one stru
ture A be a friend of another stru
ture
B. This way, the private members of stru
ture B
an be used inside the denition of stru
ture
A. To do this you merely insert the line friend A; inside the denition of stru
ture B.
16.8 Classes
A stru
ture as we have dened it, ex
ept for a minor dieren
e, is more
ommonly known in
C++ as a
lass.
The small dieren
e between the two is as follows. In a stru
ture, all members are
onsidered publi
by default, i.e. a member that is not in any group that is pre
eded by
an a
ess spe
ier is
onsidered publi
. In a
lass, all members are
onsidered private by
default. To get the latter behaviour, you simply need to use the keyword
lass instead of
stru
t in the denition.
lass Queue{
...
};
Quite often, a
lass (or stru
t) will be developed independently of the program that uses it,
possibly by a dierent programmer. Thus we need a proto
ol by whi
h the
ode that denes
the
lass
an be a
essed by
ode in other les. Following our dis
ussion of fun
tions, it is
ustomary to organize ea
h
lass C into two les: C.h and C.
pp.
295
First, some important terms. It is
ustomary to say that the body of ea
h memberfun
tion provides an implementation of the member-fun
tion. In fa
t, the bodies of all
member fun
tions together are said to
onstitute an implementation of the
lass itself. When
the implementation is given as a part of the
lass denition, it is said to be given in-line.
However, when
lasses are large and developed independently, it is more
ustomary to put
the denition of a
lass C without out the implementation, into the le C.h, the so
alled
header le. The implementation is put into the le C.
pp, using some spe
ial syntax. If
there are any friend fun
tions, their de
larations
an also put in C.h, and implementations
in C.
pp. We show this using an example.
Consider the stru
t V3 that we have been dis
ussing all along. We will show example les
V3.h and V3.
pp for it. We will make V3 be a
lass, and de
lare the data members x,y,z
as private, as is
ustomary. The le V3.h
ould be as follows.
lass V3{
private:
double x, y, z;
publi
:
V3(double p=0, double q=0, double r=0);
V3 operator+(V3
onst &w)
onst;
V3 operator*(double t)
onst;
double length()
onst;
friend ostream & operator<<(ostream & ost, V3 v);
};
ostream & operator<<(ostream & ost, V3 v);
We next show the implementation le V3.
pp, whi
h denes the member fun
tions. A
denition of a member fun
tion f appearing outside the de
laration of a
lass C is identi
al
to the denition had it appeared in-line, ex
ept that the name of the fun
tion is spe
ied as
C::f. The
onstru
tor for
lass C will appear as C::C, of
ourse.
#in
lude <simple
pp>
#in
lude "V3.h"
V3::V3(double p, double q, double r){
x = p;
y = q;
z = r;
}
// onstru tor
296
ost << "(" << v.x << ", "<< v.y << ", "<< v.z << ")";
return ost;
The last fun
tion in the le is the friend fun
tion operator<<.
It is a
eptable if some of the implementations are pla
ed in line in the header le.
Typi
ally, small member fun
tions are left in-line in the header le, while the large member
fun
tions are moved to the implementation le.
If the
lass
ontains a stati
data member, then the member is de
lared (Se
tion 15.6.1)
in the header le, and dened in the implementation le.
16.9.1 Separate
ompilation
We
an now separately
ompile the implementation le, and produ
e, for the
lass V3, the
obje
t module V3.o. This module, and the header le, must be given to any programmer
who uses the
lass V3. Suppose a program using V3 is
ontained in the le user.
pp, then
it must in
lude the le V3.h. The program
an now be
ompiled by spe
ifying
s++ user.
pp V3.o
Other sour
e/obje
t les needed for the program must also be mentioned on the
ommand
line, of
ourse.
16.9.2 Remarks
The general ideas and motivations behind splitting a
lass into a header le and an implementation le are as for fun
tions. In whi
hever le the
lass is used, the header le must be
in
luded, be
ause the
lass must be dened. The implementation le or its obje
t module
is needed for generating an exe
utable.
If the header le
hanges, but the publi
part of the
lass does not
hange, the user
program needs to be re
ompiled. If the publi
part of the
lass
hanges, then likely the user
program will also have to
hange to use the
hanged
lass de
larations.
Like fun
tions, we
an templatize
lasses as well. The pro
ess of dening a
lass template is
very similar. Here is a template version of our V3
lass.
template<T>
lass V3{
private:
T x, y, z;
publi
:
V3(T p=0, T q=0, T r=0){ x = p;
V3 operator+(V3 w);
}
y = q;
z = r;}
297
template<T>
V3 V3::operator+(V3 w){ return V3(x+w.x, y+w.y, z+w.z); }
The template variable T determines the type of ea
h
omponent x,y,z, and is expe
ted to
be spe
ied either as float or double. We have only shown 2 member fun
tions for brevity.
One is dened in-line, the other is dened outside the
lass denition. Note that you must
put the line template<T> before the member fun
tion dened outside as well.
Note that the template denition does not
reate a
lass, but a s
heme to
reate a
lass.
To
reate a
lass, you must spe
ify a value for the template variable and ax it in angle
bra
kets to the
lass-name. To
reate a
lass of the template with T being float, you simply
write:
V3<float> a,b,
;
This will
reate the
lass V3<float> from the template, as well as dene a,b,
to be variables
of type V3<float>. In your programs, you
an use V3<float> as a
lass name.
Note that the template for a
lass must be present in every sour
e le that needs to
use it. So it is
ustomary to pla
e it in an appropriate header le. Noti
e that the
lass is
generated only when an instan
e is
reated as in the line V3<float> a,b,
; above. Thus
there is no notion of separately
ompiling a template.
We should point out that you have already used lasses without knowing it.
16.11.1 Graphi
s
By now you have probably realized that our graphi
s
ommands (Chapter 5 and elsewhere)
are built using
lasses. Indeed, the names Turtle, Re
tangle, Polygon, Line, Point are
all names of
lasses. The
ommands to
reate
reate
orresponding obje
ts on the
anvas
were merely
orresponding
onstru
tors. The various operations we have des
ribed on the
graphi
s obje
ts are member fun
tions.
You
an perhaps guess how the ability to write our own
onstru
tors et
. helps in developing a graphi
s library. When we exe
ute a statement su
h as
Turtle t;
not only must we
reate a variable, but we must also draw the turtle on the s
reen. This
drawing operation
an be done inside the Turtle
onstru
tor! Similarly, when a graphi
s
obje
t is destroyed, the s
reen must be redrawn to remove that obje
t from view. This is
done as a part of the destru
tor! In general, there are a number of book-keeping operations
needed when dealing with graphi
s obje
ts, the
ode for these
an be
onveniently pla
ed in
the
onstru
tors, destru
tors, and other appropriate member fun
tions.
298
at the beginning of the le, along with other lines you may have su
h as #in
lude
On
e you have this you
an
reate a so-
alled input stream, by writing:
<simple pp>.
ifstream myinfile("input.txt");
This
reates a variable
alled myinfile in your program, of
lass ifstream, whi
h is a
sub
lass of istream. The quoted name inside the parentheses tells where the stream will get
its data: in this
ase, the statement says that the values will
ome from the le input.txt
whi
h must be present in the
urrent working dire
tory. After this, you
an treat the name
myinfile just as you treat
in, and you
an write statements su
h as myinfile >> n; whi
h
will
ause a whitespa
e delimited value to be read from the le asso
iated with myinle into
the variable n.
Note that just as you
an write assert(
in) to
he
k whether there was an error in
reading or if the stream has ended, so
an you write assert(myinfile). In both
ases, if
there is an error or if the stream ends, the variable will be
ome NULL and hen
e the assertion
will fail.
In a similar manner you
an write
ofstream myoutstream("output.txt");
whi
h will
reate the variable myoutstream, of
lass ofstream, sub
lass of ostream, and
asso
iated with the le output.txt. You
an treat myoutstream just like
out, i.e. you
an
write values to it using statements su
h as myoutstream << n;. The values will get written
to the asso
iated le, in this
ase output.txt, whi
h will get
reated in the
urrent working
dire
ory.
Here is a program whi
h takes the rst 10 values from a le squares.txt whi
h is
expe
ted to be present in your
urrent dire
tory, and
opies them to a le square
opy.txt,
whi
h will get
reated.
#in
lude <simple
pp>
#in
lude <fstream>
main_program{
ifstream infile("squares.txt");
ofstream outfile("square
opy.txt");
299
int val;
repeat(10){
infile >> val;
outfile << val << endl;
out << val << endl;
}
The values are also printed out on
out whi
h means they will also appear on the s
reen
(unless you redire
t standard out during exe
ution). Noti
e that we have
hosen to enter an
end of line after ea
h value, while printing to outfile as well as
out.
16.12 Remarks
This
hapter provides you with the tools to develop well pa
kaged data stru
tures. You will
typi
ally make the data members private; and make a subset of member fun
tions publi
.
Of
ourse, pa
kaging requires some amount of
areful programming eort. Hen
e, how mu
h
pa
kaging to put is your
hoi
e, to be determined by how you expe
t your
ode to be used
by others. If your
ode is meant for your own, one time use, perhaps it su
es to have no
pa
kaging: use a stru
t rather than a
lass and keep everything visible. However, good
programs tend to evolve. If your program works well, you will inevitably want to make it do
more things. So in general, it is a good idea to pa
kage your data stru
tures well from the
very beginning. It will save eort in the long run.
1. Dene a
lass for storing polynomials. Assume that all your polynomials will have
degree at most 100. Write a member fun
tion value whi
h takes a polynomial and
a real number as arguments and evaluates the polynomial at the given real number.
Overload the +,*,- operators so that they return the sum, produ
t and dieren
e of
polynomials. Also dene a member fun
tion read whi
h reads in a polynomial from
the keyboard. It should ask for the degree d of the polynomial,
he
k that d 100,
and then pro
eed to read in the rst d + 1
oe
ients from the keyboard. Dene a
print member fun
tion whi
h
auses the polynomial to be printed. Make sure that you
only print d + 1
oe
ients if the a
tual degree is d. Carefully de
ide whi
h members
will be private and whi
h will be publi
. Overload the >>, << operators so that the
polynomial
an be read or printed using them.
2. Dene a
lass for storing
omplex numbers. Provide 0, 1, 2 argument
onstru
tors
whi
h respe
tively
onstru
t the
omplex number 0, a
omplex number with imaginary
part 0 and real part as spe
ied by the argument, and a
omplex number with real
and imaginary parts as spe
ied by the arguments. Overload the arithmeti
operators
to implement
omplex arithmeti
.
3. Sometimes we dont know the exa
t values of
ertain quantities, but only know that
the value lies in an interval, say between some numbers L and H . In su
h
ases, we
300
might
hoose to represent the quantity by the pair of numbers L; H . In other words,
we are representing ea
h quantity by the interval [L; H . If you have two quantities
represented by intervals [L ; H and [L ; H , then
learly their sum must lie in the
interval [L + L ; H + H . Thus the last interval
ould be
onsidered to be the sum of
the rst two intervals. Su
h a representation is quite useful when there is un
ertainty
in our knowledge of a quantity.
Dene a
lass Interval whi
h enables us to represent quantities whi
h we know lie
in a
ertain interval. Overload the arithmeti
operators so that you
an perform
arithmeti
on these quantities while keeping tra
k of the un
ertainty. Be
areful:
although in general the un
ertainty in
reases when you perform arithmeti
, if you
subtra
t a quantity (however un
ertain) from itself, you get 0 with
ertainty. Your
implementation should deal with su
h possibilities properly. For this, you will have to
de
ide whether two referen
es R1,R2 are in fa
t identi
al. You
an do this by writing
&R1 == &R2.
Modify the Queue
lass so that it is not possible to make a
opy of a Queue obje
t,
or assign to it. Then write a main program that attempts to make a
opy or an
assignment. Observe that the
ompiler will tell you that you are trying to perform an
operation that is disallowed.
The Queue
lass as dened in the
hapter is to be used for storing integers. But you
may want to queues in whi
h to store double quantities, or in general obje
ts of any
(single)
lass. Templatize the Queue
lass so that this
an be done.
Dene a Car
lass for showing a
ar on the s
reen. A
ar should have a polygonal
body, and two
ir
ular wheels. Add spokes to the wheels. It should be possible to
onstru
t
ars and move them. When a
ar moves, the wheels should rotate. Add
member fun
tions to s
ale the
ar as well.
Constru
t a
lass Button whi
h
an be used to
reate an on-s
reen button, say a
re
tangle, whi
h
an be
li
ked. Clearly, you should be able to
onstru
t buttons at
whatever positions on the s
reen, with whatever text on them. Also, buttons should
have a member fun
tion
li
kedP whi
h takes an int denoting the position of a
li
k,
as obtained from getCli
k(), and determines whether the
li
k position is inside the
button. What other member fun
tions might be useful for buttons?
1
4.
5.
6.
7.
Chapter 17
A proje
t:
osmologi
al simulation
It
ould perhaps be said that the ultimate goal of S
ien
e is to predi
t the future. S
ientists
seek to dis
over s
ienti
laws so that given
omplete knowledge of the world at this instant,
the laws will enable you to say what ea
h obje
t will do in the next instant. And the next
instant after that. And so on. Predi
ting what will happen to the entire world is still very
di
ult, partly be
ause we do not yet know all laws governing all obje
ts in the world. Even
if we knew all the laws, predi
ting what happens to a large system is di
ult be
ause of
the enormous number of
omputations involved. However, for many systems of interest,
we
an very well predi
t how they will behave in dierent
ir
umstan
es. For example, we
understand the physi
s of
ollisions and of the materials used in a
ar well enough to predi
t
how badly a
ar will be damaged if it
ollides against a barrier of
ertain strength at a
ertain
velo
ity. The term simulation is often used to denote this kind predi
tive a
tivity. Indeed
many produ
ts are built today only after their designs are simulated on a
omputer to see
how they hold up under in dierent
onditions.
In this
hapter and Chapters 25 and 26, we will build a number of simulations. The
simulation in this
hapter is
osmologi
al. Suppose we know the state of the stars in a
galaxy at this instant. Can we say where they will be after a million years? Astronomers
routinely do simulations to answer su
h questions. We will examine one natural idea for doing
su
h simulations, and then examine the
aws in that idea. We will then see an improved
idea, whi
h will still be quite naive as
ompared to the ideas used in professional programs.
We will
ode up this idea. We wil use our graphi
s ma
hinery to show the simulation on the
s
reen.
In some sense, simulating a galaxy is rather simple. For the most part, heavenly bodies
intera
t with ea
h other using just Newton's laws of motion and gravitation. As you might
re
all, the law of gravitation states that, two masses ma ; mb with separated by a distan
e d
attra
t ea
h other with a for
e of magnitude
1
Gma mb
d2
301
302
where G is the gravitational
onstant. The ve
tor form of this is also important. If ra ; rb
are the ve
tors denoting the positions of the masses, then the distan
e between the masses
is d = jrb ra j. The for
e on mass ma is in the dire
tion rb ra, and hen
e we may write
the for
e on mass ma in ve
tor form as
Gma mb (rb ra )
(17.1)
jrb raj
If planets
ollide, then presumably more
omplex laws have to be brought in, whi
h might
have to deal with how their
hemi
al
onstituents rea
t. But a substantial part of the simulation only
on
erns how the heavenly bodies move under the ee
t of the gravitational for
e.
It is worth noting that su
h simulations have
ontributed a great deal to our understanding
of how the universe might have been
reated and in general about
osmologi
al phenomenon.
Also, the ideas used in the simulations are very general, and will apply in simulating other
(more earthly!) physi
al phenomenon involving
uid
ow, stresses and strains,
ir
uits and
so on.
Our system, then,
onsists of a set of heavenly bodies, whi
h we will refer to as stars
for simpli
ity. The state of the system will simply
onsist of the position and the velo
ity
(magnitude and dire
tion) of the stars. Suppose we know the initial state, i.e. for ea
h star
i we know its initial position ri and velo
ity vi (both ve
tors). Suppose we want to know
the values after some time . Letting ri0 ; vi0 be the values after time , we may write:
ri0 = ri + vi
vi0 = vi + ai
where vi is the average velo
ity (ve
tor) of the ith parti
le during the interval [t ; t +
and ai is the average a
eleration during the interval. We do not know the average velo
ities
and a
elerations, and indeed, it is not easy to
ompute these quantities. However, the key
observation, attributed to Euler, is that if the interval size is small, then we may assume
with little error that the average velo
ity remains un
hanged during the interval for the
purpose of
al
ulating the position at the end of the interval. Euler's observation is similar
to the idea we used in Se
tion 8.2 to integrate f (x) = 1=x; the value of f was not really
onstant during every interval, but we assumed it is
onstant provided the interval is small
enough. Assuming that the average velo
ity is simply the velo
ity at the beginning we may
write
ri0 = ri + vi
(17.2)
Now, we
an easily
al
ulate the new position ri0 for ea
h parti
le, be
ause we know ri; vi.
Euler's observation also applies to the a
eleration: if the interval is small, then the a
eleration does not
hange mu
h during it. Thus the average a
eleration
an be assumed to be
the a
eleration at the beginning, and we may write:
(17.3)
vi0 = vi + ai
We are not given ai expli
itly, but we have all the data to
al
ulate it. The a
eleration of
the i th star is simply the net for
e on it divided by its mass mi. The net for
e is obtained
3
303
by adding up the gravitational for
e on star i due to all other stars j 6= i. But we know how
to
al
ulate the for
e exerted by one star on another. Thus we may write:
F X Gmj (rj ri )
ai = i =
(17.4)
mi j 6 i jrj ri j
3
We have des
ribed above a pro
edure by whi
h we
an get the state of all parti
les at
time t + given their state at time t. Our answers are approximate, but the approximation
is likely to be good if is small. Pi
king a good is tri
ky; we will assume that we are
somehow given a value for it. Suppose now that we know the state of our system at time
t = 0, and we want the state at time t = T . To do this, we merely run T= steps of our
basi
pro
edure! In parti
ular, we use our basi
pro
edure to
al
ulate the state at time
given the state at time 0. Then we use the state
omputed for time as the input to our
basi
pro
edure to get the state for time 2, and so on. This may be written as:
1. Read in the state at time 0, i.e. the values ri; vi; mi for all i.
2. Read in ; T .
3. For step s = 1 to T=:
(a) Cal
ulate ri0 a
ording to equation (17.2) for all i.
(b) Cal
ulate ai a
ording to equation (17.4) for all i.
(
) Cal
ulate vi0 a
ording to equation (17.3), for all i.
(d) Set ri = ri0 , vi = vi0 for all i
4. end for
5. Print ri; vi for all i.
We will not present the
ode for this algorithm, but you should be able to write it quite
easily.
It turns out that this method
an be extremely slow, be
ause the stepsize must be
taken very small to ensure that the errors are small. However, there are many variations on
the method whi
h have better running time and high a
ura
y. One su
h variation employs
the following rule to
ompute ri0
ri0 = ri + vi + ai =2
(17.5)
where ai is to be
al
ulated as before. You may re
ognize this form. Perhaps you have studied
a formula in kinemati
s for the
ase of uniform a
eleration of a parti
le: s = ut + at =2,
in whi
h s is the distan
e
overed, u the initial velo
ity, a the a
eleration, and t the time.
Our formula is really the same, with the a
eleration, initial velo
ity and time being ai; vi;
respe
tively.
The rule to
ompute vi0
an also be rened as follows.
a + a0
vi0 = vi + i i
(17.6)
2
2
304
1. Read in the state at time 0, i.e. the values ri; vi; mi for all i.
2. Read in ; T .
3. For step s = 1 to T=:
(a) Cal
ulate ai using equation (17.4) for all i.
(b) Cal
ulate ri0 using equation (17.5) for all i. Update ri = ri0 for all i.
(
) Cal
ulate a0i using equation (17.4) for all i.
(d) Cal
ulate vi0 using equation (17.6) for all i. Update vi = vi0 , for all i.
4. end for
5. Print ri; vi for all i.
Figure 17.1: Basi
Leapfrog
in whi
h a0i is the a
eleration
al
ulated at the new positions of the stars, i.e. using equation 17.4 but with ri0 instead of ri. It is not hard to understand the intuition behind this
formula. The a
eleration
at the beginning of the interval is ai, and at the end is a0i. The
ai a0i
average of these, , is likely to be a better estimate of the a
eleration during the interval
rather than simply ai . This is what the above rule uses. Equations 17.5 and 17.6 are said to
onstitute the Leapfrog method of
al
ulating the new state. The algorithm in Figure 17.1
is based on this.
You will note that the algorithm in Figure 17.1 is ine
ient: the value a0i
al
ulated at
the end of an iteration of the loop is re
al
ulated at the beginning of the next iteration. We
will avoid this in the
ode we des
ribe later.
It turns out that the Leapfrog method does indeed give more a
urate results for the
same value of as
ompared to the simpler rules in Equations (17.2,17.3). Of
ourse, the
story does not end here. State of the art programs for
harting the evolution of stars use
even more rened methods. These are outside the s
ope of this book.
+
2
Let us rst
learly write down the spe
i
ations. Our input will be positions and velo
ities
of a
ertain set of stars at time 0. We will also be given a number T . Our goal will be to nd
the positions and velo
ities of the stars at time T . We are also asked to show the traje
tories
tra
ed by the stars between time 0 and time T .
The rst question in writing the program is of
ourse how to represent the dierent
entities in the program. The main entity in the program is a star, of
ourse. A star has
several attributes, its velo
ity and position, and its mass. The mass is simply a
oating point
number. However, the velo
ity and position both have 3
omponents,
orresponding to ea
h
spatial dimension. Clearly, we
an use our V3
lass of Se
tion 16.9 to represent positions,
velo
ities, and a
elerations. The traje
tory of a star is also to be shown on the s
reen.
305
1.
2.
3.
4.
5.
Read in the state at time 0, i.e. the values ri; vi; mi for all i.
Read in ; T .
Cal
ulate ai using equation (17.4) for all i.
Cal
ulate ri0 using equation (17.5) for all i. Update ri = ri0 for all i.
For step s = 2 to T=:
(a) Cal
ulate a0i using equation (17.4) for all i.
(b) Cal
ulate vi0 using equation (17.6). Update vi = vi0 , for all i.
(
) Update ai = a0i for all i.
(d) Cal
ulate ri0 using equation (17.5) for all i. Update ri = ri0 for all i.
6. end for
7. Print ri; vi for all i.
Figure 17.2: Final Leapfrog algorithm
So we probably should asso
iate a graphi
s obje
t, say a Point, with ea
h star. When we
ompute the new position of a star, we should move the Point asso
iated with the star. The
star
lass will need a
onstru
tor and some methods to implement the position and velo
ity
updates as per Equations (17.5,17.6).
As we mentioned in the previous se
tion, the value a0i
al
ulated at the end of the sth
iteration is the same as the value ai
al
ulated at the beginning of the s + 1th iteration.
However, when s = 1, we do need to
al
ulate ai be
ause there is no previous iteration. So
we rearrange the
ode slightly, as shown in Figure 17.2.
Figure 17.2 is really a slight rearrangement of the
ode in Figure 17.1, in the manner
of Figure 7.2. We pulled up statements 3(a), 3(b) out of the loop of Figure 17.1, and they
be
ome statements 3, 4 in Figure 17.2, and they also get added to the end of the loop, i.e.
be
ome statements 5(
), 5(d). Note that ai of the next iteration is the a0i of the previous, so
in statement 5(
) we did not re
al
ulate ai, but merely set ai = a0i . Note that the new loop
is run 1 step less be
ause we ee
tively pulled out one step out.
17.2.1 Main Program
The main program will
reate the stars. It will maintain a variable to keep tra
k of the
elapsed time. It will advan
e this variable in small steps to rea
h the given duration T . As
it advan
es time, it will
al
ulate the for
es, and
all appropriate methods on the stars to
update their positions and velo
ities.
int main(int arg
,
har* argv[){
initCanvas("Star satellite system",800,800);
306
ifstream simDatafile(argv[1);
int n; simDatafile >> n;
Star stars[n;
onst float star_radius_for_graphi
s = 15;
float T, delta; simDatafile >> T >> delta;
setup_star_data(simDatafile, stars, n, star_radius_for_graphi
s);
arstep(n,stars, delta);
The program
reates the
anvas to show the orbits, then opens the le
ontaining the data
about the simulation. It expe
ts the lename to be spe
ied as a
ommand line argument.
From the spe
ied le, it reads n, the number of stars, T, the time duration of the simulation,
and delta the time step duration, i.e. the value . Next, the fun
tion setup star data
reads the data about the stars into the array stars of
lass Star. It pla
es the data read
into ea
h star obje
t.
void setup_star_data(ifstream & file, Star stars[, int n, float radius){
float mass, x, y, z, vx, vy, vz;
for(int i=0; i<n; i++){
file >> mass >> x >> y >> z >> vx >> vy >> vz;
stars[i.init(mass, V3(x,y,z), V3(vx,vy,vz), radius);
}
assert(file); // qui
k
he
k that input was valid
}
Next the program
alls the fun
tion arstep
orresponding to steps 3,4 of Figure 17.2. Then,
within the loop, the fun
tion avrstep is
alled,
orresponding to steps 5(a){5(d). The
fun
tion arstep is as follows.
void arstep(int n, Star stars[, float delta){
V3 for
es[n;
al
ulate_net_for
e(n, stars, for
es);
for(int i=0; i<n; i++)
stars[i.arStep(delta, for
es[i);
}
As you
an see, it
al
ulates the for
es on ea
h star due to other stars, using the fun
tion
al
ulate net for
e. The for
e on ea
h star is passed as an argument to the arstep
method of ea
h star. The avrstep fun
tion is identi
al, ex
ept that it
alls the avrstep
method for ea
h star.
The task of
al
ulating for
es is fairly simple as you would expe
t.
307
V3 f(distve
*(fmag/dist));
for
es[i = for
es[i + f;
for
es[j = for
es[j - f;
// for e on star i
Sin
e the for
e due to star i on star j has the same magnitude as the for
e due to star j on
star i, but opposite dire
tion. So we
al
ulate the for
e just on
e, and add it to the total
for
e on star i, and subtra
t it from the total for
e on star j . Noti
e how the V3
lass makes
it easy to write this fun
tion.
These fun
tions
an be pla
ed in a le, main.
pp.
The data member image, of
lass Point, will be used for produ
ing the graphi
al animation.
The x,y
oordinates of the position (stored in member r) will be used as the position of
ea
h body on the s
reen; you may
onsider that we are viewing the
osmologi
al system in
the z dire
tion, so that only the x,y
oordinates are important. The member image will be
made to put down its pen, so that the orbit will be tra
ed on the s
reen, as you will see in
the member fun
tion init, in the implementation le star.
pp below.
308
// update anvas
where we assume that V3.h and V3.o from Se
tion 16.9 are in the same dire
tory as main.
pp
and star.
pp.
To exe
ute the program we need a le
ontaining the data for stars. A sample le
3stars.txt is as follows.
3
3000
10
100 497.00436 375.691247 0 0.466203685 0.43236573
309
0
0
This is meant to simulate a 3 star system for 1000 steps, with = 10. The initial positions
and velo
ities of the stars are given as above. Note that they have been
arefully
al
ulated.
You
an simulate this system by typing:
./a.out 3stars.txt
The stars will tra
e an interesting gure of 8 orbit on whi
h they will
hase ea
h other.
Figure 17.3 gives a snapshot. The stars have their pen down, and hen
e the orbits tra
ed
are also visible.
310
1. Run the
osmologi
al simulation given in the text. Use it to simulate a system
onsisting of a planet orbiting a star. Modify the given
ode so that it uses the rst (simpler)
method dis
ussed in the
hapter. Compare the two methods. For small enough velo
ities, the planet will travel around the star for both methods. You will observe,
however, that for Euler's method, the orbit will keep diverging for any stepsize, whi
h
is
learly erroneous. For the same stepsize, you should be able to observe that the
leapfrog orbit does not diverge, or diverges mu
h less.
2. Consider an elasti
string of length L tied at both ends. Suppose it
onsists of n
equal weights,
onne
ted together by springs of length L=n + 1. Suppose ea
h spring
has Hooke's
onstant k, i.e. if the string is stret
hed by distan
e x, a tension kx is
produ
ed. Suppose one of the masses is moved to some new position. Suppose the
string is at rest after this. Clearly, the springs on either side of the mass will stret
h
equally, if gravity is ignored. Now suppose the mass is released. Simulate the motion
assuming there is no gravity.
3. Consider a sequen
e of
ars travelling down a single lane road. In a simplisti
model,
suppose that the
ars have the same maximum speed V , and a
eleration a and de
eleration d. Suppose ea
h
ar attempts to ensure that it
an
ome to a halt even if
the
ar ahead of it were to stop instantaneously (e.g. be
ause of an a
ident). Further
assume that the driver is aware of this distan
e, and slows down if the distan
e ahead
redu
es, and speeds up if the distan
e in
reases, but only till the speed rea
hes V .
Build a simulation of a
onvoy of
ars whi
h travels along the road on whi
h there are
signals present. When a signal turns red, the leading
ar in the
onvoy brakes so that it
omes to a halt at the signal. Of
ourse, the drivers do not rea
t immediately, but have
some response time. Note though that usually it is very easy to see if the
ar ahead is
slowing down, be
ause the tail red light
omes on. In
orporate su
h details into your
simulation. Show an animation of the simulation using our graphi
s
ommands.
Chapter 18
Graphi
s Events
You already know that the fun
tion getCli
k
auses the program to wait for the user to
li
k on the graphi
s
anvas, and then returns a representation of the
oordinates of the
li
k
position. However, it is possible to intera
t in a ri
her manner with the graphi
s
anvas. It
is possible for your program to wait for the mouse to be dragged, or a key to be pressed, or
a similar su
h event. After the event happens, you
an de
ide what a
tion to take. Using
the features that we will dis
uss in this
hapter, you should be able to write very intera
tive
and easy to use programs, and even games.
We begin by des
ribing how you
an wait for events, and nd out exa
tly what event has
happened. After that we will sket
h two appli
ations.
18.1 Events
311
312
A nextEvent
all
auses the program to wait for an event to happen. In this it is like
the statement
in >> ..., whereupon the program waits for input to be given. When the
fun
tion returns, the argument event will
ontain information about the event that has
taken pla
e. The program
an extra
t this information and a
ordingly take a
tions.
A
all to
he
kEvent returns true if an event has happened sin
e the last
all to nextEvent
or
he
kEvent. It If no event has taken pla
e sin
e the last
all to nextEvent or
he
kEvent
then the fun
tion just returns false.
It is worth emphasizing that the
he
kEvent fun
tion does not wait, unlike nextEvent.
18.2.1 Mouse button press events
The fun
tion mouseButtonPressEvent when
alled on an event returns true i the event is
of type mouse button press. On
e you know that the event is of type mouse button press,
you
an get additional information about it using the members event.xbutton.button,
whi
h returns an integer denoting whi
h button was pressed, and event.xbutton.x and
event.xbutton.y whi
h give the
oordinates of the mouse at the time the button was
pressed. Here is an example.
XEvent event;
nextEvent(event);
if(mouseButtonPressEvent(event)){
out <<"Mouse button "<< event.xbutton.button
<<" pressed, at position ("<< event.xbutton.x <<
<<", "<< event.xbutton.y << endl;
}
This
ode will
ause the program to wait until some event happens, and then if the event
was the pressing of some mouse button, it will print whi
h button was pressed (i.e. 1, 2 or
3) and at what
anvas
oordinates.
18.2.2 Mouse drag events
The fun
tion mouseDragEvent when
alled on an event returns true i the event was a
mouse drag, i.e. the user dragged the mouse after pressing a mouse button. The members
event.xmotion.x and event.xmotion.y give the
oordinates of the drag position.
313
In this we have used the imprintLine fun
tion rather than
reate a line and
alling imprint
on it. In our experien
e, the latter is too slow { the line drawing lags behind the
ursor
movement.
Perhaps many of you are familiar with a game
alled Snake, variations of whi
h are available
on many
omputers and even mobile phones.
The essen
e of the game is to
ontrol a snake that keeps on moving on the s
reen. The
player may have goals su
h as steering the snake towards food/prizes, or preventing it from
hitting obsta
les. There may be variations in whi
h the tail of the snake might grow as it eats
food. Typi
ally the snake is represented as a sequen
e of segments (vertebrae!). The head,
or segment 0 has a movement dire
tion, North, East, South or West, and it keeps moving
one step in that dire
tion per time step. The subsequent segments follow, i.e. segment i
314
moves to the position of segment i 1, for i 1. The player
an
hange the dire
tion of the
head movement to a new dire
tion, say by typing in n,e,s,w.
Here we will develop the
ore logi
e of the game, i.e. show the snake on the s
reen and
enable the player to
hange its dire
tion. The addition of prizes et
. are left for the exer
ises.
18.4.1 Spe
i
ation
We have more or less des
ribed the spe
i
ations above. Perhaps only one
lari
ation
is needed: the segments of the snake will move on a two dimensional grid, with the grid
separation being gridsep.
18.4.2 Classes
As we have mentioned, we should have a
lass for all the important entities
ontained in our
program. So
learly we must have a Snake
lass.
The snake will have a body whi
h
onsists of several segments. So it is natural to
onsider an array named body
onsisting of Cir
les, where we have arbitrarily de
ided that
the segments be
ir
ular. We will use a
onstant length to denote the number of segments
in the body. Ea
h segment will maintain its own position, so we need not have an additional
position attribute for the snake. However, it is useful to keep data member giving the
urrent
dire
tion of movement. Sin
e the movement is only along the four dire
tions, we have
hosen
type
har for this member, and it is expe
ted to take values 'n', 'e', 's', 'w'.
When the snake moves, segments 1 through length-1 move into the positions of the
segments 0 through length-2. So in ee
t, be
ause the segments are visually identi
al,
it might seem that the segments 1 through length-2 stay xed, while segment length-1
moves to the position where segment 0 is expe
ted to move. Our
ode in fa
t does this.
Note that this means that what was previously segment i be
omes segment i-1 % length.
So instead of
opying around the segments within the array body, we merely keep an data
member headindex whi
h gives the index of the segment whi
h is the head of the snake.
The way in whi
h the segment whi
h was the tail earlier be
omes the head
an be handled
in many ways. We do this as follows. We make the erstwhile tail segment be
ome a
opy
of the erstwhile head. At this point we move the erstwhile tail segment in the dire
tion of
motion.
The
ode for the
lass snake is given in Figure 18.1.
18.4.3 User intera
tion
The main point of the example is of
ourse how the user intera
ts with the snake. This
happens in the main program given below.
The main program sets up the
anvas and the snake. It then goes into an endless loop
in whi
h every 0.1 se
onds the program
he
ks if the user has typed anything in order to
hange the dire
tion of the snake. This is done using the fun
tion
he
kEvent. If the user
has indeed typed a key, then its value is extra
ted using the
harFromEvent fun
tion. This
key is then used as an argument to the member fun
tion move of the snake, so that the
movement happens in the required dire
tion. If the user did not type anything, then an an
315
onst int gridsep = 20, xinit = 30, yinit = 20, length = 10, npts = 40;
stru
t Snake{
stru
t Snake{
Cir
le body[length;
int headindex;
// whi
h body element is the head of the snake
har dir;
//
urrent dire
tion of motion.
Snake(){
// head at (xinit,yinit) in the
oarse grid.
headindex = 0;
for(int i=0; i<length; i++)
body[i.reset((xinit+0.5+i)*gridsep, (yinit+0.5)*gridsep, gridsep*0.5);
dir = 'w';
}
void move(){
move(dir);
//
ontinue along old dire
tion
}
void move(
har
ommand){
if(
ommand != 'w' &&
ommand != 'n' &&
ommand != 'e' &&
ommand != 's')
ommand = dir;
// if user typed illegal letter, keep moving in old dire
tion
int newhead = (headindex +length - 1) % length; // old tail
body[newhead = body[headindex; // old tail now on top of head
headindex = newhead;
// old tail element be
omes head
// move new head in dire
tion of motion.
if(
ommand == 'w')
body[headindex.move(-gridsep, 0);
else if (
ommand == 'n') body[headindex.move(0, -gridsep);
else if (
ommand == 'e') body[headindex.move(+gridsep, 0);
else if (
ommand == 's') body[headindex.move(0, +gridsep);
};
dir = ommand;
316
argumentless form of the move member fun
tion is
alled, whi
h
auses the snake to
ontinue
in the dire
tion whi
h it was originally moving.
int main(){
initCanvas("Snake", gridsep*npts, gridsep*npts);
Snake s;
while(true){
XEvent event;
if(
he
kEvent(event)){
if(keyPressEvent(event)){
har
=
harFromEvent(event);
s.move(
);
// new dire
tion?
}
}
else s.move();
// keep moving as before.
wait(0.1);
}
}
1. Modify the drawing program dis
ussed in the
hapter so that it \beauties" what the
user draws. Spe
i
ally, if the user draws something that nearly looks like a straight
line, you should draw it as a straight line. Or a
ir
ular ar
. Try to
ome up with some
proto
ols so that the user
an draw beautiful pi
tures without too mu
h eort.
2. Another use of \dragging", in addition to drawing, is to drag obje
ts around on the
s
reen. In parti
ular, the user
an move the
ursor to an obje
t, then
li
k a button,
drag the mouse, and nally release the button (drop). This should
ause the obje
t
to get sele
ted and moved and dropped at the new position. This idea will be useful
in building graphi
al editors, as you must have seen. Modify the drawing program of
the previous exer
ise so that it does not imprint the lines on the
anvas, but merely
shows them by
reating suitable line and
ir
le obje
ts. Keep tra
k of these obje
ts in
suitable arrays. Allow them to be dragged and dropped.
3. Add prizes/food/walls to the snake game. Also make the snake's length in
rease by
one everytime it eats food. Also assign a s
ore to the player depending upon how mu
h
food/prizes the snake has eaten, and even just how long the snake has stayed around.
Display the s
ore suitably.
Chapter 19
Representing variable length entities
We
ontinue with the idea of building
lasses to represent entities that we might want in our
programs. In many
ases, the entities we wish to represent do not have a standard length, e.g.
a pie
e of text su
h as the name of a person. Indeed, human beings have names whi
h
an
be very short or very long. We may also wish to represent entities su
h as polygons, where
the number of verti
es might be dierent, or polynomials, where the number of
oe
ients
might be dierent. We may also be
alled upon to represent graphs (e.g. road networks) or
the set of sets of students in dierent
lasses; in general we might want to represent several
instan
es of ea
h su
h
olle
tions, and the instan
es will typi
ally have dierent lengths.
One natural strategy is to allo
ate the maximum possible length to represent the entity in
question. We used this idea for representing text strings in Se
tion 14.1: if we want to store
names of people, we allo
ated
har arrays of what we supposed was the maximum possible
length. This
learly uses memory ine
iently. People have names of widely varying lengths,
e.g. the a
tor Om Puri and the freedom ghter/s
holar Chakravarti Rajagopala
hari. So in
general we will be for
ed to allo
ate long arrays, but most of the time we will use only small
portions of these.
In this
hapter, we will see how to design a data type for storing text strings, su
h that it
does not waste memory. We
annot dire
tly use stru
tures/
lasses/arrays the way we have
des
ribed them so far. This is be
ause the size of a
lass/stru
ture/array must be xed on
e
for all, typi
ally without the knowledge of the size of the text string to be stored in it. The
most
onvenient way of representing entities whose size is not known when we write the
program is to use the so
alled heap memory allo
ation. This is also referred to as dynami
memory allo
ation. Using this heap memory, we will
onstru
t a String
lass using whi
h
you will be able to store and manipulate text strings e
iently and
onveniently.
In general the heap memory will be useful in building representations for entities whose
sizes may not be known at the time of writing the program, or whose sizes may even vary
over time. We will see examples of su
h representations in the Exer
ises.
The Standard Library of C++
ontains several
lasses whi
h use heap memory to a
ommodate entities of variable sizes. In fa
t one of the
lasses in the library is a string
lass, whi
h
an be
onsidered to be an advan
ed version of the String
lass we dis
uss in
this
hapter. We will study the Standard Library in
luding the string
lass in Chapter 20.
Chapter 20 will not dis
uss how these
lasses are implemented; however the implementation
of the String
lass in this
hapter will provide some
lues.
317
318
So far, for the most part, we have been
onsidering variables that have been allo
ated in
the a
tivation frame of some fun
tion or another. Su
h variables are present only for the
duration in whi
h the
orresponding fun
tion is exe
uting.
However, a C++ program
an also be given memory outside of a
tivation frames. A
ertain region of memory is reserved for this purpose. This region is
alled the heap memory,
or just the heap. You
an request memory from the heap by using the operator new. Suppose
T is a data type su
h that ea
h variable of type T requires s bytes of storage. Then the
expression
1
new T
auses a variable of type T, or in other words s bytes of memory, to be allo
ated in the
heap, and the expression itself evaluates to the address of the allo
ated variable. To use this
allo
ated variable, you must save the address { this you
an do typi
ally by storing it in a
variable of type pointer to T. More generally, we may write
new
all-to-a-
onstru
tor-for-T
This will not just
reate a variable of type T, but it will also be initialized using the given
onstru
tor.
Thus, for the Book type as dened in Se
tion 15.1, we
ould write:
Book *p;
p = new Book;
The rst statement de
lares p to be of type pointer to Book. The se
ond statement requests
allo
ation of memory from the heap for storing a Book variable. The address of the allo
ated
memory is pla
ed in p. We
ould of
ourse have done this in a single statement if we wish,
by writing Book *p = new Book;. The memory allo
ated
an be used by dereferen
ing the
pointer p, i.e. we may write
p->pri
e = 335.00;
p->a
essionno = 12345;
whi
h will allo
ate memory in the heap for storing an array of n elements of type T, and the
address of the allo
ated array would be pla
ed in q. We
an a
ess elements of the array
starting at q by using the [ operator as dis
ussed in Se
tion 13.3.3. Thus we
ould write
q[i where i must be between 0 and n (ex
lusive). Note that T
ould be a fundamental
data type, or a
lass. If it is a
lass, ea
h obje
t T[i would be
onstru
ted by
alling the
Other than this, there are the global variables. They have to be essentially allo
ated before the program
begins exe
ution, and hen
e are not interesting for the purpose of this dis
ussion.
1
319
onstru
tor whi
h does not take any arguments. You must ensure that su
h a
onstru
tor is
available.
Note that allo
ating memory in this manner is a somewhat involved operation. There is
some bookkeeping needed to be done so that subsequently the same memory is not allo
ated
for another request, until we expli
itly free the memory. We
an free memory, i.e. return it
ba
k to the heap by using the operator delete. Thus, we might write:
delete p;
Assuming p pointed to memory allo
ated as above, delete p; would
ause the memory to
be returned ba
k, i.e. somewhere it would be noted that the memory starting at p is now
free and may be allo
ated for future requests. The delete[ operator is used if an array
was allo
ated. So for q as dened earlier, we may write:
delete[ q;
Note that on
e we exe
ute delete (or delete[) it is in
orre
t to a
ess the
orresponding
address; it is almost akin to entering a house we have sold just be
ause we know its address
and perhaps have a key to it. It does not belong to us! Someone else might have moved in
there, i.e. the allo
ator might have allo
ated that memory for another request. A
essing
su
h a pointer is said to
ause a dangling pointer error.
We used the phrase allo
ator above. By this we mean the set of (internal) fun
tions and
asso
iated data that C++ maintains to manage heap memory. These are the fun
tions that
get
alled (behind the s
enes, so to say) when you ask for memory using the new operator
and release memory using the delete operator.
19.1.1 A detailed example
We present a detailed example of how heap memory might get allo
ated during exe
ution.
Consider the program of Figure 19.1 (a). We will assume for sake of deniteness that
the heap starts at address 24000. When the exe
ution starts, all the memory in the heap is
available.
When the rst statement, int* intptr = new int; is exe
uted, memory to store a
single int is given from the beginning of the heap. Sin
e an int requires 4 bytes, the 4 bytes
with address 24000 to 24003 are reserved, and the address of the rst of these bytes, 24000,
is returned and stored in intptr. Next, memory for an array of 3
hara
ters is requested.
For this the next 3 bytes are reserved, starting at 24004. Thus
ptr gets the value 24004.
The following statement *intptr = 279; stores the number 279 into the allo
ated memory pointed to by intptr, i.e. at address 24000. The next 3 statements store the
hara
ter
string
onstant "ab" into the array pointed to by
ptr. At this stage of the exe
ution, the
memory asso
iated with the program is in two parts: the a
tivation frame whi
h
ontains
the variables intptr and
ptr, and the memory whi
h has been allo
ated in the heap.
Figure 19.1(b) shows the a
tivation frame. The heap is shown in Figure 19.1(
).
320
int main(){
int* intptr
har*
ptr
*intptr
ptr[0
ptr[1
ptr[2
}
=
=
=
=
=
=
(a)
AF of
main()
intptr : 24000
ptr : 24004
(b)
new int;
new
har[3;
279;
'a';
'b';
'\0';
Heap memory
Address Content
24000
24001
279
24002
24003
24004
'a'
24005
'b'
24006
0
24007
24008
24009
24010
24011
24012
:::
(
)
Figure 19.1: (a) Program, (b) A
tivation Frame at end, (
) Heap area at end
19.1.2 Lifetime and a
essibility
We have said earlier that if a variable is
reated in the a
tivation frame, then it is destroyed
as soon as the
ontrol exits from the
on
erned fun
tion. In fa
t, the rule is more stringent:
a variable is destroyed as soon as
ontrol leaves the blo
k in whi
h the variable is
reated.
Variables
reated in the heap are dierent. Exiting from a blo
k, or returning from
fun
tions does not
ause them to be destroyed: they
an only be destroyed by exe
uting the
delete operations.
The se
ond point
on
erns how the variables on the heap are a
essed. They are not
given a name, but are a
essible only through its address! So it is vital that we do not lose
the address. Thus we must not overwrite the pointer
ontaining the address of a variable
allo
ated in the heap, unless we stored the address in some other pointer as well. If we do
overwrite a pointer
ontaining the address of a heap variable, and there is no other
opy,
then we
an no longer a
ess the memory area whi
h has been given to us. The memory
area has now be
ome
ompletely useless. This is te
hni
ally
alled a memory leak. We must
not let memory leak, we must instead return it using the delete operator so that it
an be
reused!
int main(){
String a,b;
a = "pqr";
b = a;
String
= a + b;
.print();
String d[2;
d[0 = "xyz";
d[1 = d[0 +
;
d[1.print();
321
// should
on
atenate a, b.
// should print on s
reen
// array of 2 strings
Other operations might also be desirable for this
lass, we dis
uss those later.
19.2.1 The basi
storage ideas
Here is the de
laration of a
lass String whi
h will enable us to write the main program
above.
lass String{
har* ptr; // will point to address in heap where a
tual text is stored.
publi
:
String();
void print();
void operator=(
har* rhs);
void operator=(String rhs);
String operator+(String rhs);
};
The
lass
ontains just one data member, ptr whi
h is meant to point to the starting address
in the heap memory where the text asso
iated with the variable is stored. Spe
i
ally, we
will store the text, terminated by a null
hara
ter (i.e. 'n0') in the heap memory. This way,
we will not need to store the length of the allo
ated region expli
itly. Further, if a String
variable
ontains the empty string, we will set its member ptr to NULL.
In prin
iple, if two String variables have the same value, i.e.
ontain the same text, then
potentially we
an store a single
opy of that text in the heap, and have the ptr members
of both the variables point to that
opy. While this sharing will likely save memory, it will
also
ompli
ate the logi
we will need to use to ask for and release heap memory. So we will
adopt the simpler idea: the text asso
iated with every variable will be stored in a distin
t
area in the heap memory.
19.2.2 Constru
tor
Initially, when we
reate a string variable, we want it to hold the empty string. Hen
e the
onstru
tor is as follows.
322
String::String(){
ptr = NULL;
}
Sin
e ptr gives the address from where the string is stored, it su
es to write
out << ptr
<< endl;. However, if ptr is NULL, then we
annot print it, instead we must expli
itly print
out "NULL".
19.2.4 Assignments
We dis
ussed in Chapter 15 that assignment is already dened for stru
ture types, provided
the right hand side of the assignment is also a stru
ture of the same type as the left hand
side. Su
h a statement exe
utes by
opying ea
h data member of the right hand side to the
orresponding member of the left hand side. We will see that this is not adequate for our
purpose. In addition, our String data type allows the right hand side to be of type
har*.
This we will have to dene afresh. This is what we
onsider rst.
As dis
ussed in Se
tion 16.6, we
an dene a member fun
tion operator= to spe
ify how
assignment should work. Sin
e the right hand side is to be of type
har*, this member
fun
tion must have a
har* parameter. In the body of the fun
tion we des
ribe what we
want to happen to exe
ute the assignment. We
an dene this as follows.
void String::operator=(
har *rhs){
delete [ ptr;
ptr = new
har[length(rhs) + 1;
str
py(ptr,rhs);
}
We give an example to see how this will work. Suppose z is of type String and say we have
a statement
z = "mno";
This statement will
ause the member fun
tion operator= to exe
ute, with the variable z
being the re
eiver, and the parameter rhs being the address of the text string "mno".
Note that the variable z may already
ontain some value before
ontrol arrives at the
assignment statement, say the variable z
ontains the text "pqr". In this
ase, before our
assignment statement, z.ptr will already be pointing to a heap region storing "pqr". When
we set z.ptr to point to the area storing "mno", the area
ontaining "pqr" will no longer be
323
needed, and hen
e
an be deleted. This is what the rst statement in the fun
tion does.
After that we request memory from the heap enough to store the new value, i.e. as many
bytes as the number of
hara
ters in rhs plus an extra byte to store the null
hara
ter.
For this, we have used the length fun
tion from Se
tion 14.1.3. After that we
opy the
text pointed to by rhs into the new region. In this we have used the fun
tion str
py from
Se
tion 14.1.3.
Next we
onsider assigning one string to another as in the statement b = a; to exe
ute,
we need to do something mu
h like above. The only dieren
e is that the right hand side of
the assignment is a String rather than a
har*. The text that is needed to be
opied now
omes from taking the ptr member of the right hand side, rather than taking the right hand
side itself dire
tly.
void String::operator=(String rhs){
delete [ ptr;
ptr = new
har[length(rhs.ptr) + 1;
str
py(ptr,rhs.ptr);
}
The result will be
al
ulated as the String res. It has to hold text of length equal to the
sum of the texts of the left hand side, i.e. the text in the impli
it argument, of length
length(ptr), and the text in the rhs argument, of length length(rhs.ptr), and an extra
byte to hold the null
hara
ter. So the se
ond statement requests memory of this size in
the heap. Then the text in the impli
it argument is
opied into res.ptr using the fun
tion
str
py. The se
ond str
py
all above assumes that there exists a str
py fun
tion taking
3 arguments as follows.
void str
py(
har destination[,
har sour
e[, int dstart=0){
int i;
for(i=0; sour
e[i != '\0'; i++)
destination[dstart+i=sour
e[i;
destination[dstart+i=sour
e[i;
//
opy the '\0' itself
}
324
As you
an see this will
opy the sour
e string to the destination starting at index dstart,
This nishes the denition of the String
lass. With this the main program given in the
beginning
an be exe
uted.
The String
lass as dened in the previous se
tion is enough for the main program given
at the beginning of the se
tion (Se
tion 19.2). However, the denition will not allow us to
do many other operations that we might want, e.g. pass String obje
ts as arguments to
fun
tions by value, or return String obje
ts as results. How to enable these operations is
the subje
t of this se
tion.
We begin by xing a short
oming of the existing denition of String. Turns out that
we will have a memory leak if we allo
ate a String variable inside a blo
k:
{
}
String s;
s = "pqr";
This is be
ause when the blo
k ends, the obje
t s will be deallo
ated from the
urrent
a
tivation frame. As a result we will no longer be able to use the memory pointed to by
s.ptr. So ideally we should delete[ that memory at the end of every blo
k. We
ould
do this by writing the statement delete[ s.ptr; before the end of the blo
k. Having to
write this ourselves for every su
h variable and every su
h blo
k is in
onvenient and error
prone (we might forget). But also note that ptr is a private member, so to delete it from
outside the
lass denition we will need some further modi
ation to our
lass denition.
This is where destru
tors
ome in handy.
19.3.1 Destru
tors
A String obje
t will be deallo
ated when
ontrol exits the blo
k in whi
h it is dened. This
is ne, however, we would like the memory pointed to by the member ptr to also be returned
ba
k to the heap when the obje
t is deallo
ated. We
an do this by dening a destru
tor for
String.
String::~String(){
delete[ ptr;
}
C++ will
all the destru
tor on any variable that is about to be dello
ated, and a
tually
deallo
ate it only after the destru
tor exe
ution nishes. When the destru
tor above is
alled, it will return the memory pointed to by ptr ba
k to the heap, as we wish.
Remember that the destru
tor
all happens impli
itly. So you should never expli
itly
all
the destru
tor be
ause then it will end up being
alled twi
e, with ptr being deleted twi
e,
whi
h is erroneous.
Note that if we do not supply a destru
tor, C++ itself denes a destru
tor that does
nothing. In this
ase, the memory pointed to by ptr will not be returned to the heap, thus
ausing a memory leak.
325
The
opy
onstru
tor is essentially like the assignment operator; however sin
e the left hand
side is just being
onstru
ted, it
an be simpler than an assignment operator. When assigning
a String x to String y, i.e. for y = x, we need to perform delete[ on y be
ause what it
points to will no longer be needed. However, if y is just being
onstru
ted, then we know that
its ptr member does not point to anything yet. So a delete operation is not needed. Hen
e
the above
ode does not
ontain a delete[ ptr operation while the assignment operator
of Se
tion 19.2.4 does.
Note that the parameter for the
opy
onstru
tor must be passed by referen
e, the reason
for this will be
ome
lear shortly. Another important point is that it is most natural that
when you
opy an existing obje
t to
onstru
t a new obje
t, you will likely not modify the
existing obje
t. So it is appropriate to make the
onstru
tor parameter
onst.
A
opy
onstru
tor
an be
alled expli
itly by the user; however there are 3 situations
when it is used by the
ompiler.
1. When you dene an obje
t and assign another obje
t to it at the time of denition,
e.g. if you write:
String s = "ab
";
String t = s;
Then to
opy s into t the
opy
onstru
tor is used, and not the assignment operator,
i.e. member fun
tion operator=.
2. When an obje
t is passed to a fun
tion by value.
3. When an obje
t is returned from a fun
tion.
Thus by dening the
opy
onstru
tor yourself, you
an
ontrol how the three operations
above happen! If you do not dene a
opy
onstru
tor, C++ supplies you one, and that
merely
opies members. Clearly, su
h a default
opy
onstru
tor will not be appropriate for
String.
You will now see that it is not appropriate to pass the argument to a
opy
onstru
tor
by value. If you indeed pass it by value, then it would have to be rst
opied, but for that
you would have to invoke the
opy
onstru
tor, and so on. Thus we would have innite
re
ursion. In fa
t if you write a
opy
onstru
tor whi
h takes an argument by value, C++
will
ag it as an error.
326
// returning a referen e.
Note that we are returning a referen
e to ptr[i, not the value of ptr[i. Thus we
an use
it on the left hand side of the assignment statement as well.
19.3.4 An improved assignment operator
We
an improve our assignment operator slightly to allow multiple assignments in the same
statement, i.e. allow us to write something like
String s,t,u;
s = t = u = "ab
";
For this to happen, we merely have to return a referen
e to the left hand side. Thus a ni
er
denition of the assignment operator is as follows.
String& String::operator=(
onst String &rhs){
delete [ ptr;
ptr = new
har[length(rhs.ptr) + 1;
str
py(ptr,rhs.ptr);
return *this;
}
a = "PQR";
b = a;
String
= a + b;
.print();
327
// should
on
atenate a, b.
// should print on s
reen
String d[2;
// array of 2 strings
d[0 = "Xyz";
d[1 = l
ase(d[0 +
);
d[1.print();
d[1[2 = d[0[1;
d[1.print();
This will rst print
, whi
h will have the value "PQRPQR". The se
ond print statement will
on
atenate "Xyz" and "PQRPQR" and then
onvert it all to lower
ase. Thus "xyzpqrpqr"
will get printed. After that we will set the
hara
ter at index 2 of d[1 to the
hara
ter at
index 1 of d[0. Thus the last print statement will print "xyypqrpqr".
19.4 Remarks
We have shown how we
an dene a data type String to store
hara
ter strings. We showed
that the denition was good enough to allow
reating strings, indexing into them,
on
atenating them, assigning to strings, passing strings to fun
tions, and returning them from
fun
tions. Ee
tively, using our denition, we have an illusion that String is a fundamental data type. Our implementation guarantees that the obje
ts we
reate will use memory
e
iently.
There is, however, one operation that we should not perform on the String
lass. This
is the operation of allo
ating a String obje
t itself in heap memory. Thus we should not
write something like
2
If this is inside a blo
k, then on exit from the blo
k the variable ptr will get deallo
ated.
As a result, the memory area it points to will leak away.
The key point is that the way the String
lass is dened, you will not need to worry
about allo
ating memory. Indeed, you
an use the String
lass without having to know
about the heap. You are not expe
ted to worry about managing the heap; memory will
get allo
ated for you when needed, it will also get deallo
ated when needed. All this will
happen behind the s
enes. You are expe
ted to sit ba
k and enjoy the
onvenien
e, without
interfering in the memory management.
19.4.1 Class invariants
While designing String, we made some important de
isions early on. In parti
ular we said
that there will be a separate
opy in the heap memory of the value stored in ea
h String
Ex
ept that if two variables of type String have the same value, we will keep two
opies of the value.
This
an be improved upon, as dis
ussed in Appendix B.
2
328
lass String{
har* ptr; // will point to address in heap where a
tual text is stored.
publi
:
String(){ ptr = NULL; }
String(
onst String &rhs){
ptr = new
har[length(rhs.ptr)+1;
str
py(ptr,rhs.ptr);
}
String& operator=(
onst
har* rhs){
delete [ ptr;
ptr = new
har[length(rhs) + 1;
str
py(ptr,rhs);
return *this;
}
String& operator=(
onst String &rhs){
delete [ ptr;
ptr = new
har[length(rhs.ptr) + 1;
str
py(ptr,rhs.ptr);
return *this;
}
String operator+(String rhs){;
String res;
res.ptr = new
har[length(ptr) + length(rhs.ptr) + 1;
str
py(res.ptr, ptr);
str
py(res.ptr, rhs.ptr, length(ptr));
return res;
}
void print(){
if(ptr != NULL)
out << ptr << endl;
else
out << "NULL" << endl;
}
int size(){return length(ptr);}
har& operator[(int i){return ptr[i;}
};
329
obje
t. Su
h a property that the members of a
lass possess throughout their lifetime, is
sometimes
alled a
lass invariant. It is useful to
learly write down su
h invariants, as you
have seen, they guide the implementation of the
lass.
The possible errors are: memory leaks, dangling pointers (a
essing memory that was
allo
ated to us earlier but has sin
e been deallo
ated), and referring to uninitialized
variables.
2. Suppose you have a le that
ontains some unknown number of numbers. You want to
read it into memory and print it out in the sorted order. Develop an extensible array
data type into whi
h you
an read in values. Basi
ally, the real array should be on the
heap, pointed to by a member of your stru
ture. If the array be
omes full, you should
allo
ate a bigger array. Be sure to return the old unused portion ba
k to the heap.
Write
opy
onstru
tors et
. so that the array will not have leaks et
.
3. Dene a
lass for representing polynomials. In
lude member fun
tions for addition,
subtra
tion, and multipli
ation of polynomials. Ensure that your implementation
obeys a
lass invariant in the style of Se
tion ??.
4. Dene the modulo operator % for polynomials. Suppose S (x); T (x) are polynomials,
then in the simplest denition, the remainder S (x) mod T (x) is that polynomial R(x)
of degree smaller than T (x) su
h that S (x) = T (x)Q(x) + R(x) where Q(x) is some
polynomial.
The main motivation for writing the modulo operator is to use it for GCD
omputation
later. So it is important to make sure that there are no round-o errors as would happen
if you divide. One way around this is to dene the remainder S (x) mod T (x) to be
any kR(x) where k is any number, where R(x) is as dened above. Assuming that the
oe
ients of the polynomials are integers to begin with, you should now be able to
ompute a remainder polynomial without division. Hen
e there will be no round o
either. Of
ourse this has the drawba
k that the
oe
ients will keep getting larger.
For simpli
ity ignore this drawba
k.
330
5. In this assignment you are to write a
lass using whi
h you
an represent and manipulate sets of non-negative integers. Spe
i
ally, you should have member fun
tions
whi
h will (a) enable a set to be read from the keyboard, (b)
onstru
t the union of
of two sets, (
)
onstru
t the interse
tion of two sets, (d) determine if a given integer
is in a given set, (e) print a given set. Use an array to store the elements in the set.
Do not store the same number twi
e. With your fun
tions it should be possible to run
the following main program.
main(){
Set a,b;
a.read();
b.read();
set
= union(a,b);
set d = interse
tion(a,b);
int x;
in >> x;
bool both = belongs(x,d);
bool none = !belongs(x,
);
if( both ) {
out << x << " is in the interse
tion ";
.print();
}
else if (none)
out << x << " is in neither set." << endl;
else
out << x << " is in one of the sets." << endl;
The fun
tion Set::read will be very similar/identi
al to Poly::read. For the rest,
ensure that you allo
ate arrays of just the right size by rst determining the size of the
union/interse
tion.
6. Eu
lid's GCD algorithm works for nding the GCD of polynomials as well. Write the
ode for this, using the iterative expression as well as the re
ursive expression. Will
both versions
ause the same number of heap memory allo
ations? Whi
h one will be
better if any?
7. Consider the following new member fun
tion for the
lass Poly:
void move(Poly &dest);
331
new memory while implementing move in order to make sure that the
lass invariant
holds?
See if the Poly
lass with the new move fun
tion will improve the GCD programs
onsidered earlier.
8. Templetize the g
d fun
tion so that it
an work with ordinary numbers as well as polynomials. You will have to dene a few more member fun
tions as well as a
onstru
tor.
Note that int is a
onstru
tor for the int type, i.e. int(1234) returns the integer
1234.
9. Consider the
lass dened as follows.
onst int QUEUESIZE = 10;
lass Queue{
int front;
int nWaiting;
int elements[QUEUESIZE;
Queue(){front = nWaiting = 0;}
Queue(Queue &q){}
Queue & operator= (Queue & other){ return *this; }
publi
:
stati
Queue*
reate(){ return new Queue(); }
bool insert(int value){
if(nWaiting == QUEUESIZE) return false; // queue is full
elements[(front + nWaiting) % QUEUESIZE = value;
nWaiting++;
return true;
}
int remove(){
if(nWaiting == 0) return -1; // queue is empty
int item = elements[front;
front = (front + 1) % QUEUESIZE;
nWaiting--;
return item;
}
~Queue(){if (nWaiting > 0)
out << "Non empty queue destroyed.\n";}
};
This denition of Queue is designed to be used in some unusual ways. State what
operations it allows, and what it does not allow. Dis
uss the rationale for these unusual
features. Rewrite the taxi dispat
h program using it.
Chapter 20
The standard library
An important prin
iple in programming is to not repeat
ode: if a single idea is used repeatedly, write it as a fun
tion or a
lass, and invoke the fun
tion or instantiate a
lass
obje
t instead of repeating the
ode. But we
an do even better: if some ideas are used
outstandingly often, perhaps the language should give us the fun
tions/
lasses already! This
is the motivation behind the development of the standard library, whi
h you get as a part
of any C++ distribution. It is worth understanding the library, be
ause familiarity with it
will obviate the need for a lot of
ode whi
h you might otherwise have written. Also, the
fun
tions and
lasses in the library use the best algorithms, do good memory management
if needed, and have been extensively tested. Thus it is strongly re
ommended that you use
the library whenever possible instead of developing the
ode yourself.
The library is fairly large, and so we will only take a small peek into it to get the
avour.
We will begin with the string
lass, whi
h is very
onvenient for storing text data. This
lass is an advan
ed version of the String
lass of Chapter 19. It is extremely
onvenient,
and you should use it by default instead of using
hara
ter arrays.
Next we will study the template
lasses ve
tor and map whi
h are among the so
alled
ontainer
lasses supported in the library. They
an be used to hold
olle
tions of obje
ts,
just as arrays
an be. Indeed you may think of these
lasses as more
exible, more powerful,
extensions of arrays. We will not dis
uss how any of these
lasses are implemented, although
you
an get some
lues from the dis
ussions in Chapter 19 and Se
tion 22.2. But of
ourse,
as users you only need to know the spe
i
ation of the
lasses, and need not worry about
how they are implemented.
As examples of the use of the standard library, we program variations on the marks
display program of Se
tion 13.2.2. You know enough C++ to solve all these variations,
and you have already solved some of them. However, you will see that using the Standard
Library, you will be able to solve them with mu
h less programming eort.
At the end of the
hapter we will give a qui
k overview of the other
lasses in the standard
library. Of these, we will use the priority queue
lass in Chapter 25.
The string
lass is a very
onvenient
lass for dealing with
har data. It is so
onvenient,
that you are en
ouraged to use the string
lass wherever possible, instead of
har arrays.
332
333
To use the string
lass you need to in
lude the header le <string>, but note that it will be
in
luded automati
ally as a part of <simple
pp>.
We
an
reate string obje
ts p,q,r very simply.
#in
lude <string>
The rst statement will dene variables p, q, r and initialize them respe
tively to "ab
",
"defg" and the empty string respe
tively. The se
ond statement
opies string p to string r.
When you make an assignment, the old value is overwritten. Noti
e that you do not have to
worry about the length of strings, or allo
ate memory expli
itly.
You
an print strings as you might expe
t.
out << p << "," << q << "," << r <<endl;
This will print out the strings separated by
ommas. Reading in is also simple,
in >> p;
will
ause a whitespa
e terminated sequen
e of the typed
hara
ters to be read into p. To
read in a line into a string variable p you
an use
getline(
in, p);
Note that you
annot write
in.getline(p) as you might expe
t from your experien
e with
har* variables. Also see the variation des
ribed an used in Se
tion 20.5.1.
The addition operator is dened to mean
on
atenation for strings. Thus given the
previous denitions of p,q,r you may write
r = p + q;
string s = p + "123";
This will respe
tively set r,s to "ab
defg" and "ab
123". The operator +=
an be used to
append.
You
an write s[i to denote the ith
hara
ter of string s. Member fun
tions size
and length both return the number of
hara
ters in the string. Many other useful member
fun
tions are also dened. Here are some examples.
s[2 = s[3;
out << r.substr(2)
<< s.substr(1,3)
<< endl;
//
//
//
//
int i = p.find("ab");
// find from the beginning
int j = p.find("ab",1);
// find from position 1.
out << i << ", " << j << endl; // will print out 0, 3
Note that if the given string is not found, then the find operation returns the
onstant
string::npos. We
an use this as follows:
334
Finally, we should note that strings have an order dened on them: the lexi
ographi
order,
i.e. the order in whi
h the strings would appear in a di
tionary. One string is
onsidered
< than another if it appears earlier in the lexi
ographi
al order. Thus we may write the
omparison expressions p == q or p < q or p >= q and so on for strings p,q with the
natural interpretation.
20.1.1 Passing strings to fun
tions
Sin
e string is a
lass, we
an pass it to fun
tions using value, in whi
h
ase a new
opy is
passed, or by referen
e, in whi
h
ase the
alled fun
tion operates on the argument itself.
20.1.2 A detailed example
In Se
tion 20.5.1 we will see a detailed example of string manipulation.
The template
lass ve
tor is meant to be a friendlier, more general variation of one dimensional arrays. To use the template
lass ve
tor you need to in
lude a header le:
#in
lude <ve
tor>
A ve
tor
an be
reated by supplying a single template argument, the type of the elements.
For example, we may
reate a ve
tor of int and a ve
tor of float by writing the following.
ve
tor<int> v1;
ve
tor<float> v2;
These ve
tors are empty as
reated, i.e. they
ontain no elements. But other
onstru
tors
are available for
reating ve
tors with a given length, and in addition, a given value. For
example, you might write:
ve
tor<short> v3(10);
// ve
tor of 10 elements, ea
h of type short.
ve
tor<
har> v4(5,'a'); // ve
tor of 5 elements, ea
h set to 'a'.
ve
tor<short> v5(v3);
//
opy of v3.
A ve
tor keeps tra
k of its own size, to know the size you simply use the member fun
tion
size. Thus
v3.size()
335
would evaluate to 10, assuming the denition earlier. You
an a
ess the ith element of a
ve
tor using the subs
ript notation as for arrays. For example, you
ould write
v3[6 = 34;
v4[0 = v4[0 + 1;
The usual rules apply, the index must be between 0 (in
lusive) and the ve
tor size (ex
lusive).
You
an also extend the ve
tor by writing:
v1.push_ba
k(37);
v3.push_ba
k(22);
These statements would respe
tively in
rease the length of v1 to 1, and of v3 to 11. The
argument to the method push ba
k would
onstitute the last element.
A whole bun
h of operations
an be performed on ve
tors. For example, unlike arrays,
you
an assign one ve
tor to another. So if v,w are ve
tors of the same type, then we may
write
v = w;
whi
h would make v be a
opy of the ve
tor w. The old values that were
ontained in v are
forgotten. This happens even if v,w had dierent lengths originally. You should realize that
although the statement looks very small and simple, all the elements are
opied, and hen
e
the time taken will be roughly proportional to the length of w.
You
an shrink a ve
tor by one element by writing v.pop ba
k(). But you
an also set
the size arbitrarily by writing:
v.resize(newSize);
w.resize(newSize,newValue);
The rst statement would merely
hange the size. The se
ond statement would
hange the
size, and if the new size is greater, then the new elements would be assigned the given value.
20.2.1 Inserting and deleting elements
It is possible to insert and delete elements from the middle of a ve
tor. This is dis
ussed in
Se
tion 20.6.2
20.2.2 Index bounds
he
king
Instead of using subs
ripts [ to a
ess elements, you
an use the member fun
tion at. This
will rst
he
k if the index is in the range 0 (in
lusive) to array size (ex
lusive). If the index
is outside the range, the program will halt with an error message. Note that the at fun
tion
an be used on the left as well as the right hand side of assignments.
ve
tor<int> v;
for(int i=0; i<10; i++) v.push_ba
k(i*10);
v.at(0) = v.at(1);
This will
ause the rst element to be
opied to the zeroth, i.e. at the end v will
ontain 10,
10, 20, 30, 40, 50, 60, 70, 80, 90.
336
where V3 is the
lass from Chapter 15, and Cir
le from Chapter 5. You
an also make
ve
tors of pointers.
ve
tor<Cir
le*>
ir
leve
;
// allowed.
337
This simply denes v to be a zero length ve
tor of zero length ve
tors. Noti
e the spa
e
between the two >
hara
ters. Without this spa
e the two >
hara
ters would be interpreted
as the input extra
tion operator >>.
Here is how we might dene a length 10 ve
tor of length 20 ve
tors, i.e. a 10 20 matrix.
ve
tor<ve
tor<int> > w(10, ve
tor<int>(20));
In this we have used the two argument
onstru
tor for ve
tors, the rst argument, 10 spe
ies
the length, and the se
ond element, ve
tor<int>(20) gives the value of ea
h element. But
this value is itself a ve
tor of length 20. Thus we get a 10 by 20 matrix represented.
We
an a
ess the elements of the matrix in the usual manner, i.e. by writing w[i[j.
However, we may also modify whole rows if we wish. Thus for w as dened above, we write:
w[0 = ve
tor<int>(5);
we will
hange w to be
ome a pe
uliar stru
ture: it will have 10 rows; the rst will have 5
elements, and the remaining will
ontinue to have 20 elements.
This
exibility is very useful. Often in s
ienti
omputing, we en
ounter matri
es of
ertain shapes, e.g. lower triangular matri
es. In a lower triangular matrix, all elements
above the main diagonal are 0. Thus we need not even store them. So we
an
reate a
ve
tor of ve
tors in whi
h the ith ve
tor (i starting at 0) having length i+1. This is an easy
exer
ise.
On the one hand, the
exibility des
ribed above is useful, but on the other,
reating a
matrix as dis
ussed above is also a bit verbose. Also, if someone uses a ve
tor of ve
tors in
a program, there is always the suspi
ion that they may be
hanging the sizes of the rows
as des
ribed above. It is easier to understand a program if we are assured that a parti
ular
name always refers to a matrix 10 20 matrix, and that some fun
tion will not suddenly
hange it to be
ome a 5 5 triangular matrix. In other words, we want to signal to the
reader that we are really using only the usual kind of matrix operations, not using all the
ve
tor fun
tions. For this, we
an
reate a matrix
lass.
20.2.6 A matrix
lass
A safe matrix
lass is dened below. It does not allow the size of the individual rows to be
hanged on
e
reated, and only allows a
ess to elements for reading and writing.
lass matrix{
ve
tor<ve
tor<double> > elements;
publi
:
matrix(int m, int n) : elements(m, ve
tor<double>(n)){}
double &operator()(int i, int j){return elements[i[j;}
int nrows(){return elements.size();}
int n
ols(){return elements[0.size();}
};
int main(){
matrix D(10,10);
338
// 10 x 10 matrix
As you
an see we have overloaded the fun
tion
all operator to a
ess the elements. This
is be
ause we need to supply two indi
es, and the indexing operator [
an only take one
index. Thus the fun
tion
all operator is more
onvenient. Also note that as dened, the
default assignment operator is available to the
lass. You
an disable that if you wish by
making it private.
The
lass
an be templatized so as to form a matrix of arbitrary type T rather than a
matrix of type double.
The standard template library
ontains many useful fun
tions whi
h you
an a
ess by
in
luding another header le.
#in
lude <algorithm>
If you in
lude this le, sorting a ve
tor v is easy, you simply write:
sort(v.begin(), v.end());
That's it! This fun
tion will sort the ve
tor v in-pla
e, i.e. the elements in v will be rearranged
so that they appear in non-de
reasing order. The arguments to the sort fun
tion indi
ate
what portion of the array to sort. By writing v.begin() you have indi
ated that the portion
to sort starts at the beginning of v, and v.end() indi
ates that the portion to sort ends at
the end of the ve
tor. In other words, the entire array is to be sorted. The expression
v.begin() evaluates to an iterator. An iterator, whi
h we will dis
uss in Se
tion 20.6, is a
generalization of pointers. The expression v.end() is also an iterator.
The sort fun
tion
an be used to sort arrays of arbitrary
lass. For this we must somehow
spe
ify a
omparison fun
tion, i.e. a fun
tion whi
h takes two obje
ts and says whether the
rst one is smaller than the se
ond. If we provide su
h a fun
tion, then the array
an be
sorted in as
ending order as per the
omparison fun
tion. There are 3 ways in whi
h the
omparison fun
tion
an be spe
ied.
339
First, we
an dene the binary operator < for the
lass (Se
tion 16.4). Thus the sort
fun
tion will
all the member fun
tion operator< to de
ide whether one obje
t is smaller
than another. We will see an example of this in Se
tion 20.4.2.
Se
ond, we
an dene an non-member
omparison fun
tion. This fun
tion must be
passed as an additional argument to the sort fun
tion. We will see an example of this in
Se
tion 20.4.3.
Third, we
an dene a fun
tion obje
t (Se
tion 16.4) whi
h performs the
omparison as
required. This fun
tion obje
t
an then be passed as an additional argument to the sort
fun
tion. Se
tion 20.4.3 gives an example of this also.
1
20.4 Examples
int nextVal;
while(
in >> nextVal)
marks.push_ba
k(nextVal);
sort(marks.begin(), marks.end());
It is assumed that the marks le is redire
ted to the standard input during exe
ution.
20.4.2 Marks display variation 2
Suppose now that our marks le
ontains lines of the form: roll number of the student
earning the marks, followed by the marks. Again, we are not expli
itly given the number of
entries and the goal is to print out the list in a sorted order of the roll numbers.
More a
urately, we pass a pointer to the fun
tion, as dis
ussed in Se
tion 11.7. But note that as
dis
ussed in Se
tion 11.7.1, we
an drop the & operator while referring to fun
tion pointers.
1
340
The natural way to write this program is to use a stru
ture in whi
h to read the roll
number and marks. We would then use a ve
tor of stru
tures. In order to be able to sort
a ve
tor of stru
tures, we simply dene a member fun
tion operator<, whi
h de
ide whi
h
stru
ture is to be
onsidered smaller given two stru
tures. Sin
e we want to order by roll
number, we must
onsider the stru
ture whi
h
ontains the smaller number to be the sorter
stru
ture. That is what the denition of operator< does, in the
ode below. Note an
important point: the sort fun
tion requires that the fun
tion operator< be dened, with
both the re
eiver and the argument being
onst.
stru
t student{
int rollno;
float marks;
bool operator<(
onst student& rhs)
onst{ // used by the sort fun
tion
return rollno < rhs.rollno;
// note the two
onst keywords
}
};
int main(){
ve
tor<student> sve
;
student s;
while(
in >> s.rollno){
in >> s.marks;
sve
.push_ba
k(s);
}
sort(sve
.begin(), sve
.end());
We will shortly see how this
an be used to sort by marks. Note that we
ould have used
a fun
tion obje
t instead of a fun
tion. So for the sake of variety, we will use a fun
tion
obje
t to sort by roll numbers. So we rst dene a stru
t from whi
h to
reate the fun
tion
obje
t.
341
stru
t
ompareRollnoStru
t{
bool operator() (
onst student& a,
onst student& b){
return a.rollno < b.rollno;
}
};
// by marks
// by roll no
In this
ode an extra argument has been passed to both
alls to sort, spe
ifying how the
sorting must happen. The argument in the rst
all is really a fun
tion pointer, but the
& operator is not written out sin
e it
an be dropped (Se
tion 11.7.1). In the se
ond
all,
the last argument is
ompareRollNoStru
t(). This is a
all to the
onstru
tor, so what is
passed is an obje
t of the
lass
ompareRollNoStru
t. This is used to de
ide the sorting
order.
As you
an see, passing a fun
tion or a fun
tion obje
t to sort is more
exible than
dening operator< in the
lass { you
an sort a
ording to dierent orders by passing
dierent fun
tions or fun
tion obje
ts.
The simplest way to think of the map
lass is as a generalization of an array or a ve
tor. In
an array or a ve
tor, the index is required to be an integer between 0 and the n 1 if the
length of the array is n. In a map, this
ondition is severely relaxed: you are allowed to use
any value as the index, it need not even be numeri
al! As in an array, the value of the index
determines whi
h element of the map is being referred to.
To use the map template
lass you need to in
lude the header <map>. Next, you de
lare
the map you want.
map<indexType,valueType> mapname;
This
auses a map named mapname to be
reated. It stores elements of type valueType, whi
h
an be a
essed by supplying indi
es of type indexType. It is required that the operator
342
be dened for the type indexType. Of
ourse, if the operator is not originally
dened, you
an dene it. However the denition should have the usual properties expe
ted
of a
omparison operator, i.e. it should be transitive and asymmetri
.
Let us take a simple example. Suppose we want to store the population of dierent
ountries. Then we
an
reate a map named Population, whi
h will store the population
value (numeri
). Say we store the population in billions as a unit, so our valueType is
double. We would like to use the name of the
ountry to a
ess the element
orresponding
to ea
h
ountry, so our indexType
ould be string. So we
an dene our map as follows.
operator<
map<string,double> Population;
Next we insert the information we want into the map, i.e. we spe
ify the population of
dierent
ountries.
Population["India" = 1.21; // population of India is 1.21 billion
Population["China" = 1.35;
Population["Unites States" = 0.31;
Population["Indonesia" = 0.24;
Population["Brazil" = 0.19;
The rst line, for example,
reates an element whose value is 1.21, and whose index is
"India". You use an array a
ess like syntax also to refer to the
reated elements. For
example, the following
out << Population["Indonesia" << endl;
will print 0.24, whi
h is the value stored in the element whose index is "Indonesia".
You have to realize that while the statements look like array a
esses super
ially, their
implementation will of
ourse be very dierent. Ee
tively, what gets stored when you write
Population["India" = 1.21; is the pair ("India",1.21). The name Population really
refers to a
olle
tion of su
h pairs. Subsequently, when we write Population["India" we
are ee
tively saying: refer to the se
ond element of the pair whose rst element is "India".
So some
ode will have to exe
ute to nd this element (Se
tion 20.5.2). So a lot is happening
behind the s
enes when you use maps.
What if you write two assignments for the same index, e.g.
Population["India" = 1.21;
Population["India" = 1.22;
This will have the ee
t you expe
t: the element
reated the rst time around will be
modied so that the value stored in it will
hange from 1.21 to 1.22.
An important operation you might want to perform on a map is to
he
k if the map
ontains an element with a given index. Suppose you have read in the name of a
ountry
into a string variable
ountry. Say you want to print out the population of that
ountry if
it is present in the map; else you want to print out a message saying that the population of
that
ountry is not known to the program. You
an write this as follows
343
This
ode should immediately follow the
ode given above for dening the map Population
and spe
ifying the population of the various
ountries.
In this
ode the member fun
tion
ount takes as argument an index value, and returns 1 if an element with that index is present in the given map. Thus suppose the
user typed in "India", in response to our request to give the name of a
ountry. Then
Population.
ount(
ountry) would return 1 be
ause we did enter the population of "India"
into the map earlier. So in this
ase the nal value entered, 1.22, will get printed. On the
other hand, if the
ountry typed in was "Britain", then Population.
ount(
ountry)
would return 0, and hen
e the message \Britain not found." would be printed. Another way
of determining whether a map
ontains a
ertain entry is dis
ussed in Se
tion 20.6.1.
You may wonder what would happen if we anyway exe
ute
out << Population["Britain" << endl;
without assigning a value to Population["Britain" earlier in the
ode. The exe
ution of
this statement is somewhat unintuitive. In general suppose we have dened a map
map<X,Y> m;
and suppose x is a value of type X. Then if we a
ess m[x without rst assigning it a
value, then impli
itly this rst
auses the statement m[x=Y(); to be exe
uted, i.e. an
element is
reated for the index x, and the element stores the value Y() obtained by
alling the default
onstru
tor of
lass Y. After that the value of m[x is returned. Thus
in the
ase of the statement
out << Population["Britain" << endl;, the statement
Population["Britain"=double(); is rst exe
uted. The
onstru
tor for the type double
unfortunately does not initialize the value. So the map will now
ontain an element of unknown value but having the index "Britain". Hen
e this unknown value would get printed.
20.5.1 Marks display variation 4
In this, we will have the tea
her enter the names of the student instead of the roll numbers.
We will
onsider the original problem, i.e. students walk up to the
omputer and want
to know their marks. But this time they type in their name rather than the roll number.
Clearly, we
an use strings to represent student names, and a map to store marks of students.
To make the problem more interesting, we will assume that for ea
h student we have the
marks in Mathemati
s, Physi
s, and Sanskrit. Further assume that the names are given in
a le with lines su
h as the following.
A. A. Fair, 85, 95, 80
Vibhavari Shirurkar, 80, 90, 90
Ni
olas Bourbaki, 95, 99, 75
344
i.e. the le will
ontain a line for ea
h student with the name appearing rst, su
eeded
by a
omma, following whi
h 3 numbers would respe
tively give the marks in the dierent
subje
ts. The numbers are also separated by
ommas. This format, in whi
h ea
h line of the
le
ontains values separated by
ommas, is often
alled the CSV format, or the \
omma
separated values" format.
We will use a string to store the student name. To store the marks, we will use a
stru
ture.
stru
t marks{
double s
ien
e, math, sanskrit;
};
The marks will be stored in a map, whose index will be the name of the student given as a
string.
map<string,marks> mark_map;
Say our le
ontaining the marks is named marks.txt. Then we
an de
lare it in our program
as
ifstream infile("marks.txt");
Next we dis
uss how to read values from a le in the CSV format. For this we
an use a
form of getline fun
tion whi
h allows a delimiter
hara
ter to be given. The signature for
this is:
istream& getline(istream& instream, string stringname,
har delim)
In this, instream is the name of the input stream from whi
h data is being read. The
parameter stringname is the name of a string, and delim is the
hara
ter that delimits the
read. Thus, data is read from the stream instream until the
hara
ter delim is found. The
hara
ter delim is dis
arded, and the data read till then is stored into string stringname.
Thus, we
an read the name of a student by exe
uting something like:
string name;
getline(infile,name,',');
Used with the le above, this statement will
ause name to get the value \A. A. Fair",
in
luding the spa
es inside it. Subsequently if we exe
ute
getline(infile,name,',');
again, the string name would then hold the string "85". Of
ourse, we would like to
onvert
this to a double, so we
an use a stringstream (Appendix F).
double mmath;
stringstream (name) >> mmath;
345
This would
ause the string name to be
onverted into a stringstream, from whi
h we read
into the variable mmath. Similarly, the other data
an be read.
Figure 20.1
ontains the entire program based on these ideas. In the rst part, the le is
read into the map mark map. The rst 3 values on ea
h line, the name, the marks in math
and the marks in s
ien
e are
omma separated. So they are used as dis
ussed above. The
last eld is not
omma separated, so it
an be read dire
tly. Note that when reading using
the operator >>, the end of line
hara
ter is not read. So before the next line is to be read,
it must be dis
arded.
In the se
ond part, the program repeatedly reads the names of students. If a name is
present in the map, then the
orresponding marks are printed.
20.5.2 Time to a
ess a map
The (index,value) pairs
onstituting a map are stored using binary sear
h trees (Se
tion 22.2.1).
As will be dis
ussed in Se
tion 22.2.7, making an a
ess su
h as Population[
ountry happens fairly fast, i.e. in time proportional to log n, where n is the number of
ountries for
whi
h data is stored in the map.
2
The
lasses ve
tor and map are
onsidered to be
ontainer
lasses, i.e. they are used to hold
one or more elements. Even a string is thought of as a
ontainer be
ause it
ontains sets of
hara
ters. There are other
ontainers as well in the Standard Library, and we will glan
e
at some of them shortly.
The standard library allows some generi
pro
essing of
ontainers, be they ve
tors, or
maps, or even strings. For this, it is ne
essary to be able to refer to the elements of the
ontainer in a uniform manner. This is a
omplished using an iterator.
An iterator
an be thought of as a generalized pointer to an element in a
ontainer.
It is intended to be used in a manner analogous to the use of an (a
tual) pointer in the
following
ode whi
h applies a fun
tion f to all the elements of an array.
int A[10
int* Aptr
for(Aptr = A; Aptr<A+10; Aptr++)
f(*Aptr);
In this
ode we initialize the (a
tual) pointer Aptr to point to the zeroth element of A, and
then in
rement it so that it points to su
essive elements. In ea
h iteration we dereferen
e
it and then apply the fun
tion f to it. Impli
it in this
ode is the idea that the elements are
ordered in a unique manner: spe
i
ally the elements are
onsidered in the order in whi
h
they are stored in memory.
Now we see how we
an write analogous
ode for
ontainers. Analogous to the a
tual
pointer Aptr, we will have an iterator whi
h will abstra
tly point to elements of the
ontainer,
and whi
h we
an step through as the exe
ution pro
eeds. In general, an iterator for a map
an be dened as follows.
#in
lude
#in
lude
#in
lude
#in
lude
<simple
pp>
<fstream>
<sstream>
<map>
stru
t marks{
double s
ien
e, math, sanskrit;
};
int main(){
ifstream infile("students.txt");
map<string,marks> mark_map;
marks m;
string name;
while(getline(infile,name,',')){
string s;
getline(infile,s,',');
stringstream (s) >> m.math;
getline(infile,s,',');
stringstream (s) >> m.s
ien
e;
infile >> m.sanskrit;
// read dire
tly, not
omma terminated
getline(infile,s);
// dis
ard the end of the line
hara
ter
}
mark_map[name = m;
while(getline(
in,name)){
if(mark_map.
ount(name)>0)
out << mark_map[name.math << " " << mark_map[name.s
ien
e
<< " " << mark_map[name.sanskrit << endl;
else
out << "Invalid name.\n";
}
346
347
map<X,Y> m;
map<X,Y>::iterator mi;
Here mi is the iterator, and its type is map<X,Y>::iterator. Next we need to say how
to set it to \point" to the rst element in the map, and then how to step it through the
elements. For this we rst need to x an ordering of the elements stored in the
ontainer.
For ve
tors and maps, the elements are
onsidered ordered a
ording to the index, i.e. the
rst element is the element with the smallest index. The member fun
tion begin on the
ontainer returns an iterator value that abstra
tly point to this rst element. Thus we
an
initialize our iterator by writing:
mi = m.begin();
An iterator supports two operations: by dereferen
ing you get to the element abstra
tly
pointed to by the iterator, and by using the operator ++, the iterator
an be made to point
to the next element in the order. Finally, to determine when the iterations should stop we
need to know when the iterator has been in
remented beyond the last element in the order.
For this the member fun
tion end on the
ontainer is dened to abstra
tly point beyond the
last element, just as the address A+10 in the example above points beyond the last element
of the array.
Suppose we wish to merely print all the elements in a
ontainer. Then here is how this
an be done using iterators, rst for the
ontainer marks of Se
tion 20.4.1.
for(ve
tor<float>::iterator mi = marks.begin();
mi != marks.end();
++mi)
out << *mi << endl;
The
ode for map
ontainers is similar. When we dereferen
e a map iterator, we get an
element of the map, whi
h is an (index,value) pair. The pair that we get is a (template)
stru
t, with data members first and se
ond whi
h hold the index and the value respe
tively. Sin
e we
onsider an iterator to be a pointer, the stru
t elements
an be a
essed
using the operator ->. Here is how we
an print out the map Population of Se
tion 20.5.
for(map<string,double>::iterator Pi = Population.begin();
Pi != Population.end();
++Pi)
out << Pi->first <<": " << Pi->se
ond << endl;
Similar
ode
an be written for the string
lass. Note that the dereferen
ing operator *
or the in
rementation ++ should not be understood literally, these operators are given to
you appropriately overloaded. But you dont need to worry about all this; you
an
onsider
iterators to be abstra
tions of pointers for the purpose of using them.
348
map<string,int>::iterator Pi = Population.find("Britain");
If "Britain"
"Britain" is
map<string,int>::iterator Pi = Population.find("Britain");
if(Pi != Population.end())
out << Pi->first << " has population "<<Pi->se
ond << endl;
You
an delete the element pointed to by an iterator by using the erase fun
tion as follows.
map<string,double>::iterator Pi = Population.find("Indonesia");
Population.erase(Pi);
// deleting an element
The rst two statements respe
tively de
lare a ve
tor v and set it to
ontain the elements
0, 10, 20, 30, 40, 50, 60, 70, 80, 90. The third statement
auses vi to point to the seventh
element of v, i.e. the element
ontaining 70. Then 100 is inserted at that position, the
elements in the positions seventh onwards being moved down one position. The size of the
ve
tor of
ourse in
reases by one. After that we set vi to point to the fth element. Then
that element is deleted. This
auses the subsequent elements to be moved up one position.
Thus at the end the ve
tor v would
ontain the elements 0, 10, 20, 30, 40, 60, 100, 70, 80,
90.
The standard library has several other
ontainers whi
h are very useful.
For example, the
ontainer deque is a double ended queue, into whi
h you may insert or
remove elements from the front as well as the ba
k. The
ontainer queue allows insertions
at the ba
k and removal from the front, while the
ontainer sta
k requires that insertions
and removals both be done from the same end.
349
An important
ontainer is the priority queue. You
an insert elements arbitrarily, however, when removing elements, you always get the smallest element inserted till then. We
dis
uss and use priority queues in Chapter 25.
An interesting
ontainer is the set. This supports operations for inserting elements and
subsequently nding them. The elements are required to have operator< dened on them,
and this order is used for storing the elements in a binary sear
h tree, just as a map was
stored in a binary sear
h tree. The elements are ordered a
ording to the operator< order
dened on them, and will get printed in this order if printed using iterators as in Se
tion 20.6.
So if we store elements in a set, we really dont need to expli
itly sort them.
This des
ription is of
ourse very sket
hy. You should
onsult various standard library
referen
es on the web to get details.
The typedef statement
an be used to
reate a new name for an existing type.
typedef existingType newName;
So with these denitions, you
an write popType and matrix instead of the longer names,
and save yourself typing and perhaps make your programs more readable.
Of
ourse, the typedef statement is not in any way limited to being used with
ontainer
types from the standard library. It
an be used also for ordinary types.
typedef double mynum;
With this, you
ould use mynum as a synonym for double. This is useful in
ase you de
ide
one day that you really want to represent the numbers in your program using long double.
If you had de
lared them to be of type mynum, then you would only need to make the
hange
in the denition of mynum, rather than
hange the denition of every numeri
al variable in
your program.
20.8.1 More general form
The above type denitions
ould be
onsidered to be only
onvenient, but not providing new
apability. This is be
ause you
ould textually substitute existingType for newTypename.
However, there is a general form whi
h a
tually provides new
apability. The form is:
typedef existingType newTypeExpression;
350
This says that double is the same as the type you get when you dereferen
e something of
type fptrtype, and then apply that to arguments of type double and int. In other words,
fptrtype is of type pointer to fun
tion that takes a double and int as argument and returns
a double. As you
an see, there is no other way to dene the type fptrtype. With this
denition of fptrtype, you
ould dene ph from the end of Se
tion 11.7 as follows.
fptrtype ph;
20.9 Remarks
You have probably guessed by now that the
lasses we dis
ussed in this
hapter would have
to be implemented in the style of the String
lass dis
ussed in Chapter 19. Indeed that is
true. They will use heap memory to store data, and allo
ate and deallo
ate heap memory
when needed.
The important point to note however is that you dont have to worry about the implementation in order to use these
lasses. Indeed the
onstru
tors, destru
tors,
opy
onstru
tors,
assignment operators of these
lasses have already been written, so that there are no memory
leaks, dangling pointers et
. You dont need to worry about memory allo
ation; indeed you
should be able to do everything you want without ever having to use the new operator.
351
As you might guess the
onstru
tor
onstru
ts an LTM matrix with the given number
of rows and
olumns. The member fun
tions return the element at index i,j and
assign the value v to the element at index i,j respe
tively. Note that if j>i then
getElem must return 0. If j>i the setElem must do nothing and print a message.
Give implementations of all the member fun
tions. Of
ourse, it will be mu
h ni
er to
use array indi
es rather than getElem and setElem. The natural indexing operator is
[ { but that
an take only one index. So instead overload the () operator, so that
the fun
tion arguments
an be indi
es. Return a referen
e so that you
an use assign
to array elements as well as read array elements.
6. Write a program that will re
eive information about the states of India and their
apitals and answer questions about these when asked. Spe
i
ally it should pro
ess
3 kinds of
ommands. The rst kind is:
Learn state
apital
As an example, the user may type Learn Maharashtra Mumbai. In this
ase this
information must be remembered by the program. The se
ond kind of
ommand is
Tell
apital-or-state-name
For example, the user may type Tell Gandhinagar, whereupon the program must
respond that it is the
apital of Gujarat. Likewise if the state is given its
apital must
be given in response. The third kind of
ommand is just
Exit
352
10. The algorithm
olle
tion in standard library also
ontains a binary sear
h fun
tion
for performing binary sear
h on sorted
ontainers su
h as ve
tors. The signature of
this fun
tion is
bool binary_sear
h(ForwardIterator first, forwardIterator last,
onst T& value_to_sear
h);
Here the region of the
ontainer between first (in
lusive) and last (ex
lusive) is
sear
hed to nd an element equal to value to sear
h. The type of the element stored
in the
ontainer must be T. An additional argument, a fun
tion obje
t is also allowed.
The fun
tion obje
t must implement the < operator for obje
ts of type T (Se
tion 20.3).
Use this to implement variation 4 of the marks display program, using just ve
tors
rather than maps.
The fun
tion binary sear
h is guaranteed to exe
ute in logarithmi
time when used
with ve
tor
ontainers.
11. Write a program whi
h implements a di
tionary of the English language. A natural
representation would be a map, with the words being the indi
es and the meanings
being the values. This will be suitable for exa
t look ups. However, suppose we wish
to nd approximate mat
hes too. This is be
ause we will typi
ally only store the root
words in the di
tionary, e.g. the word \di
tionary", but not the words obtained from
the root by in
e
tion, e.g. the word \di
tionaries". In su
h
ases, when you look up
\di
tionaries", you would like to get the word that has the longest prex mat
h, whi
h
will likely be \di
tionary" in this
ase. After that your program
ould de
ide whether
the word you found
an indeed be in
e
ted to give the word you are looking for. For
su
h pro
essing, perhaps a simple (sorted) ve
tor might be a better representation than
a map. In any
ase, write a program whi
h not only tells you whether the given word is
in the di
tionary, but also whether it is likely an in
e
tion of a word in the di
tionary.
Chapter 21
Representing networks of entities
Many real life systems
an be
onsidered to be
olle
tions of entities whi
h are somehow
linked together into a network. For example, a
ir
uit
onsists of
omponents
onne
ted
together by wires. Roads
onne
t
ities. When you browse the internet, you
an go from
one page to another by
li
king on a link, a link in the page thus
onne
ts the page to other
pages. Or the entities might be people, with individuals linked to one another if they are
friends. This
hapter will be an introdu
tion to
omputations relating to su
h networks,
whi
h we will loosely dene as
olle
tions of entities along with the
onne
tions between
them.
There may be several questions we
ould ask about su
h networks. For a
ir
uit, we
might want to know the
urrents and voltages in the dierent
omponents. In a road map
we might want to know the shortest path to go from one
ity to another. We might want
to determine the importan
e of ea
h page on the internet. Sea
h engines have to routinely
answer this question when they have to show results of a web sear
h { the more important
pages must be listed before the less important ones. In a network of friends, you might
perhaps want to know who has the largest number of friends, or whether two individuals
have a mutual friend. Some su
h questions require substantial mathemati
al and
omputer
s
ien
e ideas whi
h are outside the s
ope of this book.
However, it is possible to get some sense of how to represent su
h networks on a
omputer,
and answer at least some simple questions about them. That is what we hope to a
omplish
in this
hapter. Our dis
ussion will be based on examples, but it is hoped that you will get
some understanding of how to represent networks in general and perhaps do some elementary
pro
essing with them.
The basi
mathemati
al model used to represent networks is a graph. We begin by
dis
ussing the
ommon representations used for representing graphs. We then dis
uss spe
i
networks and operations on them. In Chapter 22 we will see additional examples of linked
stru
tures, and how they
an be pro
essed.
21.1 Graphs
A graph, as you might know,
onsists of two sets, V , a set of verti
es, and E , a set of edges.
Ea
h edge is a pair (u; v), where u; v 2 E . An edge (u; v) is said to
onne
t the verti
es u; v.
The edges may be dire
ted or undire
ted,
orrespondingly, we may
onsider the pairs (u; v)
353
354
=
=
=
=
=
"Harry";
"Hermione";
"Ron";
"Dra
o";
"Crabbe";
Now to make Harry and Hermione friends of ea
h other, we merely have to add an undire
ted
edge. As we have said, an undire
ted edge
orresponds to pointers between the
orresponding
entities. Thus we must add a pointer to persons[1 in persons[0.friends, and vi
e versa.
For this we will write a fun
tion.
355
void makefriends(Person &p, Person &q){ // add an undire
ted edge in the graph
p.friends.push_ba
k(&q);
q.friends.push_ba
k(&p);
}
This
an be
alled to add the required friendship edge, and others too.
makefriends(persons[0,
makefriends(persons[2,
makefriends(persons[0,
makefriends(persons[3,
persons[1);
persons[1);
persons[2);
persons[4);
Now if we want to print the friends of Hermione (stored in persons[1), we merely write:
for(unsigned i=0; i<persons[1.friends.size(); i++)
out << persons[1.friends[i->name << endl;
21.1.2 Extensions
Ea
h person need not have links only to his/her friends. Suppose we also want to have
links to enemies. In that
ase we merely add another data member enemies of type
ve
tor<Person*> to the Person
lass. Suppose that some of the persons have a favourite
friend. Representing this is even simpler. Sin
e we know that there is at most one favourite,
we just add a data member favourite of type Person*. Thus our denition now be
omes:
stru
t Person{
string name;
ve
tor<Person*> friends, enemies;
Person* favourite;
}
Given our pre
eding denitions, we
an make Ron be Harry's favourite friend by writing
persons[0.favourite = &persons[2;
On the other hand, if we wanted to indi
ate that Harry has no favourite we write
persons[0.favourite = NULL:
As you might remember, NULL is a spe
ial value, whi
h indi
ates that no real pointer is
intended.
21.1.3 Array indi
es rather than pointers
If we know that all the entities in our network will belong to a single array, e.g. the array
persons as above, then an alternate representation is possible. Instead of storing a pointer
to an obje
t, we
an store the index of the obje
t in the array. Thus our denition of person
would
hange as:
356
stru
t Person{
string name;
ve
tor<int> friends;
};
Adding an edge between Harry and Hermione would have to be done as:
persons[0.friends.push_ba
k(1);
persons[1.friends.push_ba k(0);
You may re
all that we have used a similar idea in Se
tion ??.
21.1.4 Edges represented expli
itly
Suppose we wish to represent the network of roads between
ities in a
ountry.. The
ities will
be the verti
es in this network, and the roads themselves will be the edges. Sin
e inter
ity
roads are typi
ally two way, we will use two edges for ea
h road, one in ea
h dire
tion. We
ould make roads be pointers from one interse
tion to another; but we might also want to
store the names of roads, and their lengths. So it is
onvenient to have obje
ts to represent
roads too.
A
lass for representing interse
tions
ould be denes as follows.
stru
t Road; // forward de
laration so that we
an write Road* below.
stru
t City{
ve
tor<Road*> roads;
}
A road obje
t must store the interse
tion it leads to; optionally also the interse
tion it starts
from, and say its name and length.
stru
t Road{
string name;
City* from, to;
double length;
}
Graphs
an also be represented using a so
alled adja
en
y matrix. If the graph has n
verti
es, an n n matrix A is used, with entry Aij giving information about the edge from
vertex i to vertex j , if any. For example, we might set Aij = 1 to indi
ate that an edge
357
is present, and Aij = 0 to indi
ate that there is no edge. Other values
an also be used,
depending upon the
ontext, as we will see later.
The main drawba
k of the adja
en
y matrix representation is the large memory required.
An adja
en
y matrix has n elements, and thus memory for them will be needed no matter
how many a
tual edges there are. If we use an adja
en
y list representation, then in ea
h
vertex obje
t we will use just the memory needed to represent the edges in
ident on that
vertex. Thus the total memory used for edge representation is proportional to the number
of edges. If a graph has only a few edges, then the adja
en
y list representation saves on
memory.
However, there are advantages too for adja
en
y matri
es. The most obvious advantage
is that we
an very easily
he
k whether an edge from vertex i to vertex j exists: we simply
examine the value of Aij . Also, as we will see shortly, in many appli
ations, the adja
en
y
matrix
an be dire
tly used in operations su
h as matrix multipli
ation, solving a system of
equations, and so on. So in su
h appli
ations, the adja
en
y matrix representation is very
onvenient.
In the exer
ises, you are asked to develop a sparse matrix representation: a
lass that
behaves like a matrix but uses a smaller amount of storage. With this you
an sometimes
get the best of both worlds.
In the following se
tions we will see examples of the adja
en
y matrix representation.
2
358
20
23
24
21
22
The probability of being at a page
an be high even if a page has few in
oming links, if those in
oming
links
ome from pages
whi
h themselves have a large number of in
oming links. We
an view this
situation as:
are primarily important be
ause there are many in
oming links, but is se
ondarily
important be
ause it has in
oming links from important pages! This is the intuition for
onsidering the
probability as being indi
ative of the importan
e.
1
q; r; : : :
q; r; : : :
359
4
1
3
2
We have already seen the
ode for multiplying a matrix by a
olumn ve
tor; the
ode for
multiplying a row ve
tor by a matrix is very similar. This is all that is needed!
So suppose now that we are at page 0. This
orresponds to x being the ve
tor (1; 0; 0; 0; 0).
We
an use the above method to nd the probabilities of visiting dierent pages as the time
in
reases. Indeed, if you
ompute xT , we get
x (0:3; 0:3; 0:15; 0:225; 0:025)
You will see that the ve
tor does not
hange mu
h with subsequent multipli
ations. Thus
from this
al
ulation it would seem that pages 0,1 are most important, and page 4 the least.
10
We will
onsider the problem of nding the
urrents and voltages at dierent points in a
ir
uit
onsisting of resistors and
urrent sour
es, su
h as the one shown in Figure 21.2. You
may perhaps not be familiar with
urrent sour
es, the spe
ied
urrent
ows out of them no
matter what they
onne
t to. Cir
uits whi
h
ontain voltage sour
es
an also be analyzed,
but the algebra is slightly more
ompli
ated. We will dis
uss this later.
First we view this
ir
uit as a graph, i.e. identify the verti
es and edges in it. The
resistan
es and the
urrent sour
e
an be
onsidered to be the edges, the points at whi
h
these atta
h to ea
h other
an be
onsidered to be the verti
es. There are 6 nodes in
Figure 21.2, numbered 0 to 5, shown as solid
ir
les. There are 9 edges, one
onsisting of the
voltage sour
e, and 8
onsisting of resistors. It is possible to have
ir
uits in whi
h there are
devi
es whi
h have more than two ele
tri
al
onne
tions to them, su
h as transistors. The
model for su
h graphs will be dierent.
360
4 ohm
3 Ohm
2 Ohm
! Ampere
4
3
5 Ohm
6 Ohm
8 Ohm
7 Ohm
5
0
3 Ohm
361
1
5
1
3
1
5
1
5
1
4
1
4
1
4
1
2
1
3
1
3
1
4
1
3
1
2
1
3
1
2
1
6
1
2
1
6
1
7
1
7
1
3
1
6
0
1
1
3
1
6
1
8
1
8
1
3
1
7
1
7
1
8
2
3
1
8
4
5
362
Often it might be useful to maintain additional data stru
tures when representing networks.
For example, we may wish to qui
kly nd out the obje
t that represents a given person,
given the name of the person. A natural way to do this is using a map, from names to the
obje
ts representing the person.
21.7 Remarks
We have sket
hed some ways of representing networks. However, there
an be others. Espe
ially if the network has some spe
ial stru
ture, then we may be able to assign numbers
to dierent verti
es su
h that from the number of a node it is
lear what other nodes are
onne
ted to it. We will see an example of this in Chapter 26, where we will suitably number
the runways and taxiways so that it is
lear from the numbers what other runways/taxiways
a given runway
onne
ts to.
We should also note that in this
hapter, we have dened all
lasses as stru
ts, i.e. so
that every member is publi
by default. That was only for keeping the
ode short. In a
tual
programming, you should follow the usual rule that data members should be private and
a sele
ted fun
tion members publi
. Indeed, this is what you should do when solving the
Exer
ises.
1. Write a program that
onstru
ts representations about friendship and enemity as dis
ussed in Se
tion 21.1.1. It should take as input a le
ontaining information about
how many persons are there, who are the friends of ea
h person, who are their enemies,
and who are the favourite friends of ea
h person if any.
Write fun
tions to (a) Find whether two given persons have
ommon friends, (b) Find
the the most popular person, i.e. the person who is named as the favourite by the
largest number of persons.
2. Code up the page rank
al
ulation and
he
k if the nal probabilities indeed tend to
the same limit, no matter what initial node you start from.
3. We have noted that many in many natural matri
es, e.g. a transition matrix or a
ondu
tan
e matrix, many elements are 0. To save memory, it is desirable to have a
representation in whi
h we only store the non-zero elements. Of
ourse, the representation should be
apable of listing out all elements of the matrix if needed (whether
they are zero or non-zero), but it should not expli
itly store the elements with value 0.
Instead, if an element is not expli
itly stored, it should be
onsidered to be 0. Devi
e
su
h a representation. Hint: It will ee
tively be the adja
en
y list representation of
the matrix.
363
Write a member fun
tion for the
lass so that you
an perform matrix ve
tor multipli
ation using that matrix.
4. Suppose in Figure 21.2 we have a voltage sour
e of value 1 volt, between verti
es 3 and
4 (with vertex 3
onne
ted to the positive end of the voltage sour
e). Add the relevant
equation into your program and nd the resulting
urrents.
In general extend your program to handle voltage sour
es.
5. Devise graphi
al editors to input ea
h of the networks dis
ussed in this
hapter. For
example to
reate a friendship network, your editor will have a button to
reate a
person. Then additional buttons to
reate friendship links and so on. The editors
should also have buttons whi
h when
li
ked will suitably pro
ess the given network.
Where relevant, devi
e ways to show the result also on the graphi
s
anvas.
Chapter 22
Stru
tural re
ursion
and
n
X
1+
3+
i2 =
5+
7+ 4.
9 + ..
2
n(n + 1)(2n + 1)
6
Both are
orre
t, and the rst one is rather elegant. Our
on
ern in this
hapter, however, is
not the validity or elegan
e of these formulae. Our
on
ern is mu
h more mundane: how do
we layout these formulae on paper. Where do we pla
e the numerator and the denominator,
how long do we make the lines denoting division? What if the denominator is itself a
ompli
ated expression as was the
ase in the
ontinued fra
tion expansion for ? Can we
have a
omputer program do all these
al
ulations for us? While this is somewhat tri
ky,
many programs are indeed available for doing this, the most important amongst these is
perhaps the TEX program developed by Donald Knuth. The program TEX has a language
for spe
ifying mathemati
al formulae, and in this language, the two formulae above
an be
spe
ied as:
i=1
and
\sum_{i=1}^ni^2=\fra
{n(n+1)(2n+1)}{6}
Given this textual des
ription, TEX
an generate layouts like the ones shown. While the
textual des
ription is quite
rypti
, you
an probably make some sense of it. You
an guess,
perhaps, that the symbol ^ is used by TEXto denote exponentiation. Or that nfra
and
364
365
1.
2.
b+
(a/(b+ ))
(a+(b/ ))
a+
3.
a+b+ +d
(((a+b)+ )+d)
4.
x+1 x
+ +6
x+3 5
((((x+1)/(x+3))+(x/5))+6)
TEX you
an see that the spe
i
ation does not
ontain any geometri
information. The
spe
i
ation does not say, for example, how long the lines in the dierent fra
tions need to
be drawn. Indeed, all this is determined by TEX, using a ni
e blend of s
ien
e and art.
How to layout mathemati
al formulae, is the rst problem we will see in this
hapter.
This will turn out to be a rather interesting appli
ation of stru
tural re
ursion. We will
then go on to another important, but more
lassi
al appli
ation: using trees to maintain an
ordered set in memory.
366
input, and so on. This will work, but turns out it will make it slightly harder to write our
program, as you will see later. So to keep matters simple, we use a slight variation on the
C++ style.
We will require that the formula be spe
ied in the style used in C++, with the operands
to the + operator as well as the / operator pla
ed in parentheses.
So as a simple example, whereas in C++ you
ould write a/b, to spe
ify this to our
program you would have to write (a/b), be
ause the rule says that the operands to every
operator must be inside parentheses. Figure 22.1 gives some more examples. As you
an
see, the input required by our program is more verbose as
ompared to what is required
to spe
ify the formula in C++. In the exer
ises we will explore the issues in allowing less
verbose input.
We should note an interesting feature of our input format. You will note that any formula
written in the style des
ribed above will have one of the following forms:
x : where x is a primitive formula, i.e. an identier or a number.
(f+g) : where f and g are themselves formulae. For example, in ((((x+1)/(x+3))+(x/5))+6)
we have f = (((x+1)/(x+3))+(x/5)) and g = 6
(f/g) : where f and g are themselves formulae. For example, in (a/(b+
)) we have f = a
and g = (b+
).
In other words, formulae are built up either using primitive formulae (\base
ase") or other
formulae. Hen
e a formula is said to have a re
ursive stru
ture. The re
ursive stru
ture will
be very useful.
22.1.2 Layout \by hand"
Before jumping into a dis
ussion of how to do a layout on a
omputer, it is worth
onsidering
how we would do a layout using paper and pen
il. A
tually, this time we will also use s
issors
and glue!
Before reading further, you are invited to think about how you will do a layout. After
all, you have been writing out formulae sin
e high s
hool! You will perhaps nd the problem
to be slightly tri
ky.
It is
onvenient to
onsider the problem in a re
ursive manner. First the base
ase.
Suppose the formula you wish to layout is a primitive formula. In this
ase, the requirement
is simple: you print out the number or the identier wherever it is to be printed out.
Next
onsider non-primitive formulae, i.e. f=g or f + g, where we have omitted the
parentheses for brevity. For this we will assume that we are given re
tangular pie
es of
paper with the layouts of f; g respe
tively. The re
tangles are bounding boxes for the layouts,
i.e. they are the smallest re
tangles having horizontal and verti
al sides that
ontain the
layouts. Our goal is to sti
k these on a larger pie
e of paper, with a horizontal bar or +
drawn suitably to produ
e the layout of f=g or f + g as desired. How did we get the layouts
of f; g in the rst pla
e? As you may guess, the answer is: re
ursion!
First
onsider how we might produ
e the layout for f=g. Clearly, the formula f must be
at the top, below whi
h there must be a horizontal bar denoting the division, below whi
h
there must be the formula g. The length of the bar must equal the maximum of the widths
367
of the formulae f; g. Further, f; g must be
entered with respe
t to the horizontal bar. Here
is an example.
a
2
1+b
a
We have shown the bounding boxes for f; g as well as f=g. The gap between the bounding
boxes has been put in for readability; if you are
utting paper, there will be no gap.
Next
onsider f + g. In this
ase f must appear to the left, then the symbol + must
appear, and g must appear to the right. However, how do we align f; g with the + symbol?
This will depend upon what is inside the formulae, as the following example will illustrate.
a+ 12
+b
a
Here we have shown the layout of f + g where f = a and g = a b . As you
an see, how the
+ symbol aligns with f; g depends upon what is in f; g. Both f; g appear to have a
ertain
operator level whi
h must align with the horizontal bar of the intervening +. For f , whi
h
is a primitive formula, the operator level seems to be just the level of its
enter, whi
h as
you see is aligned with the horizontal bar of the +. However, for g whi
h is itself a ratio of
2 and a + b , the operator level seems to be the horizontal bar between the numerator 2 and
the denominator a + b.
So if we wish to layout f + g we will need to know the operator levels of f; g. In the
dis
ussion above we have seen how to determine the operator level a formula whi
h is either
primitive, or whi
h is a ratio. We will now dis
uss how to nd the operator level of a sum.
To understand this,
onsider the following example.
2
1+
+a +
d
2
1+b
a
In this, f is itself a sum of d
and a. As you will see the sum inside f must align with the
sum outside. Thus the operator level of f is the level of the + inside it. In other words, the
operator level of a sum v + w is the level of the + between the summands v; w.
Using what we have stated above, you should indeed be able to layout any formula using
paper, pen
il, s
issors and glue. You are requested to think this over
arefully, a
tually trying
out some examples if ne
essary. This kind of introspe
tion over what you might
onsider
\obvious" and \routine" is
ru
ial for developing algorithms.
Next we turn to developing the program. Our goal will be to produ
e a layout identi
al
to the one as would be produ
ed by the pro
edure we des
ribed above.
368
+
/
(a)
(b)
+1
+3
stru
t Node{
string value;
har op;
Node* lhs;
Node* rhs;
};
This stru
ture
an be used to express primitive as well as non primitive formulae. If a
formula is primitive, i.e.
onsists of an identier or a number, we will store that symbol or
number in the member value as a
hara
ter string. Otherwise, the formula must be a binary
369
omposition of two smaller formulae. In this
ase, we store the operator in the op member,
and we store pointers to the roots of the subformulae in the members lhs,rhs.
It is useful to have
onstru
tors for both ways of
onstru
ting formulae.
Node::Node(string v){
// primitive
onstru
tor
value = v;
op = 'P';
//
onvention: 'P' in op denotes primitive formula.
lhs = NULL;
rhs = NULL;
}
Node::Node(
har op1, Node* lhs1, Node* rhs1){
value = "";
op
= op1;
lhs
= lhs1;
rhs
= rhs1;
}
Here is the rst way we
an
onstru
t the representation for the formula b a
in our program.
+
Node
Node
Node
Node
Node
aexp("a");
bexp("b");
exp("
");
bplus
('+', &aexp, &bexp);
f1('/', &aexp, &bplus
);
Thus f1 will be the root of the tree for the formula b a
. Thus we
an say that f1 represents
the formula. Or alternatively we
an also
onstru
t a representation more dire
tly:
+
An important point to note here is that the operator new when used on a
onstru
tor
all
returns a pointer to the
onstru
ted obje
t, whi
h is exa
tly what we want as an argument
to our re
ursive
onstru
tor. Thus f2 will also be a root of the tree for the formula b a
, and
an thus be said to represent the formula.
There is a dieren
e between the two
onstru
tions, however. In the rst
onstru
tion, all
memory for the formula
omes from the
urrent a
tivation frame. In the se
ond
onstru
tion,
all memory ex
ept for the node f2
omes from the heap, the memory for f2
omes from the
urrent a
tivation frame.
On
e we have a representation for formulae, our task splits into two parts:
1. Read the formula from the input in the format spe
ied in Se
tion 22.1 and build a
representation for it using the Node
lass.
2. Generate the layout for the
onstru
ted representation. It is natural to dene member
fun
tions on Node whi
h will generate the layout.
We
onsider these steps in turn.
+
370
This will
all our latest
onstru
tor with the parameter infile being
in, i.e. the formula
will be read from the keyboard. You may type in something like
(a/(b+
))
371
372
stru
t Node{
Node *lhs, *rhs;
har op;
string value;
double width, height, des
ent;
Node(string v);
Node(
har op1, Node* lhs1, Node* rhs1);
Node(istream& infile);
void setSizes();
void draw(float
lx, float y); // to a
tually draw
}
We have already given the implementations for the
onstru
tors. The implementation for
setSizes follows the ideas des
ribed above. Note that f
orresponds to lhs and g to rhs.
onst double h_o = 20; // spa
e for horizontal bar
void Node::setSizes(){
swit
h (op){
ase 'P':
// Primitive formula
width = textWidth(value);
as
ent = textHeight()/2; des
ent = textHeight()/2;
break;
ase '+':
//
ase f+g
lhs->setSizes();
rhs->setSizes();
des
ent = max(lhs->des
ent, rhs->des
ent);
width = lhs->width + textWidth(op) + rhs->width;
as
ent = max(lhs->as
ent, rhs->as
ent);
break;
ase '/':
//
ase f/g
lhs->setSizes();
rhs->setSizes();
width = max(lhs->width, rhs->width);
as
ent = h_o/2 + lhs->as
ent + lhs->des
ent;
373
(xf
+g
; yf +g )
(xg ; yg )
(xf ; yf )
ag
af +g
af
hf +g
dg
df
wf
df +g
wg
wo
wf +g
(a) Layout of f + g
(xf =g ; yf =g )
(xf ; yf )
hg
wf
hf =g
Operator level
of f=g
ho
(xg ; yg )
hg
wg
wf =g
374
Next we
onsider how to a
tually draw the layout. We will dene a draw member fun
tion
taking as arguments the desired
oordinates x,y of the top left
orner of the bounding box.
The implementation will of
ourse be re
ursive.
If the formula being drawn is primitive, then we simply write the
orresponding text.
For this we
reate a text obje
t and imprint it. In
reating text we must spe
ify the
enter;
thus if we want the top left
orner to be (x,y) the text must be
entered at (x+width/2,
y + as
ent).
If the formula beind drawn is of the form f + g, then we
an see where to draw it by
onsulting Figure 22.3(a). As
an be seen, the x
oordinate of the top left
orner of f is the
same as that of f + g, i.e.
xf = xf g
(22.7)
The box of g is oset to the right by the amount wf + wo. So we have
xg = x f g + w f + w o
(22.8)
As to the y
oordinates, note that the top left
orners of f + g; f; g are above the operator
level by af g ; af ; ag . Thus it follows that
yf = y f g + a f g a f
(22.9)
yg = y f g + a f g a g
(22.10)
Finally, the + symbol must be drawn at the operator level whi
h is wf + wo=2 to the left
and af g below the top left
orner of f + g. This is used in the
ode below. Note that f
orresponds to the member lhs, and g to the member rhs.
+
375
The last
ase is of drawing an expression of the form f=g. How to position f; g and how to
draw the horizontal bar
an be worked out from Figure 22.3(b). We omit the details.
22.1.6 The
omplete main program
Here is the main program.
int main(){
initCanvas("Formula drawing");
Node f(
in);
f.setSizes();
f.draw(0,0); // top left of formula must align with top left of
anvas.
wait(5);
}
22.1.7 Remarks
Re
ursive stru
tures appear in many real life situations. For example, the administrative
heirar
hy of an organization is re
ursive, e.g. there is a dire
tor/president/prime ministers,
to whom report deputies, to whom report further deputies.
It is natural to asso
iate a tree with a re
ursive stru
ture. The substru
tures are denoted
as subtrees, and the element joining the subtrees, e.g. the dire
tor will
orrespond to the
root. In the
ase of mathemati
al expressions, the operator
orresponds to the root, and the
sub expressions
orrespond to the subtrees.
You may be wondering why we require that the formulae to be layed out be spe
ied in
our verbose format; why not just spe
ify them as they might be in C++? It turns out that
getting a program to read formulae in C++ like languages is a
lassi
al
omputer s
ien
e
problem, in its most general setting. If you pursue further edu
ation in Computer S
ien
e,
you will perhaps study it in a
ourse on
ompiler
onstru
tion, or automata theory. For now
su
e it to say that reading C++ style expressions is a di
ult problem. However, in the
exer
ises you are en
ouraged to think about it.
We
onsider the following abstra
t problem: how to maintain a set whose elements
an be
integers. As the program exe
utes, integers
an get added into the set. In addition, the
program must respond to membership queries, i.e. given some integer x, the program must
determine if x is present in the set. For simpli
ity, we only
onsider these two operations,
376
insertion, and membership. But you
an see that other operations might also be useful, e.g.
removing an element from a set, or nding the number of elements smaller than a given
number z. Even more generally, you
ould let the elements of the set be
omplex obje
ts,
e.g. a stru
ture
ontaining the roll number and marks of a student. Then you might merely
want to know if a student belongs to a
lass (membership), or you might want to know the
number of students who got fewer marks than some number z. The ideas we dis
uss for
our simple problem will be of use in these more
ompli
ated situations as well, as you will
dis
over in the exer
ises.
The simplest way to store a set is to use an array, or a ve
tor (Chapter 20). To add an
element, we simply use the push ba
k fun
tion. To determine if an element is present, we
an s
an through the ve
tor. The s
anning operation, however, is rather time
onsuming:
we need to examine every element stored in the ve
tor. A slight improvement is to keep the
elements sorted in the ve
tor. Then we will be able to perform membership queries using
binary sear
h, whi
h would go very fast. However, when a new element is to be inserted, we
will need to nd its position, and shift down the elements larger than it. This operation will
on the average require us to shift half the elements, and thus is quite time
onsuming. So
again this is unsatisfa
tory.
377
34
56
50
18
40
77
30
70
(a)
35
12
86
60
34
10
18
30
36
51
65
78
93
56
(b)
18
30
70
(c)
int value;
This is identi
al to what we had in Se
tion 22.1.3, ex
ept that we do not have the member
op, whi
h is not needed here. We do have a member value whi
h will hold the set element
stored at the node. As before the members lhs and rhs will pointers to the root nodes of
the left and right subtrees.
22.2.2 The general idea
We explain with an example why sear
h trees are useful for storing sets. Suppose we have
somehow
onstru
ted the sear
h tree in memory. Suppose now the user presents us a membership query, say determine whether some number x is present in the tree. How do we
do this? Remember that when we build the tree, we will only be able to refer to the root
dire
tly. To get to the other nodes, we need to follow the left or right pointers. So our
problem is: we know the root of the tree that
ontains the elements, and we are given x,
and we wish to determine if x is present at any node of the tree.
Sin
e we only know the root of the tree, we
an only
ompare x with the number stored
at the root. If the number happens to be x, then we
an immediately respond with a true
response. If the number at the root is not x, then we need to do more work. So we ask
whether x is smaller or larger than the number at the root. If x is smaller, then we know
that it
an only be in the left subtree. This is be
ause of the sear
h tree property: the
numbers in the right subtree
an only be larger than the number at the root, so there is no
378
need for us to
ompare x to them. Thus now we
an re
urse on the left subtree! Similarly,
if x is larger than the number at the root, then we re
urse on the right subtree, i.e. try to
determine if x is present in the right subtree. The re
ursion stops if we are for
ed to sear
h
an empty subtree { if that happens we know that the number is not present and we return
false.
As an example, suppose we have in memory the tree in Figure 22.4(b). Suppose we want
to know if x = 63 is in the tree. Then we would
ompare x to the number at the root, 50.
Finding that x is bigger, we would de
ide that we only need to sear
h the right subtree. The
root node
ontains a pointer to the root of the right subtree, so we use that to get to root
of the right subtree. So next we
ompare x with the number stored there, whi
h is 77. This
time we realize that x whi
h we are looking for is smaller. So we know we must sear
h the
left subtree beneath 77. So we follow the left pointer this time and get to the node
ontaining
the key 60. This time we
he
k and realize that x is in fa
t larger. So we follow the right
bran
h out of the node
ontaining number 60. So we get to the node
ontaining the number
65. Sin
e x is smaller than 65, we know we must go to the left subtree. But there is no left
subtree for the node
ontaining 65! So in this
ase we have determined that our number x
is not present in the set. So we return false as the answer.
Noti
e that we have been able to get to an answer by examining a very few nodes: those
nodes
ontaining 50, 77, 60, and 65. We did not examine the other nodes, yet we dedu
ed
that the number x = 63
ould not have been present in the other nodes: be
ause we know
that the tree obeys the sear
h tree property. So this is how we
an give a fast response.
If at this point you feel that our argument is too sli
k, you would be right. The argument
given above depends very mu
h upon the shape of the tree in whi
h the set was stored. The
tree of Figure 22.4(b) was balan
ed, i.e. both subtrees under ea
h node had exa
tly the same
number of nodes. If the tree is unbalan
ed, then the e
ien
y
an be
ome mu
h worse. We
take up this aspe
t in Se
tion 22.2.5. For now, we will assume that somehow the trees that
we en
ounter will be balan
ed, or nearly balan
ed. This assumption
an be justied, as you
will see in Se
tion 22.2.5.
Next we
onsider the operation of inserting elements into the set. For this, we do something similar to
he
king if a number is present. Suppose we wish to insert the number x.
Then we rst
he
k the number at the root. If x is smaller, then we know that it must be
inserted in the left subtree, or else the sear
h tree property will be violated. So we re
ursively try to insert in the left subtree. Similarly if x is larger than the number at the root,
we re
ursively try to insert in the right subtree. The pre
ise details will be
ome
lear when
we give the
ode.
22.2.3 The implementation
We will indeed represent a set as a tree. In the last se
tion, we used the root node of the
tree as the representative of the tree, i.e. the point from whi
h we
an get a
ess to the rest
of the tree. This does not quite work in the present
ase.
There is a slight te
hni
ality that we need to
onsider. How do we represent an empty
set? If the representation
ontains any Node obje
t whatsoever, then that obje
t will store
a value, and hen
e will not represent an empty set. So
learly we
annot represent a set by
the node denoting the root of the tree. Instead, we represent a set by a pointer to the node
379
denoting the root. Now if we want to represent the empty set, we merely set this pointer to
NULL.
For
onvenien
e, we will put the pointer to the root inside a
lass Set, whi
h will
ontain
a data member we will
all root, whi
h will point to the root node of the tree.
stru
t Set{
Node* proot;
// pointer to tree root.
Set(){proot = NULL;}
// more to
ome.
}
The
lass will have more member fun
tions, but we have put in a
onstru
tor whi
h sets the
member proot to NULL, indi
ating that the set is empty. Given this denition, we may write
Set mySet;
and we will have de
lared a set in our program. This is ni
er than saying Node* mySet;.
We will also
hange the Node stru
t slightly. Instead of using it as given earlier, we will
dene it as follows.
stru
t Node{
Set lhs, rhs;
int value;
};
Noti
e that this denition is really the same as the old, after all Set
ontains no other data
members ex
ept proot of type Node*. But making the members lhs, rhs of type Set will
make the
ode easier to read. Many programmers might sti
k with the old denition, so the
Exer
ises ask you to do that also.
We will implement the membership query as a bool fun
tion find taking as argument
the element to look for. The
ode for the fun
tion follows dire
tly from what we dis
ussed
earlier.
bool Set::find(int elt){
if(proot == NULL) return false;
else{
if(elt == proot->value) return true;
else if(elt < proot->value) return proot->left.find(elt);
else return proot->right.find(elt);
}
}
As we said, if we rea
h an empty tree, the element is not present, hen
e the rst statement
returns false. Else we
ompare the element being sear
hed, elt, with the value at the root of
the set. Note however that Set merely
ontains a pointer proot to the root, hen
e the value
stored at the root is proot->value. If elt is equal to proot->value, we have dis
overed that
elt is indeed in the set, and so we return true. Else if elt is smaller than proot->value,
we must sear
h the left subtree, proot->left. Similarly the right.
Before we dis
uss insertion, it is useful to see a
onstru
tor for Node.
380
Node::Node(int v){
value = v;
}
This sets the value member to the given value, but does nothing to the other members
lhs, rhs. Is this OK? If nothing is spe
ied, then these members will be initialized by their
default
onstru
tors. But the default
onstru
tor for Set
auses the member proot to be
set to NULL. So the members lhs, rhs would indeed get initialized
orre
tly.
Now we
ome to insertion. We will implement insertion by a fun
tion insert taking the
element to be inserted as the argument. The
ode is re
ursive and follows our dis
ussion.
void Set::insert(int elt){
if(proot == NULL){proot = new node(elt);}
else{
if(elt == proot->value) return;
// no need to insert again.
if(elt < proot->value) proot->left.insert(elt);
else proot->right.insert(elt);
}
}
If our set is empty, then proot will be NULL. So in this
ase, we will insert a node
ontaining
the new element. This is what the rst statement does. If the set is not empty, then proot
must be non NULL, and in this
ase we will
ome to the se
ond line of the fun
tion. We will
he
k if the value at the root is equal to the element being inserted, if so, we do nothing,
there is no need to insert the same element again. But if the value being inserted is smaller,
then we will insert into the left subtree. Similarly for the right.
The exer
ises ask you to dene other operations, e.g. printing the set. As you might
guess, most operations on trees
an be naturally ta
kled using re
ursion.
22.2.4 A note about organizing the program
Note that our denition of Node refers to the denition of Set, and vi
e versa. Whi
h one
must pre
ed the other? The denition of Set only refers to a pointer to Node. Hen
e we
an dene that after putting a forward referen
e to Node. The denition of Node however
mentions a member of type Set. Hen
e it must
ome after the denition of Set. The
implementation of the member fun
tions in Set
an
ome any pla
e following the denition
of Set.
22.2.5 On the e
ien
y of sear
h trees
We rst observe that there
an be many binary sear
h trees that
ontain a given set of
numbers. Figure 22.5 give two additional trees whi
h
ontain the same numbers as Figure 22.4(a).
There
an be other trees also, in fa
t you should be able to prove that a set with 5
elements
an be represented by 42 trees. Clearly, the time required to answer membership
queries will depend upon whi
h of these 42 trees has arisen during the exe
ution.
381
18
56
34
40
34
70
56
70
18
40
(a)
(b)
Figure 22.5: Other trees representing the same set as Figure 22.4(a)
Of these trees, the worst is the one in Figure 22.5(b). In this, the smallest value is at
the root. The se
ond smallest is at the right
hild of the root. The third smallest is at its
right
hild, and so on. Our program will build this \tree" if the numbers were inserted in
as
ending order. Suppose the user asked to sear
h for 100. Then the sear
h would start at
the root, and go rightward, examining every node in the tree. In
omparison, if the set had
been stored as Figure 22.5(a), then the we would rst
ompare 100 to the value 56, and then
to the value 70, and
on
lude that 100 is not in the set. So
learly the tree of Figure 22.5(b)
is bad for the purpose of
he
king if 100 is in the set. Indeed, you will observe that sear
hing
in the tree of Figure 22.5(b) is essentially like sear
hing in a sorted ve
tor.
So perhaps we
ould make a general observation: our find fun
tion will examine the
values stored in some path starting at the root and ending at the leaf. So if we want the
program to run fast, then we would like all su
h paths to be short. It is
ustomary to dene
the height of a tree as the length of the longest root leaf path in a tree. Thus our hope is
that as the program exe
utes, the tree we get has small height.
Theorem 3 Suppose numbers from a
ertain set jS j are inserted into a sear
h tree using
our insert fun
tion. Then if the order to insert is
hosen at random, then the expe
ted
height of the tree smaller than 2 ln jS j, i.e. twi
e the natural log of the number of elements
in the set.
The proof of the theorem is outside the s
ope of this book, but the exer
ise asks you to
validate it experimentally.
Let us try to understand what the theorem says using an example. Suppose we have
a set with size 1000, whose elements are inserted in random order into our tree. Then on
the average we expe
t to see that the height will be at most 2 ln1000 14. Thus when we
perform membership queries (or further insertions) we expe
t to
ompare the given number
with the numbers in at most 15 nodes in the tree.
You
ould also ask what are the worst and best heights possible for 1000 nodes. Clearly, if
the numbers
ame in in
reasing order, then we would get just one path of length 1000 { that
would be the height. The other extreme is a tree in whi
h we keep on inserting nodes as
lose
to the root as possible. So we would start by inserting two nodes dire
tly
onne
ted to the
root, then two nodes
onne
ted to ea
h of these, and so on, till be inserted 1000 nodes. So we
would have 1 node (the root itself) at distan
e 0, 2 nodes at distan
e 1, 4 nodes at distan
e
382
2 and so on till 256 nodes at distan
e 8, and the remaining 1000 256 128 1 = 489
nodes at level 9. So the height of this tree would be 9.
So it is ni
e to know that on the average we are likely to be mu
h
loser to the best height
rather than the worst. Or alternatively, on the average our find and insert fun
tions will
run fast.
22.2.6 Balan
ing the sear
h tree
You might be bothered that the above program will work fast \on the average", but might
take very long if you are unlu
ky. What if the numbers in the set got inserted in as
ending
order, or some su
h bad order?
In that
ase there are advan
ed algorithms that try to balan
e the tree as it gets built.
This is done by modifying an already built tree, and say
hanging the root. With su
h
rebalan
ing, it is indeed possible to ensure that the height of the tree remains small. Further,
rebalan
ing algorithms have been developed that also run very fast. But this is outside the
s
ope of this book.
22.2.7 Sear
h trees and maps
The (index,value) pairs
onstituting a map from the C++ Standard Library are stored
using binary sear
h trees. The ordering rule is that all pairs in the left subtree must have
index smaller than that at the root, whi
h in turn must be smaller than the indi
es of the
elements in the right subtree. Further, the tree is kept balan
ed as dis
ussed above. Thus
making an a
ess su
h as value[index happens fairly fast, i.e. in time proportional to
log n, where n is the number of pairs stored in the map.
2
1. Extend the formula drawing program so that it allows the operators '*', '+' and
'-'. This is not entirely trivial: make sure your program works
orre
tly for input
((x+3)*(x-2)). You will see that you may need to add parenthesization to the output.
For simpli
ity, you
ould parenthesize every expression when in doubt.
2. Add an operator '^' to denote exponentiation in the formula drawing program. In
other words, Node('^',Node("x"), Node("y")) whi
h will print as xy .
3. Allow the impli
it multipli
ation operator, i.e. it should be possible to draw x uv .
4. Suppose the user gives the position of the top left
orner of the bounding box of the
formula. Show how you
ould do this. Also if the user asks that the formula be
entered
at some given point.
5. Write a
onstru
tor fun
tion whi
h takes as input a single referen
e argument, infile&
whi
h is a referen
e to an istream, and
onstru
ts an expression based on what it
reads there. The asso
iated le should
ontain valid expressions but written in a prex
form. Note that in the prex form, the operator
omes rst, and every operator is
383
1
0
x2 dx
x2 + 1
Hint: The best way to do this is to use a ternary operator, say denoted by the letter
I, whi
h takes as arguments 3 formulae: the lower limit L , the upper limit U, and the
expression E to be integrated. You
ould require this to be spe
ied as (L I U E).
9. As we have dened, our formulae
annot in
lude bra
kets. Extend our program to
allow this. You
ould think of bra
kets being a unary operator, say B. Sin
e it is
our
onvention to put the operator se
ond, you
ould ask that if a formula F is to be
bra
keted, it be written as (F B). Make sure that you draw the bra
kets of the right
size.
10. You may want to think about how the program might
hange if the formula to be
layed out is spe
ied in the standard C++ style, i.e to draw a +
b the input is given
as a+b/
rather than (a+(b/
)) as we have been requiring. The key problem as you
might realize, is that after reading the initial part a+b of the input, you are not sure
whether the operator + operates on a,b. This is the
ase if the subsequent operator,
if any, has the same pre
eden
e as +. However, if the subsequent operator has higher
pre
eden
e, as in the present
ase, then the result of the division must be added to a.
So you need to look ahead a bit to de
ide stru
ture to
onstru
t. This is a somewhat
hard problem, but you are en
ouraged to think about it. Note that your job is not only
to write the program, but also argue that it will
orre
tly deal with all valid expressions
that might be given to it.
11. Add a deriv member fun
tion, whi
h should return the derivative of a formula with
respe
t to the variable x. Use the standard rules of dierentiation for this, i.e.
d(uv )
= v du + u dv
dx
dx
dx
384
This will of
ourse be re
ursive. You should be able to draw the derivatives on the
anvas, of
ourse.
12. You will noti
e that the result returned by deriv often has sub-expressions that are
produ
ts in whi
h one operand is 1 and sums in whi
h one operand is 0. Su
h expressions
an be simplied. Add a simplify member fun
tion whi
h does this. This will
also be re
ursive.
13. Suppose you want to represent sets using just the Node denition from Se
tion22.2.1.
Then to
reate a set mySet whi
h is initially empty, I would write:
Node* mySet = NULL;
To implement membership and insertion queries, we
ould merely adapt the fun
tions
insert and find of Se
tion 22.2.3. Note however, that those were member fun
tions
for Set, whis is really of type Node*, so they
annot be
ome member fun
tions for
Node. Thus they must be
ome ordinary fun
tions. Here is a suggested adaptation of
insert:
void insert(node* set, int elt){
if(set == NULL){set = new node(elt,NULL,NULL);}
else{
if(elt < set->value) insert(set->left, elt);
else insert(set->right, elt);
}
}
Do you think it is a faithful adaptation? Does it work? Hint: Be
areful about whether
you should use
all by referen
e or by value.
Here is an adaptation of the nd method.
bool find(node* set, int elt){
if(set == NULL) return false;
else{
if(elt == set->value) return true;
else if(elt < set->value) return find(set->left, elt);
else return find(set->right, elt);
}
}
385
Note: Your answer to the previous problem will likely print absolutely nothing for an
empty set. Suppose that you are to print a message \Empty set" in su
h
ases. Hint:
Use one non-re
ursive member fun
tion whi
h
alls a re
ursive one.
15. Add a member fun
tion with signature int smaller(int elt) whi
h returns the number of elements in the set smaller than elt. Hint: Add a member
ount to ea
h node
whi
h will indi
ate the number of nodes in the subtree below that node. You will need
to update
ount values suitably whenever you insert elements. Now use the
ount
value to respond to smaller.
16. Experimentally verify Theorem 3. Let n denote the number of elements in the set.
Assume without loss of generality that the elements in the set are integers 1; 2; : : : ; n.
Run the insertion algorithm by generating numbers between 1 and n (without repla
ement) in random order. Measure the height of the resulting tree. Repeat 100 times
and take the average. Repeat for dierent values of n and plot average tree height
versus n.
Chapter 23
Inheritan
e
Inheritan
e is one of the most important notions in obje
t oriented programming. The key
idea is: you
an
reate a
lass B by designating it to be derived from another
lass A. Created
this way, the
lass B gets (\inherits") all the data members and ordinary fun
tion members
of A. In addition to what is inherited, it is possible to in
lude additional data and fun
tion
members in B. It is also possible to redene some of the inherited fun
tion members. The
lass B thus
reated, is said to be a sub
lass of the
lass A. As you might suspe
t, this is a
onvenient way to
reate new
lasses.
The most
ommon and natural use of inheritan
e is in the following setting. Suppose, a
program deals with
ategories of obje
ts, whi
h are divided into sub
ategories. For example,
a program might be
on
erned with bank a
ounts, and these may be divided into dierent types of a
ounts, e.g. savings a
ounts and
urrent a
ounts. Or a program might be
on
erned with drawing geometri
shapes on the s
reen, and the
ategory of shapes, as we
have seen, might be subdivided into sub
ategories su
h as
ir
les, lines, polygons and so on.
In su
h
ases it turns out to be useful to represent a
ategory (e.g. a
ounts or geometri
shapes) by a
lass, and sub
ategories (savings a
ounts and
urrent a
ounts, or lines and
ir
les) by sub
lasses. As you will note, the attributes asso
iated with a
ategory (e.g. a
ount
balan
e, or s
reen position) are present in the sub
ategories. Hen
e it is natural that these
attributes will be dened in the
lass
orresponding to the
ategory. These attributes will be
inherited when we dene sub
lasses
orresponding to the sub
ategories. In ea
h sub
lass we
need additionally dene the attributes whi
h are spe
i
to the
orresponding sub
ategory.
For example, in the
ir
le sub
lass we
ould dene the attributes
enter and radius, while the
polygon
lass will have verti
es as the attributes. Categories and sub
ategories are
ommon
in real life, and hen
e inheritan
e
an play a
entral role in the design of
omplex programs.
First some terminology. Suppose we derive a
lass B from a
lass A using inheritan
e.
It is
ustomary to say that
lass B is a sub
lass of
lass A, is derived from A, or obtained
by extending
lass A. And of
ourse, B is said to inherit from A. It is likewise
ustomary to
say that A is a super
lass of B, or base
lass of B or sometimes the parent
lass of B. We
an
have several
lasses say B,C,D inheriting from A. In turn we may have
lasses E,F inheriting
from B. In su
h a
ase, the
lasses A, B, C, D, E, F are said to
onstitute an inheritan
e
heirar
hy.
In this
hapter we will mainly
onsider the me
hani
s of inheritan
e. We begin by
onsidering a simple example and then dis
uss how to use inheritan
e in general. An important
386
387
aspe
t of inheritan
e is that we
an have many views of an obje
t; sometime we might
onsider it to be an instan
e of a sub
ategory (e.g. a
ir
le) at other times we may
onsider it
as belonging to a
ategory (e.g. a shape). In order to be able to shift views smoothly, we
need the notions of polymorphism and virtual fun
tions. We dis
uss these notions. In the
next
hapter we
onsider how to design programs using inheritan
e.
Suppose we wish to design a
lass mTurtle (short for metered turtle) whi
h is exa
tly like the
lass Turtle, ex
ept that the turtle will keep a
ount of how mu
h distan
e it has
overed.
So a mTurtle will be able to move forward, turn,
hange
olours et
. just like a Turtle, but
in addition it will have an additional member fun
tion distan
eCovered whi
h will return
the total distan
e
overed till then.
Here is an example of a main program that we would like to write.
int main(){
initCanvas();
mTurtle m;
m.forward(100);
m.right(90);
m.forward(50);
out << m.distan
eCovered() << endl;
}
};
388
As you
an see, inside mTurtleC we have a Turtle obje
t whi
h you will see on the s
reen,
and a member distan
e whi
h keeps tra
k of how mu
h the turtle has moved. Clearly,
when an mTurtleC is
reated, we should set distan
e to 0, whi
h is what the
onstru
tor
above does. The
onstru
tor does not appear to do mu
h with the turtle member t. But
you know that the member t is also
reated, using the default Turtle
onstru
tor. Thus
a turtle will appears on the s
reen. The member fun
tion forward in mTurtleC
auses
distan
e to be updated, and
auses the turtle to move as well. Finally, the member fun
tion
distan
eCovered prints distan
e as expe
ted.
The
ode for right is simple, we just
all the fun
tion right on member t, with the
same argument. We will have to write su
h forwarding fun
tions for other fun
tions su
h as
left, hide and so on.
Clearly, this will enable us to write the main program given earlier; we merely have to
use mTurtleC in it instead of mTurtle. The solution is fairly satisfa
tory, the main drawba
k
is the need to write the forwarding fun
tions.
23.1.2 Implementation using Inheritan
e
Using inheritan
e, we
an dene a metered turtle more
ompa
tly, as follows. We will
all
this
lass mTurtleI.
lass mTurtleI : publi
Turtle{
float distan
e;
publi
:
mTurtleI(){
distan
e = 0;
}
void forward(float d){
distan
e += abs(d);
Turtle::forward(d);
}
float distan
eCovered(){
return distan
e;
}
};
We will shortly explain what ea
h line in this does. For now we merely note that this will
essentially do what the
ode in Se
tion 23.1.1 did. With this, we will be able to run the
389
main program given at the beginning of Se
tion 23.1, of
ourse we will need to use mTurtleI
in the main program instead of mTurtle.
Note that we have not dened fun
tions su
h as right. We have not expli
itly dened
the member t of type Turtle. As we will see next, these are inherited!
390
color : Color
..other members
inherited from
Turtle..
t : Turtle
"Inherited object"
distance : double
distance : double
(a) mTurtleI object
391
member fun
tion f of A whi
h refers to m. Now, the
ode in B
an refer to f, and hen
e
it will indire
tly refer to m.
Case 3: m is a \prote
ted" member of A: The notion of prote
ted is as follows. If a
member of a
lass A is designated as prote
ted then it
an be a
essed only inside
the denition of A or of its sub
lasses. In other words, a prote
ted member is less
a
essible than a publi
member (whi
h is a
essible everywhere), and more a
essible
than a private member, (whi
h is a
essible only in the denition of A). Note that m is
to be
onsidered a prote
ted member of B as well.
We illustrate the above rules using the following
ode snippet.
lass A{
private:
int p;
prote
ted:
int q;
int getp(){return p;}
publi
:
int r;
void init(){p=1; q=2; r=3;}
};
lass B: publi
A{
double s;
publi
:
void print(){
out << p << endl; //
ompiler error 1.
out << q << ", "
<< r << ", "
<< getp() << endl;
}
};
int main(){
B b;
b.init();
out << b.p
<< b.q
<< b.r
<< b.getp()
<< endl;
b.print();
}
p is private.
//
ompiler error 2.
//
ompiler error 3.
p is private
q is prote
ted
// ompiler error 4.
If you
ompile this
ode, you will get the 4
ompiler errors as marked. Compiler errors
1 and 2 are be
ause p is private in A, and
an hen
e not be a
essed in the denition of B,
392
p : int
p : int
q : int
q : int
r : int
r : int
Inherited object
s : double
(a) A object
(b) B object
When you
all the
onstru
tor for B, the
all-to-
onstru
tor-of-A is rst
alled, and this
onstru
ts the inherited obje
t (of
lass A)
ontained inside the instan
e of B being
reated.
The initialization-list has the form as in Se
tion 16.1.5, and is used to initialize the
new members of B. After that, body is exe
uted. The part
:
all-to-
onstru
tor-of-A
is optional. If it is omitted, the default
onstru
tor of A gets
alled. The initialization-list
is also optional, and alternatively, the new members
ould be initialized inside body.
In Se
tion 23.1 you saw an example in whi
h the default
onstru
tor of Turtle got used
for
reating a mTurtleI. Suppose now that we had an alternate
onstru
tor for Turtle
whi
h took arguments x,y giving the initial position for the turtle. Then we
ould write an
alternate
onstru
tor for mTurtleI as follows.
393
With this
onstru
tor, the metered turtle would be
reated at position (x,y) on the s
reen,
and the new member distan
e would be initialized to 0 using the initialization list. The
body of the
onstru
tor would then be left empty.
23.2.3 Destru
tors
As before suppose we have a
lass B whi
h inherits from
lass A. Then the destru
tor for
lass
B should be used to destroy the new data members introdu
ed in B that were not present in
A. The data members inherited from A? These would be destroyed by an impli
it
all that
would get made to the destru
tor of A at the end of the exe
ution of the
all to the destru
tor
of B. You should not expli
itly make a
all to the destru
tor of A from inside the destru
tor
of B!
The general rule is: destru
tion happens automati
ally, in reverse order of
reation. In
the exer
ises you will experiment with
ode whi
h will illustrate these ideas.
23.2.4 Basi
operations on sub
lass obje
ts
A sub
lass is a
lass, so all operations allowed on obje
ts of
lasses are allowed on obje
ts
of sub
lasses, and work similarly. For example, obje
ts
an be
opied, passed to fun
tions,
and so on, as dis
ussed in Chapter 15.
23.2.5 The type of a sub
lass obje
t
We have said that a
lass is a type, i.e. an obje
t of
lass A has type A. Suppose B is a
sub
lass of
lass A. Then a key idea in inheritan
e is: obje
ts of
lass B
an be
onsidered to
have type B as well as type A. This idea turns out to be quite useful.
The following analogy might be useful to understand this. Consider the
ategory of
owers, in whi
h we have sub
ategories su
h as roses, lotuses and so on. So if someone has
demanded
owers, we
an give roses. In other words, a spe
i
rose obje
t is useful as a rose
as well as as a
ower.
23.2.6 Assignments mixing super
lass and sub
lass obje
ts
If an obje
t
an be
onsidered to be of the type of its super
lass, we should allow an obje
t of
a sub
lass to be assigned to variable of the super
lass. This is indeed possible. Note however
that the sub
lass might have some additional data members. In su
h a
ase, the additional
members are dropped, or sli
ed o, during the assignment.
You
an also assign the address of a sub
lass obje
t into a pointer variable of the super
lass.
Figure 23.3 shows some examples. First we have tha assignment a = b. Sin
e b has an
extra attribute y, during the assignment this will be sli
ed o, and only the attribute x will
get
opied. Thus the rst print statement will print 2, the value that got
opied.
Next we have aptr = &b, whi
h stores the address of the
lass B obje
t b into the pointer
variable aptr of type A*. As we said, this is allowed, sin
e A is a super
lass of B. The next
lass A{
publi
:
int x;
A(){ x = 1; }
void f(){
out
};
lass B: publi
publi
:
int y;
B(){ x = 2; y
void f(){
out
};
394
<<"Calling f of A.\n";}
A{
= 1;}
<<"Calling f of B.\n";}
int main(){
A a, *aptr;
B b, *bptr;
a = b;
out << a.x << endl;
aptr = &b;
// assigning sub
lass obje
t to super
lass pointer
out << aptr->x << endl; // prints 2;
aptr->f();
A& aref = b;
aref.f();
395
statements use aptr. In the rst, we a
ess the member x, using the standard syntax
aptr->x, and this will print 2 as expe
ted. In the next statement, aptr->f(), we have
invoked the member fun
tion f. Here there is some possible
onfusion: will this mean the f
dened in A or the f dened in B? The default answer is that sin
e aptr is of type pointer
to A, the members from A will be used. Thus, this will print the message "Calling f of A.".
If you are unhappy with this default, hold on till Se
tion 23.3.
Note that in the above
ode, we
annot assign an obje
t of the super
lass into a variable
of the sub
lass, i.e. write something like b = a;. The intuition behind this, going to our
ower example, is as follows. Wherever a
ower is expe
ted, you
an supply a rose; however,
if a rose is expe
ted, you
annot supply an arbitrary
ower. Likewise, it is in
orre
t to write
bptr = &a as well.
Finally, we
an
reate referen
es of the type super
lass to obje
ts of the sub
lass. This is
done at the end of Figure 23.3. Indeed even in this
ase the fun
tion f in A will be invoked.
Consider the
ode of Figure 23.3. As we dis
ussed above, the
all aptr->f() will
ause the
fun
tion f in
lass A to be used. But you might say: aptr really points to an obje
t of type
B so isnt it more useful if the f in B were used? You
an make this happen by de
laring f to
be a virtual fun
tion. For this, you simply add the keyword virtual before the denition of
f in A. Thus the denition of A would have to be:
lass A{
publi
:
int x;
A(){ x = 1; }
virtual void f(){
out <<"Calling f of A.\n";}
};
The keyword virtual says that the denition of f should not be treated as a unique, nal
denition. It is possible that f might be over-ridden in a sub
lass, and if so, that denition
of f whi
h is most appropriate (most derived!) for the obje
t on whi
h the
all is made
should be
onsidered. When we
all aptr->f(), the most appropriate denition for f is the
one in B, sin
e aptr a
tually points to an obje
t of type B. So that denition gets used, and
our
ode will now indeed print "Calling f of B.".
Note further that if f is virtual, its most derived version will get used if it is invoked on
a referen
e as well. Thus the last statement of Figure 23.3 will also
ause f from B to be
invoked.
Here is a more subtle example of the same idea.
lass Flower{
publi
:
void whoAmI(){
out << name() << endl; }
virtual string name(){ return "Flower"; }
};
396
Exe
uting a.whoAmI() will
learly
ause \Flower" to be printed out. More interesting is
the exe
ution of b.whoAmI(). What should it print? The
all b.whoAmI() is to the inherited
member fun
tion whoAmI in the super
lass Flower. That fun
tion whoAmI
alls name, but
the question is whi
h name. Will it be the name in Flower or in Rose? The answer turns out
to be the name in Rose, be
ause (a) the obje
t on whi
h whoAmI is
alled is of type Rose,
and (b) name is virtual. Thus the most derived denition of name appropriate for the obje
t
on whi
h it is invoked will be used. Sin
e the obje
t on whi
h it is invoked is of type Rose,
the name from that
lass will be used. Thus the last statement will print ``Rose''. Note
that had we not used virtual, both statements would have printed ``Flower''.
In this example, the
all name() inside the member fun
tion whoAmI is said to be polymorphi
, be
ause the same
all will either
ause the fun
tion in Flower to be
alled, or the
fun
tion in Rose to be
alled, depending upon the a
tual type of the obje
t on whi
h it is
invoked. Note that the a
tual type will only be known during exe
ution.
Likewise the
alls aptr->f() and aref.f() in Figure 23.3 would be polymorphi
if f is
de
lared virtual.
23.3.1 Virtual Destru
tor
Suppose aptr is of type A*, and points to some obje
t. Suppose we wish to release the
memory. So we write delete aptr;. This will
all the destru
tor, but the question again
is, whi
h destru
tor? By default, the destru
tor of A will be
alled. However, if the obje
t
pointed to by aptr is of type B, whi
h is a sub
lass of A, then
learly we should be
alling the
destru
tor for B. We
an for
e this to happen by de
laring the destru
tor of A to be virtual.
Indeed, whenever we expe
t a
lass to be extended, it is a good idea to de
lare its destru
tor
to be virtual.
Here is an example.
lass A{
publi
:
virtual ~A(){
out <<"~A.\n";}
};
lass B: publi
A{
int *z;
publi
:
};
397
int main(){
A* aptr;
aptr = new B;
delete aptr;
}
If we do not de
lare the destru
tor of A to be virtual, then after the operation delete aptr;
in the main program, the memory allo
ated for z will not be freed. However, sin
e we have
de
lared the destru
tor of A to be virtual, the destru
tor of B will be
alled when delete
aptr; is exe
uted, instead of the destru
tor of A. Thus the the operation delete z; will
take pla
e. Of
ourse, as always the destru
tor of A will also be
alled, sin
e our rule is
that the destru
tor of the super
lass will be
alled after the destru
tor of the sub
lass. Do
ompile and exe
ute this
ode, you will see from the message what is
alled. Remove the
keyword virtual and exe
ute again, you will see that only the destru
tor of A is
alled.
Suppose you wish to write a program that takes as input a verb from the English language,
and prints out its past tense. Thus, given \play", the program must print \played", given
\write", the program must print \wrote", and so on. A simple implementation would be to
store every verb and its past tense as strings in memory. Given the verb, we
an then print
out the
orresponding past tense.
But you will perhaps observe that for most verbs, the past tense is obtained simply by
adding a sux \ed", as is the
ase for the verbs \play", \walk", \look". We may
onsider
these verbs to be regular. Verbs su
h as \be", \speak", \eat" whi
h do not follow this rule
ould be
onsidered irregular. Thus it makes sense to store the past tense form expli
itly
only for irregular verbs; for regular verbs we
ould simply atta
h \ed" when asked. This
an be programmed quite ni
ely using inheritan
e.
We dene a
lass verb that represents all verbs; it
onsists of sub
lasses regular and
irregular respe
tively. The denition of verb
ontains information whi
h is
ommon to
all verbs. The denition of regular adds in the extra information needed for regular verbs,
and similarly the denition of irregular.
lass verb{
prote
ted:
string root;
publi
:
string getRoot(){return root;}
};
398
The member root will store the verb itself. We have dened the member fun
tion past tense
to be virtual. For now it returns the empty string. But this is not important, sin
e we expe
t
to override it in the sub
lasses.
The sub
lasses regular and irregular are as you might expe
t.
lass regular : publi
verb{
publi
:
regular(string rt){
root = rt;
}
string past_tense(){return root + "ed";}
};
lass irregular : publi
verb{
string pt; // past tense of the verb
publi
:
irregular(string rt, string p){
root = rt;
pt = p;
}
string past_tense(){return pt;}
};
Thus, to
reate an instan
e v1 that represents the verb \play" we would just write
regular v1("play");
After this if we wrote v1.past tense(), we would get the string "played" as the result.
Similarly
irregular v2("be","was");
would
reate an instan
e v2 to represent the verb \be". And of
ourse v2.past tense()
would return \was".
We now see how the above denitions
an be used to write a main program that returns
the past tense of verbs. Clearly, we will need to somehow store the information about verbs.
For this, we use a ve
tor. We
annot have a single ve
tor storing both regular and irregular
verbs. However, we
an dene a ve
tor of pointers to verb in whi
h we
an store pointers
to irregular as well as regular obje
ts. Thus the program is as follows.
int main(){
ve
tor<verb*> V;
V.push_ba
k(new regular("wat
h"));
V.push_ba
k(new regular("play"));
V.push_ba
k(new irregular("go","went"));
399
string query;
while(
in >> query){
size_t i;
for(i=0; i<V.size(); i++)
if (V[i->getRoot() == query){
out << V[i->past_tense() << endl;
break;
}
if(i == V.size())
out << "Not found.\n";
}
We begin by
reating the ve
tor V. We then
reate instan
es of regular and irregular verbs
on the heap, and store pointers to them in
onse
utive elements of V. Finally, we enter a
loop in whi
h we read in a query from the user,
he
k if it is present in our ve
tor V. If so,
we print its past tense. Note that if the for loop ends with i taking the value V.size(), it
must be the
ase that no entry in V had its root equal to the query. In this
ase we print
"Not found.". As you know, the while loop will terminate when
in ends, e.g. if the user
types
ontrol-d.
A number of points are to be noted regarding the use of inheritan
e in this example.
1. Our need was to represent the
ategory of verbs whi
h
onsisted of mutually disjoint
sub-
ategories of irregular and regular verbs. This is a very standard situation in whi
h
inheritan
e
an be used.
2. The ve
tor V is polymorphi
: it
an
ontain pointers to obje
ts of type irregular as
well as of type regular. We
an invoke the operation past tense on obje
ts pointed
to by elements of V, without worrying about whether the obje
ts are of type regular
or irregular. Be
ause past tense is virtual, the
orre
t
ode gets exe
uted.
You will note that we return the empty string in the past tense fun
tion in verb. Returning
an empty string does not make sense, but we did this be
ause we expe
ted that the verb
lass would never be used dire
tly; only its sub
lasses would be used in whi
h the fun
tion
would get overridden. This idea works, but it is not aestheti
ally pleasing that we should
need to supply an implementation of past tense in verb expe
ting fully well that it will
not get used.
One possibility is to only de
lare the member fun
tion past tense in verb, and not
supply any implementation at all. Unfortunately, whenever an implementation is not supplied, the
ompiler expe
ts to nd it somewhere, in some other le perhaps. If su
h an
implementation is not given the
ompiler or the linker will produ
e an error message.
400
What we need is a way to tell the
ompiler that we do not at all intend to supply an
implementation of past tense for the verb
lass. This is done by suxing the phrase \=
0" following the de
laration. Thus we would write
lass verb{
...
publi
:
virtual string past_tense() = 0;
...
}
Writing \= 0" following the de
laration of a member fun
tion tells the
ompiler that we do
not intend to at all supply an implementation for the fun
tion. You may think of 0 as representing the NULL pointer, and hen
e ee
tively indi
ating that there is no implementation.
There is an important
onsequen
e to assigning a fun
tion to 0. Suppose a
lass A
ontains a member fun
tion f assigned to 0. Then we
annot
reate an instan
e of A! This is
be
ause for that instan
e we would not know how to apply the fun
tion f. In C++,
lasses
whi
h
annot be instantiated are said to be abstra
t. Indeed, the only way of making a
lass
abstra
t is to assign one of its member fun
tions to 0.
So in our
ase, if we assign past tense to 0 in verb, then the
lass verb would be
ome
abstra
t. We would not be able to
reate instan
es of it. But this is ne, we indeed would
not like users to instantiate verb, instead we expe
t them to instantiate either regular or
irregular.
1
Sometimes, an obje
t
an be thought of as a spe
ialization of not one, but two other obje
ts.
For example, we might have an Automobile
lass and a SolarPoweredDevi
e
lass.
lass Automobile{
double mileage;
};
lass SolarPoweredDevi
e{
double
ellEffi
ien
y;
};
Suppose we also need to represent solar powered automobiles, they would need to have
features of both the Automobile
lass as well as SolarPoweredDevi
e
lass. We
an get
this by
onstru
ting a
lass SolarPoweredAutomobile whi
h inherits from both!
lass SolarPoweredAutomobile : publi
Automobile,
publi
SolarPoweredDevi
e {};
If we make the
onstru
tor of a
lass private and there are no member fun
tions that use the
onstru
tor,
then ee
tively we have ensured that the
lass
annot be instantiated. But te
hni
ally su
h
lasses are not
onsidered to be abstra
t.
1
401
GP
P1
P2
GP{ double x; };
P1: virtual publi
GP{};
P2: virtual pubil
GP{};
C: publi
P1, publi
P2{};
With this, the
lass C will
ontain only one
opy of the inherited obje
t GP. Note that this
inherited obje
t will be initialized dire
tly by
alling its
onstru
tor. Thus if the initialization
list of P1 or P2
ontains a
all to the
onstru
tor of GP, then those
alls will be ignored.
So far we have only dis
ussed publi
inheritan
e. C++ allows other kinds of inheritan
e
also, namely prote
ted inheritan
e and private inheritan
e.
If a
lass B inherits from a
lass A using prote
ted inheritan
e, then the publi
and
prote
ted members of A be
ome prote
ted members of B.
402
If a
lass B inherits from a
lass A using private inheritan
e, then the publi
and prote
ted
members of A be
ome private members of B.
As you
an see, prote
ted and private inheritan
e progressively restri
t the a
essibility
of the members inherited into the derived
lass. These kinds of inheritan
e appear to be
used mu
h less in pra
ti
e. So we will not dis
uss them any further.
23.8 Remarks
Inheritan
e is a powerful idea. However, like any powerful idea, it needs to be used with
are.
An informal rule of thumb is as follows. Suppose A; B are entities whi
h you wish to
represent using
lasses A,B. If entities of type B are spe
ialized versions of type A, then
inherit
lass B from
lass A. Informally, you may ask, do the entities B; A have an \is-a"
relationship? Clearly, a rose is a
ower, so the entities rose,
ower have an \is-a" relationship.
So in this
ase, use inheritan
e. If two entities have a \has-a" relationship, then better use
omposition. For example,
owers have petals, so the entities
ower, petal have a \has-a"
relationship. It is best to model this relationship by
omposition, i.e. by making a petal a
member inside a
ower.
An important advantage of inheritan
e is polymorphism. As we saw in Se
tion 23.4,
we might have several sublasses of a base
lass. We
an
onveniently store instan
es of
the sub
lasses in a ve
tor (or some other
olle
tion), for this we
an
onsider them to be
members of the base
lass. But we
an invoke fun
tions on the obje
ts, and if the fun
tions
are virtual, we get the benet of
onsidering them to be members of the sub
lasses too. This
ame in handy when writing the program to display past tense of verbs. And we will see
more examples of this in the next
hapter.
Dene a
lass W that inherits from V and has a member fun
tion dot whi
h
omputes
the dot produ
t of two ve
tors. Thus given V type obje
ts v,w, then the dot produ
t is
v.x * w.x + v.y * w.y + v.z * w.z. Be
areful that you only use the
onstru
tor
provided in V.
2. Dene a
lass realTurtle su
h that realTurtle obje
ts move with some spe
iable
speed when they move. They should also turn slowly.
403
3. What do you think will happen when you exe
ute the program given below? Run it
and
he
k if you are right.
lass A{
publi
:
A(){
out << "Constru
tor(A).\n";}
~A(){
out << "Destru
tor(A).\n";}
};
lass B: publi
A{
publi
:
B(){
out << "Constru
tor(B).\n";}
~B(){
out << "Destru
tor(B).\n";}
};
lass C: publi
B{
publi
:
C(){
out << "Constru
tor(C).\n";}
~C(){
out << "Destru
tor(C).\n";}
};
int main(){
C
;
}
5. Write the past tense generation program using just two
lasses, a verb
lass and an
irregular
lass. A regular verb should be
reated as an instan
e of verb, and an
irregular as an instan
e of irregular.
404
6. You might note that the past tense of several verbs ending in \e" is obtained by adding
\d", e.g. re
ite, advise. Add an extra sub
lass to the verb
lass to store the past tense
of su
h verbs
ompa
tly.
Chapter 24
Inheritan
e based design
Inheritan
e is often very useful in designing large
omplex programs. Its rst advantage
we have already dis
ussed: inheritan
e is
onvenient in representing
ategories and sub
ategories. But there are some related advantages whi
h have to do with the management of
the program development pro
ess. We will dis
uss these next, and then in the rest of the
hapter we will see some examples.
There are many approa
hes to designing a large program. A
lassi
al approa
h requires
that we rst make a
omplete study of the program requirements, and only thereafter start
writing the program. More modern approa
hes instead a
knowledge/allow for the possibility
that requirements may not be understood unless one has built a few versions of the program.
Also, if a program works beautifully, users will inevitably ask that it be enhan
ed with more
features. In any
ase, programs will have a long lifetime in whi
h the requirements will
evolve. So the modern approa
hes stress the need to design programs su
h that it is easy to
hange them. As we have dis
ussed, the whole point of inheritan
e is to build new
lasses
out of old, and this idea will surely
ome in useful when requirements
hange.
Even if the requirements are well understood and xed (at least for that time in the
program development pro
ess), designing a large program is tri
ky. It greatly helps if the
program
an be designed as a
olle
tion of mostly independent fun
tions or
lasses whi
h
intera
t with ea
h other in a limited,
learly dened manner. Su
h partitioning is helpful
in understanding the behaviour of the program and also
he
king that it is
orre
t: we
an
onsider testing the parts separately for example. But it also has another advantage: dierent
programmers
an work on the dierent parts simultaneously. As we will see, inheritan
e
based designs have mu
h to oer in this regard also.
Another modern programming trend is the use of
omponents. Most likely, to write
professional programs you will not use bare C++ (or any other programming language), but
will start from a
olle
tion of fun
tions and
lasses whi
h have been already built by others
(for use in other, similar proje
ts). You will adapt the
lasses for your use, and as we have
seen, this adaptation is natural with inheritan
e.
In the rest of this
hapter we will see two
ase studies. First we revisit our formula
drawing program. We will rewrite it using inheritan
e. It will turn out that this way of
writing makes it easier to maintain and extend. Next we will dis
uss the design of the
graphi
s in simple
pp. Inheritan
e plays a substantial role in its design. Finally, we will see
the
omposite
lass, whi
h will enable you to dene new graphi
al obje
ts whi
h are made
405
406
lass Formula{
prote
ted:
407
Instead of storing the operator expli
itly, we are using a fun
tion op whi
h will return it.
The fun
tion will be dened only in
lasses in whi
h the operator is known.
Next we dene the sub
lass Formula2h of expressions whi
h require a horizontal layout.
lass Formula2h : publi
Formula2{
publi
:
void setSize(){
lhs->setSize();
rhs->setSize();
des
ent = max(lhs->getDes
ent(), rhs->getDes
ent());
width = lhs->getWidth() + textWidth(op()) + rhs->getWidth();
height = des
ent + max(lhs->getHeight() - lhs->getDes
ent(),
rhs->getHeight() - rhs->getDes
ent());
}
void draw(float
lx, float
ly){
lhs->draw(
lx,
ly);
rhs->draw(
lx+lhs->getWidth()+textWidth(op()),
ly);
Text(
lx+lhs->getWidth()+textWidth(op())/2,
ly, op()).imprint();
}
};
These fun
tion implementations are similar to the
ase '+' of the
orresponding fun
tions on
Node(Se
tion ??). The only dieren
e is that we are using a
essor fun
tions instead of the
members height, width, des
ent and op is not a data member but a fun
tion member.
We
an now
reate
lasses to represent sums, dieren
es, and produ
ts.
408
These
lasses merely give a
onstru
tor, and the operator. Noti
e that the layout aspe
ts
have been dealt with in the
lass Formula2h.
We
ould analogously dene an Formula2v
lass, whi
h does verti
al layout of its operands.
However, it is unlikely there will be many operators requiring a verti
al layout with a separating horizontal bar. So we dire
tly dene the Div
lass.
lass Div : publi
Formula2{
stati
onst float Bheight = 20; //spa
e for horizontal bar.
publi
:
Div(Formula* lhs1, Formula* rhs1){ lhs = lhs1; rhs = rhs1; }
string op(){return "/";}
void draw(float
lx, float
ly){
Line(
lx,
ly,
lx+width,
ly).imprint();
lhs->draw(
lx+width/2-lhs->getWidth()/2,
ly-Bheight/2-lhs->getDes
ent());
rhs->draw(
lx+width/2-rhs->getWidth()/2,
ly+
Bheight/2+rhs->getHeight()-rhs->getDes
ent());
}
void setSize(){
lhs->setSize(); rhs->setSize();
width = max(lhs->getWidth(), rhs->getWidth());
height = lhs->getHeight() + Bheight + rhs->getHeight();
des
ent = rhs->getHeight() + Bheight/2;
}
};
This
orresponds to the
ode for the
ase '/' in the
orresponding fun
tions on Node (Se
tion ??). It also denes a
onstru
tor and the fun
tion op.
Finally, we need a
lass to represent literals, i.e. numbers or variables given in the
formula.
409
This
orresponds to the
ode for the
ase 'P' in the
orresponding fun
tions on Node (Se
tion ??).
We
an now give a simple main program whi
h
an use the above denitions to render
the formula 1 +
.
2
451 +35
5
int main(){
initCanvas("Formula drawing");
Sum e(new Literal("1"),
new Div(new Literal("2"),
new Sum(new Div(new Literal("451"),new Literal("5")),
new Literal("35"))));
e.setSize();
e.draw(200,200+e.getHeight()-e.getDes
ent());
}
getCli k();
410
Another important benet arises when we
onsider adding new fun
tionality to the program. Suppose we want to implement layouts of exponential expressions. As you will see,
we
an do this without tou
hing any of our old les (ex
ept the main program le, if we wish
to use exponential expressions, of
ourse). The key benet of this strategy is: we
an be sure
that when we add exponential expressions, there isnt even a remote
han
e of damaging the
old working
ode. Programmers (deservedly) tend to be paranoid about their
ode, and this
kind of reassuran
e is useful. Noti
e that if the old
ode was written by one programmer, and
the new one by another, then it is very
onvenient if one programmer's
ode is not tou
hed
by another. This way there is
larity about who was responsible for what.
24.1.3 Adding exponential expressions
We will add a
lass Pow that will represent exponential expressions. This will be a sub
lass
of Formula2 sin
e it has 2 operands.
lass Pow : publi
Formula2{
publi
:
Pow(Formula* lhs1, Formula* rhs1){ lhs = lhs1; rhs = rhs1; }
string op(){return "^";}
void draw(float
lx, float
ly){
lhs->draw(
lx,
ly);
rhs->draw(
lx+lhs->getWidth(),
ly - (lhs->getHeight() - lhs->getDes
ent())
- rhs->getDes
ent()
);
}
void setSize(){
lhs->setSize(); rhs->setSize();
width = lhs->getWidth() + rhs->getWidth();
height = lhs->getHeight() + rhs->getHeight();
des
ent = lhs->getDes
ent();
}
};
The basi
idea is to layout the exponent above and to the right of the base. The detailed
expressions whi
h de
ide how to position what are obtained in the manner of Se
tion ??,
and are left for you to gure out. Using this
lass is simple, to represent X + Y Z we simply
write Sum(new Literal("X"), new Pow(new Literal("Y"), new Literal("Z"))).
The key point to appre
iate is that this new
ode
an be developed independently, in
a new le, without even having the rest of the
ode, only the
lass header les would be
needed.. If we had used the
oding approa
h of Se
tion 22.1, we would need to have and
modify the old
ode.
411
We will dis
uss the role played by inheritan
e in the design of the simple
pp graphi
s system.
Although this system is quite small by standards of real graphi
s systems, we will not dis
uss
the entire system here, but only some relevant portions of it.
Brie
y stated, the
ore spe
i
ation of the system is: allow the user to
reate and manipulate graphi
al obje
ts on the s
reen. This statement is very vague, of
ourse. What
does it mean to manipulate obje
ts? As you know, in simple
pp, manipulate simply means
move, rotate, s
ale. There are also other questions: what kind of primitives is the user to be
given? Will the user need to spe
ify how ea
h obje
t appears in ea
h frame (like the pi
ture
frames in a movie) or will the user only state the in
remental
hanges, e.g. move obje
t
x, whi
h means the other obje
ts remain un
hanged? As you know, we have opted for the
latter. And then of
ourse there is the question of what kinds of obje
ts we
an have. As you
know, simple
pp supports the following kinds of graphi
al obje
ts:
ir
les, lines, re
tangles,
polygons, turtles and text.
So in the rest of this se
tion, we
onsider the problem of
reating and manipulating the
obje
ts given above, in the manner des
ribed above. We will not dis
uss issues su
h as the
pens asso
iated with ea
h obje
t, or graphi
al input, as in the getCli
k
ommand.
Clearly, su
h a system must have the following
apabilities:
1. It must be able to keep tra
k of the obje
ts
reated by the user so far.
2. It should be able to display the obje
ts on the s
reen.
3. It should be able to manipulate the obje
ts as requested, e.g. move an obje
t.
Let us take item 2 rst. simple
pp is built on top of the X Windows system, whi
h provides
fun
tions that
an draw lines, ar
s, polygons,
ir
les (lled and non-lled) on the s
reen, at
the required pla
e. So we
all these fun
tions. Item 3 is also relatively easy. With ea
h
obje
t we asso
iated some
onguration data, whi
h says how large it is to be drawn, in
what orientation, and where. Note that this data is distin
t from the shape data. Item 1
is also not di
ult in prin
iple: we merely keep a list or a set of some kind in whi
h we
pla
e ea
h obje
t. To
omplete this very high level des
ription we need to answer one more
question: when should the obje
ts be displayed? The simplest answer to this is: whenever
the user
hanges the state of any obje
t,
lear the entire display and display all obje
ts again.
Inheritan
e is useful for fa
ilitating many of the a
tions des
ribed here.
It should be
lear that we have the
ategory of all graphi
al obje
ts, and then sub
ategories
orresponding to dierent types of obje
ts, e.g.
ir
les. So
learly, these
orrespond to
sub
lasses of the
lass representing the
ategory ALL of all obje
ts. Our
ategories indeed
form a heirar
hy, and so do the asso
iated
lasses, as shown in Figure 24.1. The
lass asso
iated with the
ategory of all obje
ts is
alled Sprite, in honour of the S
rat
h programming
environment (s
rat
h.mit.edu), where the name is used for a similar
on
ept.
The heirar
hy fa
ilitates storing of information as follows. In the Sprite
lass we keep
all attributes that are
ommon to all graphi
s obje
ts. At rst glan
e, you might think
that pre
ious little might be
ommon to all the very dierent looking obje
ts:
ir
les, lines,
polygons and so on. But as mentioned earlier, when a graphi
s obje
t is to be displayed on
the s
reen, it will have a position, orientation, s
ale in addition to its shape. It will also have
412
ALL (Sprite)
Circle
Line
Polygon
Rectangle
Text
Turtle
Be
ause the elements of A
tiveSprites have type Sprite*, they
an hold pointers to any
graphi
al obje
t. When the obje
ts are to be drawn, we simply iterate over the queue and
exe
ute the paint method of the obje
t.
for(int i=0; i < A
tiveSprites.size(); i++){
A
tiveSprites[i->paint();
}
The paint method is virtual, and so the paint method in the
lass of the obje
t is used.
This is similar to the way we used a ve
tor of Verb* to store regular and irregular obje
ts
and invoked the past tense virtual fun
tion on them in Se
tion 23.4.
In summary, inheritan
e gives us three main benets. It is easy to organize our data
strorage manner: the Sprite
lass stores the
onguration related data and the other
lasses
413
lass Sprite{
prote
ted:
double x,y;
// position on the
anvas
double orientation; // angle in radians made with the x axis
double s
ale;
// s
aling fa
tor
Color fill_
olor;
// Color is a data type in the X windows Xlib pa
kage.
bool fill;
// whether to fill or not
...
publi
:
Sprite();
Sprite(double x, double y);
...
void forward(double dist);
...
virtual void paint()=0;
...
}
lass Cir
le : publi
Sprite{
private:
double radius;
publi
:
Cir
le();
Cir
le(double x, double y, double radius=10);
void init(double x, double y, double radius=10);
virtual void paint()
};
414
store the shape related data. Also be
ause of polymorphism and virtual fun
tions, we
an
store pointers to dierent types of graphi
al obje
ts (but only subtypes of Sprite) in a single
ve
tor, and iterate over the ve
tor. Finally, we
an add new shapes easily: we simply dene
a new shape
lass whi
h is a sub
lass of Sprite, without having to modify any existing
ode.
We have somewhat simplied the des
ription of simple
pp graphi
s in order to explain
the use of inheritan
e. The a
tual system is more sophisti
ated. We see an example of this
sophisti
ation next.
We dis
uss how you
an dene a
lass whose instan
es are
omposite, i.e. a single instan
e
an
ontain several simple obje
ts. On
e built, you
an use the
lass in your program to
reate instan
es. In dening a
omposite obje
t you need inheritan
e as we will see.
Suppose you want to draw many
ars on the s
reen. It would be ni
e if you
ould
design a
lass Car whi
h you
ould then instantiate to make many
ars. A
ar is a
omplex
obje
t: it
annot be drawn ni
ely using just a single polygon, or a single
ir
le. It will
require several simple obje
ts that simple
pp provides. These simple obje
ts will have to
be grouped together, and often be manipulated together, e.g. if we want the
ar to move, we
really mean to move all its
onstituent parts. The
lass Composite whi
h we dis
uss next,
will allow you to group together obje
ts. We
an then dene a Car
lass by inheriting from
the Composite
lass.
Our Composite
lass primarily serves as a "
ontainer" to hold other graphi
s obje
ts. It
has a frame of referen
e, relative to whi
h the
ontained obje
ts are spe
ied. The Composite
lass has been dened as a sub
lass of the Sprite
lass. Thus it inherits member fun
tions
su
h as move, forward, rotate from the Sprite
lass. The Composite
lass is designed so
that the member fun
tions will
ause the
ontained obje
ts to respond appropriately, i.e.
when you move a Composite, everything inside gets moved. However, you
an override
these methods if you wish. For example, suppose your
omposite obje
t
onsists of the
body of a
ar and its wheels. When you
all forward on this, by default everything will
go forward. You might want the wheels to rotate in addition to moving forward. This
an
be a
omplished by overridding. You
an also additionally dene your own new member
fun
tions whi
h do new things. For example, the
ar might have a light on the top and there
ould be a member fun
tion whi
h
auses the light to
hange
olour from white to yellow
(suggesting it is swit
hed on). This
ould be done using a new member fun
tion.
Using the Composite
lass is fairly easy. There are only three important ideas to be
understood: the notion of ownership, and the Composite
lass
onstru
tor.
24.3.1 Ownership
A detail we have hidden from you so far is: every graphi
s obje
t has an \owner". When we
say that obje
t X owns obje
t Y, we merely mean that obje
t Y is spe
ied relative to the
oordinate frame of obje
t X. For the obje
ts you have been
reating so far, the owner was
the
anvas: the obje
ts were drawn in the
oordinate frame of the
anvas. When an obje
t
is
reated as a part of a
omposite obje
t, it must be drawn relative to the frame of the
415
omposite obje
t, and hen
e must be owned by the
omposite obje
t. Thus an important
step in dening a
omposite obje
t is to de
lare it to be the owner of the
ontained obje
ts.
To do this, the
onstru
tor of every graphi
al obje
t is provided with an optional argument named owner. This argument takes value NULL by default whi
h simple
pp interprets
to mean the
anvas. Thus so far we did not tell you about this argument, and you didnt not
spe
ify the value, and hen
e simple
pp made the
anvas the owner of all the obje
ts you
reated. If you want to indi
ate a dierent owner, you instead pass a pointer to that owner.
Sin
e we
reate
ontained obje
ts in the body of the
omposite, we must pass a pointer to
the
omposite obje
t itself. As you know, inside the denition of an obje
t, the keyword
this denotes a pointer to the obje
t. So the extra argument must have this as its value.
24.3.2 The Composite
lass
onstru
tor
The Composite
lass
onstru
tor has the following signature.
Composite(double x, double y, Composite* owner=NULL)
Here the last argument owner gives the owner of the
omposite obje
t being dened, and
x,y give the
oordinates of the
omposite obje
t in the frame of its owner. As mentioned
earlier, if you do not spe
ify this argument, it is taken as NULL, indi
ating that the
anvas
is the owner. The owner argument must be spe
ied if this
omposite obje
t is itself a part
of another
omposite obje
t. This kind of
ontainment is allowed and we will see an example
shortly.
24.3.3 A Car
lass
We now
onstru
t a Car
lass. Our
ar will
onsist of a polygonal body, and two wheels.
We will give the wheels some personality by adding in spokes. So we will model a
ar as
a
omposite obje
t,
onsisting of the body and the wheels. But note that a wheel itself
ontains a
ir
le representing the rim, and lines representing the spokes. So the wheel will
itself have to be represented as a
omposite obje
t. Note that we allow one
omposite obje
t
(e.g. a
ar) to
ontain other ordinary obje
ts (e.g. body) or other
omposite obje
ts (e.g.
wheels).
We begin by dening a
lass for wheels.
lass Wheel : publi
Composite{
Cir
le *rim;
Line *spoke[10;
publi
:
Wheel(double x, double y, Composite* owner=NULL) : Composite(x,y,owner) {
rim = new Cir
le(0,0,50,this);
for(int i=0; i<10; i++){
spoke[i = new Line(0, 0, 50*
os(i*PI/5), 50*sin(i*PI/5), this);
}
}
};
416
There are two private data members. The member rim whi
h is dened as a pointer to the
Cir
le obje
t whi
h represents the rim of the wheel. Likewise spoke is an array of pointers
to ea
h spoke of the wheel. The obje
ts themselves are
reated in the
onstru
tor. This is a
very
ommon idiom for dening
omposite graphi
s obje
ts.
The
onstru
tor
ustomarily takes as argument a pointer to the owner of the
omposite
obje
t itself, and the position of the
omposite obje
t in the frame of the owner. It is
ustomary to assign a default value NULL for the owner parameter, as dis
ussed earlier. The
initialization list Composite(x,y,owner) merely forwards these arguments so that the
ore
omposite obje
t is
reated at the required
oordinate and gets the spe
ied owner. Inside
the
onstru
tor, we
reate the sub-obje
ts. So we
reate the
ir
le representing the rim, and
as you
an see we have given it an extra argument this, so that the Wheel obje
t be
omes
the owner of the rim. Likewise we
reate lines at dierent in
linations to represent the
spokes, and even here the extra argument this
auses the lines to be owned by the Wheel
obje
t.
The Car
lass
an be put together by using Wheel instan
es as parts.
lass Car : publi
Composite{
Polygon* body;
Wheel* w1;
Wheel* w2;
publi
:
Car(double x, double y, Color
, Composite* owner=NULL)
: Composite(x,y,owner){
double bodyV[9[2={{-150,0}, {-150,-100}, {-100,-100}, {-75,-200},
{50,-200}, {100,-100}, {150,-100}, {150,0}, {-150,0}};
body = new Polygon(0,0, bodyV, 9, this);
body->setColor(
);
body->setFill();
w1 = new Wheel(-90,0,this);
w2 = new Wheel(90,0,this);
}
void forward(double dx, bool repaintP=true){
Composite::forward(dx,false); // super
lass forward fun
tion
w1->rotate(dx/50,false); // angle = dx/radius
w2->rotate(dx/50,false);
if(repaintP) repaint();
}
};
As will be seen, the private members are the pointers to the body and the two wheels. In
the
onstru
tor, the body is
reated as a polygon. We have provided a parameter in the
onstru
tor whi
h
an be used to give a
olour to the body. Finally, the wheels are
reated.
For all 3 parts, the last argument is set to this, be
ause of whi
h the parts be
ome owned
by the Car obje
t, as we want them to be.
The denition also shows the forward fun
tion being overridden. As dis
ussed, we want
the
ar to move forward, whi
h is a
omplished by
alling the forward fun
tion of the super-
417
lass. But we also want the wheels to turn; this is a
omplished by rotating them. Clearly,
if the
ar moves forward by an amount dx, then the wheels must rotate by dx/r radians,
where r is the radius of the wheels. But why does the rotate fun
tion
alled with an extra
argument false? And why is the fun
tion repaint
alled? We explain these next.
24.3.4 Frames
Another detail of simple
pp graphi
s whi
h we have withheld from you is that all the
onguration
hange
ommands, i.e. forward, move, rotate and so on have an additional
argument repaintP whi
h takes the default value true. If repaintP is true, then the
anvas
is repainted as dis
ussed in Se
tion 24.2 after every
onguration
hange. If repaintP is
false, then the repainting is not done, only the
onguration
hange is re
orded.
This feature is useful espe
ially when invoking a
onguration
hange
ommand on subobje
ts
omprising a
omposite obje
t. We do not want repainting to happen after a move of
ea
h subobje
t. It is ine
ient and also
auses visually annoying. Rather we want repainting
to happen on
e, after the
onguration
hange is re
orded for all subobje
ts. This is what
the
ode above a
omplishes: no repainting happens after the Composite::forward as well
as the two w...->rotate operations. Repainting is done only at the end unless it is disabled
by the
aller to Car::forward.
24.3.5 Main program
Finally, here is a main program that might use the above denitions.
int main(){
initCanvas("Car",0,0,800,800);
Car
(300,300,COLOR("blue"));
Car d(300,600,COLOR("red"));
d.s
ale(0.5);
getCli
k();
The main program
reates two
ars, one blue and another red. The red
ar is then s
aled
to half its size. This
auses all the
omponents of the
ar to shrink { this is handled
automati
ally by the
ode in the Composite
lass.
Finally, we move the
ars forward. When you exe
ute this, the
ar wheels should appear
to roll on the ground. Further, the wheels of the smaller
ar should appear to have twi
e as
many rotations per unit time be
ause the smaller wheel has half the radius but is travelling
the same distan
e as the larger wheel.
418
Exer ises
1. To the program of Se
tion 24.1 add the
apability of drawing summation formulae, i.e.
formulae
B
X
2.
3.
4.
5.
In this, the rst 3 lines dened 3 obje
ts. As you might guess, the numbers following
the shape names are the arguments for the
orresponding
onstru
tors. For the rest
of the sequen
e, the
onstru
ted obje
ts respe
tively get numbered 0, 1, 2 (or till as
many obje
ts as we have dened). In the rest of the
ommand sequen
e, a graphi
al
obje
t will be referred to by its number. Thus the
ommand Move 0 50 50
auses
obje
t 0 (the rst
ir
le) to be displa
ed by 50, 50 along x and y dire
tions. Likewise
Left 1 30
auses the re
tangle to be rotated left by 30 degrees. You may also dene
ommands to wait for spe
ied amount of time.
Write the program. Note that Sprite obje
ts
annot be stored in a ve
tor. However,
you
an
reate the Sprite on the heap and store a pointer to it in a ve
tor.
Chapter 25
Dis
rete event simulation
We have already dis
ussed the general notion of simulation: given the
urrent state and laws
of evolution of a system, predi
t the state of the system at some later date. In Chapter 17, we
onsidered the simulation of heavenly bodies, as might be required in astronomy. However,
simulation is very useful also for more mundane, down to earth systems. A very
ommon
use of simulation is to understand whether a fa
ility su
h as a restaurant or a train station
or airport has enough resour
es su
h as tables or platforms or runways to satisfa
torily serve
the
ustomers or travellers that might arrive into it.
As a
on
rete example, suppose we want to de
ide how many dining tables we should put
in a restaurant. If we put too few tables, we will not be able to a
ommodate all
ustomers
who might want to eat in our restaurant. On the other hand, ea
h table we put in has a
ost.
So we might want to determine, for ea
h T where T is the number of tables we put, what our
revenue is likely to be. Knowing the revenue and the
ost of putting up tables, we should be
able to
hoose the right value of T . To do this analysis, we of
ourse need to know something
about how many
ustomers want to eat in our restaurant, and when. This
an be predi
ted
only statisti
ally. We will assume that we are given p, the probability that a
ustomer arrives
in any given minute of the \busy period" for restaurants, say 7 pm to 10 pm. Ideally, we
should
onsider not single
ustomers but a party
onsisting of several
ustomers, and the
possibility that a
ustomer party might need more than one table. However, for simpli
ity
we will assume that
ustomers arrive individually and are seated individually at separate
tables. On arrival, a
ustomer o
upies the table for some time during whi
h he eats, and
then leaves. Suppose that we are also given a fun
tion e(t), that gives the probability that
a
ustomer eats for t minutes. For simpli
ity, suppose that the revenue is proportional to
the total number of
ustomers. Can we determine the total revenue for an arbitrary value
of T , the number of tables we have? Note that an arriving
ustomer will leave if all tables
are o
upied.
Problems su
h as this one
an sometimes be solved analyti
ally, i.e. we
an write the
expe
ted revenue as a reasonably simple, easily evaluatable fun
tion of the dierent parameters. But this is often not possible if the probability model is
omplex. For example, in the
above des
ription, we implied that the eating time probability distribution e(t) is a fun
tion
only of t. But if there are many people in the restaurant, the servi
e might will be slower
and ea
h
ustomer will o
upy the table for longer periods. Thus perhaps the distribution
should be a fun
tion of the number of
ustomers present as well. In this
ase, it will be
419
420
In a
osmologi
al system ea
h star attra
ts, and thereby ae
ts, every other star at every
time instant. In
ontrast, in many other real life systems the entities intera
t with ea
h
other relatively infrequently. For example, in a restaurant, a
ustomer has intera
tions
su
h as getting a table, ordering food, being served, paying the bill and leaving. These
intera
tions are typi
ally separated by long periods during whi
h the
ustomer is typi
ally
neither disturbed, nor demands any attention. Thus it is
ustomary to
all this latter kind
of system a dis
rete time system, whereas the former kind is
alled a
ontinuous time system.
25.1.1 Dis
rete time systems
In general, a dis
rete time system
onsists of a
ertain number of entities whi
h might have
states that
an
hange over time. In addition there is a list of enabled/impending events,
i.e. events that are known will o
ur, ea
h at some spe
ied time in the future. When an
event a
tually o
urs, it may
ause (a) the state of some of the entities to
hange, and (b)
more events to be
reated. The new state depends upon the event that happened as well
as the old state. Likewise the new events that are
reated also depend upon the event that
happened and the old state, i.e. the state of the entities just before the event happened. It
is to be further noted that the state of a dis
rete system
an
hange or new events
reated
only during the o
urren
e of some event.
Many real life systems
an be
onsidered to be dis
rete time systems. For example,
onsider our restaurant system as des
ribed earlier. We have a very simple model in whi
h
ustomers approa
h and they are seated if there is a table available, and then leave after a
ertain randomly
hosen time duration. If there is no table available, then we may
onsider
that they do not even enter the restaurant. In this
ase it is natural to think of the tables
in the restaurant as the entities, and these
an either be in the o
upied or not o
upied
states. We may
onsider two kinds of events:
ustomer approa
h, and
ustomer Exit. Here
is what happens during the o
urren
e of these events:
421
: State of the entities in the system. Initialized to the state at the
urrent time.
L : List of events. Initialized to
ontain the events that are known will happen after the
urrent time.
1. Let e be the event with the smallest time of o
urren
e.
2. Remove e from L.
3. Pro
ess e. This will
ause
hange to S . New events may also be added to L.
4. Repeat from step 1 if L is not empty.
S
422
The me
hanism of variable
apture in lambda expressions will enable the fun
tion to get
a
ess to the system state, as we will see.
Se
ond, for
onvenien
e we will make the event list L of Figure 25.1 hold pairs of the form
(e; t) where e is the event and t the time at whi
h it is to happen. The (e; t) pairs that we
need will be represented using the pair
lass in STL (found in the header le <util>). We
use the type double to represent time, so we need a pair of Event and double. To simplify
the subsequent dis
ussion it is
onvenient to dene:
typedef pair<Event,double> ETpair;
Remember that the event list,
onsisting of pairs (e; t), is a
essed in a very spe
ial manner.
As seen in Figure 25.1, we do not remove arbitrary pairs from the list but only those with
smallest t, though we may insert pairs in any order.
The priority queue template
lass in STL (found in header le <queue>) supports
nearly this mode of operation. It has operations return the \largest" element in the queue,
where we
an dene what is largest. Here is the prototype for priority queue.
template<
lass T,
// argument 1
lass C = ve
tor<T>,
// argument 2
lass
mp = less<typename C::value_type> // argument 3
> priority_queue;
A prototype of a template is similar to a fun
tion prototype: both give the arguments and
their types, and possible default values. In this
ase, the priority queue template takes 3
arguments. The se
ond argument C spe
ies the
ontainer
lass using whi
h the elements
in a priority queue will be stored. The default option is to use a ve
tor, whi
h we do not
wish to
hange. The third argument
mp is used to de
ide what to return. It defaults to
423
less, whi
h is simply the operator <. Thus of all the elements stored in it a priority queue
returns that element x su
h that there is no y su
h that x < y. Thus a largest element is
returned. To get a smallest instead, we must make the third argument be the equivalent of
the > operator. This is done using the
lass
ompareETpair, shown in Figure 25.2.
The
lass sim shown in Figure 25.2 is the main simulation
lass. The member pq in it
holds the event list. In addition to the event list, the
lass also has a member time whi
h is
meant to hold the time till whi
h the system has been simulated. We start o by initializing
time to 0.
This
lass sim is a little unusual. It is not meant to be instantiated! The members time
and pq are dened as stati
elements. Thus there will be only one
opy of pq and time,
but this is exa
tly what we want. The fun
tionality is provided through four stati
member
fun
tions. Note that stati
member fun
tions
an be a
essed outside the
lass denition as
lass-name::fun
tion-name.
The member fun
tion post allows an event to be posted, i.e.
reated and inserted into
the priority queue. The rst argument laten
y is the time duration after whi
h the event is
to happen, from the
urrent time of the system. The se
ond argument e is the event itself.
The fun
tion thus inserts the pair (e, time + laten
y) into the priority queue.
The member fun
tion getTime merely returns the
urrent time, i.e. the time till whi
h
the system has been simulated.
The member fun
tion pro
essAll pro
esses posted events till nothing is left to pro
ess.
For this, it repeatedly pi
ks the pair (e; t) in the priority queue with the smallest time t and
alls the event e. Note that when we pi
k the element with the smallest time t, we know
that the system time
an be advan
ed to t. Thus the member time is updated to t. Note
that the smallest element in the queue
an be removed by using the pop method, or we
an
just examine it using the top method.
The last method, log, is for reporting
onvenien
e. It is used to print messages to the
s
reen, but ea
h message is prefa
ed by the
urrent time. Note that a referen
e to the
onsole output,
out is returned, so that the rest of the message
an be appended using the
<< operator.
sim::pro essAll();
// event A
// event B
The a
tion asso
iated with ea
h event is simple: a message is printed, with the
urrent
simulation time.
424
lass sim{
typedef std::fun
tion<void()> Event;
typedef pair<Event,double> ETpair;
stru
t
ompareETpair{
bool operator()(ETpair p, ETpair q){return p.se
ond > q.se
ond;}
};
stati
double time; // time to whi
h the system has been simulated
stati
priority_queue< ETpair, ve
tor<ETpair>,
ompareETpair> pq;
publi
:
stati
void post(double laten
y, Event e){
pq.push(make_pair(e, time + laten
y));
}
stati
double getTime(){return time;}
stati
void pro
essAll(double tmax=1000){
while(!pq.empty() && time < tmax){
ETpair ETp = pq.top();
time = ETp.se
ond;
pq.pop();
ETp.first();
}
}
};
425
When you exe
ute, the rst two statements will
ause events A, B to be posted to o
ur
15 and 5 time units respe
tively from the
urrent time. The simulation starts with time 0,
so these events will o
ur at time 15 and 5 respe
tively. The third statement in the program
will
ause the system to be evolved, i.e. all the events in the queue will be pro
essed, in the
order of the time at whi
h they are meant to o
ur. Thus event B, s
heduled at time 5, will
get pro
essed, rst, i.e. its lambda expression will be
alled. This will
ause the message
\Event B." to be printed, prexed by the simulation time, be
ause of the
all to sim::log.
After that event A will be pro
essed. Thus you would get the following output.
5) Event B.
15) Event A.
The point to be noted is that in the program the
reation of event A happens before the
reation of event B. However, the time of A, 15, is larger than the time of B, 5. Hen
e B
happens before A during the exe
ution.
As we dis
ussed in Se
tion 25.1.1, events in a dis
rete time system will in general
ause
the state of some of the entities to
hange, and also
ause the
reation (posting) of new
events. We will now
onsider an example in whi
h events do both of these. This example
will be
ontain the
ore idiom used in all future simulations.
In the program below, we have 4 events, A, B, C, D. We also have a variable
ount. We
will see that the events will a
ess this variable (\simulation state"), and in fa
t event D will
be posted during the o
urren
e of event C.
int main(){
int
ount=0;
sim::post(15, [&(){sim::log() << "A.
ount: "<<
ount++ <<endl;});
sim::post(5, [&(){sim::log() << "B.
ount: "<<
ount++ <<endl;});
sim::post(10, [&(){
sim::log() << "C.
ount "<<
ount++ <<endl;
sim::post(100, [&(){sim::log() << "D.
ount: " <<
ount++ << endl;});
});
sim::pro
essAll();
}
//A
//B
//C
//D
The program begins by setting
ount to 0, and then posting the events A, B, C for time 15,
5, 10 respe
tively. Then sim::pro
essAll
auses the posted events to be pro
essed.
The event posted for the earliest time is event B, and hen
e that gets pro
essed rst.
This will
ause the time of o
urren
e of B to be printed and also the value of
ount. Note
that
ount has been
aptured into the event by referen
e, and hen
e the value at the time
of o
urren
e of the event will be printed. Thus we will see the following output.
5) B.
ount: 0
Note that while printing,
ount is also in
remented. Sin
e
ount was
aptured by referen
e,
the in
rement will ae
t the variable
ount as dened in the rst line of the program. Thus
this will be
ome 1.
426
The event posted for the next earliest time, 10, is event C, whi
h is then pro
essed. The
pro
essing starts o by printing the message and the value of
ount. Noti
e that even in
event C the variable
ount has been
aptured by referen
e. Thus the following message will
be printed
10) C.
ount: 1
and
ount will be in
remented. But the pro
essing of event C does not stop after printing
the message. After printing, the
all sim::post is exe
uted, whi
h
auses event D to be
reated, as a part of the o
urren
e of the event C. Event D is to o
ur at 100 steps after
the
urrent time, i.e. 10. Thus event D gets posted for time 110. Thus when the pro
essing
for C nishes, there are two events, A, D in the queue, and the value of
ount is 2.
Again, the earliest of the events is pro
essed, i.e. event A. This will print a message:
15) A.
ount: 2
and
ause
ount to in
rement to 4. Note the time in the message { sim::log will indeed
print 110, the time at whi
h we expe
t the event to o
ur.
We now show how to program the restaurant simulation. It is
hara
terized by a number of
parameters, the restaurant
apa
ity (i.e. the number of tables), the time duration in minutes
for whi
h the restaurant remains open, the probability that a
ustomer arrives in the next
minute, and the minimum and maximum eating time for the
ustomers. The restaurant
state
onsists of the number of tables that are o
upied.
At ea
h minute that the restaurant is open, a
ustomer
an arrive with the spe
ied
probability. We model ea
h arrival as an event. On arrival, the
ustomer
he
ks if there are
uno
upied tables, if not the
ustomer leaves and we should print a message to that ee
t.
If however there are uno
upied tables, then the
ustomer o
upies one and sits down to eat.
Then an eating duration is randomly
hosen, and the
ustomer must exit the restaurant after
that duration. We model the exit also as an event. Thus the arrival event for the
ustomer
must perform the above a
tions, in
luding the
reation of the exit events. All this goes into
the lambda expression for the arrival events. On exit, it is only ne
essary to de
rement the
number of o
upied tables. Thus the de
rementation
ode must be pla
ed in the lambda
expression of the exit event.
Thus the
ode is as follows.
int main(){
onst int
apa
ity=5;
// number of tables
onst int duration=180;
// minutes open
double arrivalP=0.1, eatMin=21, eatMax=40;
427
int nO
upied = 0 ;
int id = 0;
25.3 Resour es
Many real life systems
ontain resour
es that are s
ar
e. For example, the same ma
hine
might be needed to pro
ess several jobs of whi
h it might be
apable of pro
essing only one
at any instant. In su
h
ases, the jobs must wait to gain ex
lusive a
ess to the ma
hine.
As an example,
onsider a roadside
oee shop manned by a single server. Suppose the
shop serves beverages and food, all of whi
h require some eort and time from the server. If
a
ustomer arrives while the server is busy with a previous
ustomer, then the new
ustomer
must wait. So a line of waiting
ustomers might form at popular
oee shops. Given the
probability of
ustomer arrival and the probability distribution of the servi
e time,
an we
predi
t how mu
h business the shop gets and also how long the line be
omes?
We will develop a Resour
e
lass whi
h will make it easy to write su
h simulations.
25.3.1 A Resour
e
lass
So far, we have said that events
an be
reated and posted to o
ur at a
ertain time in
the future. We will extend this notion and allow an event to be posted to o
ur when a
ertain resour
e be
omes available. If the resour
e is immediately available, then the event
will o
ur immediately. Otherwise, it will be put in a queue asso
iated with the resour
e.
When the resour
e be
omes available, be
ause some other event releases it, the waiting event
will be taken o the queue and posted for immediate exe
ution.
428
We
an
reate instan
es of the Resour
e
lass to model resour
es whi
h must be used
ex
lusively. The data member inUse in Resour
e whi
h if true denotes that the resour
e
(represented by the instan
e) is deemed to be in use, and if false denotes that the resour
e
is available. In addition, the resour
e
lass has a data member q whi
h is a queue from the
standard template library. You
an add elementes to (the end of) a queue by
alling the
member fun
tion push, examine the element at the front of a queue by
allig the member
fun
tion front, and remove the element at the front by
alling the member fun
tion pop.
The member fun
tion a
quire
an be used to get ex
lusive a
ess to the resour
e. It takes
as argument the event e that needs ex
lusive a
ess, i.e. the lambda expression whi
h will
be exe
uted when the resour
e be
omes available. If the resour
e is immediately available,
the event is posted for immediate exe
ution by
alling sim::post(0,e). That the resour
e
has been a
quired is represented by setting inUse = true. If the resour
e is not available,
then the event is put in the queue asso
iated with the resour
e.
The member fun
tion release
an be used to release a resour
e and make it available to
other events. If an event e is waiting in the queue (front) of a resour
e that is being released,
429
then e is posted for immediate exe
ution by
alling sim::post(0,e). Note that in this
ase
we do not need to
hange inUse: it was true before and must remain true be
ause we
assigned the resour
e to the waiting event. If there was no event waiting in the queue, then
we must set inUse = false, so that a future a
quire request
an su
eed immediately.
Finally, there is a member fun
tion reserve whi
h marks the resour
e as being in use
and returns true if and only if the resour
e was not in use earlier. We will see a use of this
in Se
tion 26.6.
25.3.2 Simple example
The Resour
e
lass is also a part of simple
pp, and so is immediately available for use.
Here is a simple example.
int main(){
Resour
e r;
sim::post(15, [&(){
r.a
quire([(){sim::log() << "Got it! \n";});
});
r.a
quire([&(){
sim::post(20, [&(){
r.release();
});
});
sim::pro
essAll();
The rst statement posts an event for time 10, in whi
h the resour
e r is sought to be
a
quired. Upon a
quisition a message will be printed giving the time at whi
h the a
quisiton
su
eeds. The se
ond statement seeks to a
quire r immediately (at time 0), and releases it
20 steps after a
quisition.
As you
an see, the a
quisition in the se
ond statement will su
eed, sin
e at time 0 the
resour
e is available. Thus the resour
e will get released 20 steps afterwards, i.e. at time 20.
Thus the a
quisition in the rst statement will su
eed at time 20. Thus that is when the
message \Got it!" will be printed. Thus the output of the program will be:
20) Got it!
Note that r needs to be
aptured in the se
ond statement. Sin
e r is shared between the
statements, it is
aptured by referen
e.
25.3.3 The
oee shop simulation
Using the Resour
e
lass, the simulation is easily written. The main program for simulating
a 180 minute duration is as follows.
430
int main(){
onst int duration=180;
double arrivalP = 0.1;
int id = 0;
Resour
e server;
// minutes open
The general outline is similar to the restaurant simulation. We need a Resour
e to model
the server, whi
h is
alled server in the
ode. Then for ea
h minute we simulate
ustomer
arrival, with the arrival probability arrivalP. If a
ustomer is deemed to arrive, then we
generate a random servi
e time for the
ustomer. Then we must perform the following
a
tions.
1. Print out a message saying that a
ustomer arrives, along with the servi
e time.
2. Request ex
lusive a
ess to the server.
3. On getting ex
lusive a
ess, we hold the server for the servi
e time.
4. After the servi
e time elapses, we release the server.
As you
an see, the
ode above exe
utes exa
tly these steps. However, the steps appear
nested. This is be
ause the
ode for an event is required to be pla
ed in the argument list
of the pre
eding event.
We note some important points about variable
apture. First, server is shared a
ross
iterations, and hen
e must be
aptured by referen
e. However, we want the value of the
variable id from the time of when the
ustomer arrival is posted. Hen
e id must be
aptured
by value. Finally servi
eT is a lo
al variable inside the loop, so it must be
aptured by
value. Hen
e we have asked that all variables be
aptured by value ex
ept for server whi
h
must be
aptured by referen
e.
431
Nagpur
500
Nashik
200
Mumbai
220
160
50
Pune
450
Satara
300
Kolhapur
In this se
tion we
onsider the single-sour
e-shortest-path problem. Suppose we are given
information about whi
h
ities are dire
tly
onne
ted by road, and the length of all su
h
roads. We want to travel from a given origin
ity to a destination
ity by road, passing
whatever
ities along the way, so that the total distan
e
overed is as small as possible. The
problem is di
ult be
ause there
an be several paths from the origin to the destination,
depending upon whi
h
ities you
hoose to pass through along the way. Of all su
h possible
paths, we want the shortest. We will fo
us on the problem of nding the the length of the
shortest path, the path themselves
an be identied with a little additional book-keeping,
whi
h is left for the Exer
ises. We dis
uss a
lassi
algorithm, attributed to Edsgar Dijkstra.
The algorithm a
tually nds the distan
e from the origin to all other
ities, be
ause that is
onvenient.
Dijkstra's algorithm
an be viewed as a
omputer analogue of the following physi
al
experiment you
ould undertake to nd the distan
es. For the experiment we need many
y
lists who
an ride at some
onstant speed, say 1 km/minute. Spe
i
ally, we need to have
as many
y
lists in ea
h
ity as there are roads leading out of it. If we do have su
h
y
lists,
here is how they
ould
ooperatively nd the length of the shortest paths.
To start with, all the
y
lists assemble in their respe
tive
ities. Ea
h
y
list is assigned
one road leading out of the
ity, and the job of the
y
list will be to travel on just that one
road when asked to. After the
y
list is
agged o somehow, she starts pedalling and rea
hes
the
ity at the other end of the road. Here she must
he
k if she is the rst
y
list to arrive.
432
If she is not the rst, i.e. someone arrived earlier, then she does nothing and stops. If she is
indeed the rst to arrive, then she
ags o all the waiting
y
lists to start pedalling. After
that her job is over.
Here is how the experiment starts o. At time 0, a
titious
y
list arrives into the origin
ity, and
ags o the
y
lists in that
ity. They then
ag o other
y
lists as des
ribed
above. The experiment ends when all
y
lists have nished their journey.
As an example, suppose our graph is the map of Figure 25.3, and we want the distan
es
from Nashik. So at time 0, a
titious
y
list arrives into Nashik and
ags o the
y
lists
there. So
y
lists start pedalling from Nashik to respe
tively Nagpur, Mumbai and Pune.
The
y
list from Nashik arrives at time 200 into Mumbai, where we are measuring time in
minutes from the start. She is the rst one to arrive there, so she
ags o the 3 Mumbai
y
lists who then start travelling towards Kolhapur, Pune, and Nashik respe
tively. Of these
3 the
y
list heading to Pune would rea
h 160 minutes later, at time 360. However, when
she rea
hes Pune, she would have found that the
y
list from Nashik has already arrived at
time 220. So the
y
list arriving from Mumbai into Pune would need to do nothing. In this
manner the pro
ess
ontinues.
We will show that: (a) the length of the shortest path from the sour
e to any
ity is
simply the time in minutes when the earliest
y
list arrives in that
ity! (b) we
an use
dis
rete event simulation to simulate this system.
We explain (a) rst. Let S denote the sour
e
ity, and C be any
ity. Let t be the time
at whi
h the rst
y
list arrives into C . We argue that there must be a path from S to
C of pre
isely this length. To see this,
onsider the
y
list that arrives into C . We follow
this
y
list ba
kward in time to the
ity from whi
h he started. There, he was
agged o
by some other
y
list, whom we follow ba
k in time, and so on. Eventually, we must rea
h
the
ity S , at time 0. In this pro
ess, note that we are not only going ba
k in time but
also
ontinuously travelling ba
k, at 1 km/minute. Thus, we must have
overed, ba
kwards,
exa
tly the same distan
e as the time taken. Thus we have proved that there exists a path
from S to C of length equal to the time at whi
h the rst
y
list arrives in C . We now prove
that it is the shortest.
Consider a shortest path P from S to C , the
ities on it being
;
; : : : ;
k in order, with
= S , and
k = C . Let di be the distan
e from
to
i along the path. We will prove that
the rst
y
list (
titious or real) to arrive at
i does so no later than di, for all i. Clearly,
this is true for i = 0: indeed a
y
list arrives at
at 0 = d . So assume by indu
tion that a
y
list arrives at
i at time di or before. Thus a
y
list must have left
i at time di or before
for
ity
i . But this
y
list travels at 1 km/minute, and hen
e
overs the distan
e di di
also in time di di. Hen
e he will arrive at
i at time at most di + di di = di . Thus
the indu
tion is
omplete. Thus we know that some
y
list must arrive at
k = C at time at
most the length of the shortest path P . But we proved earlier that the time of arrival must
equal the length of some path. Hen
e it follows that the rst
y
list arrives at time exa
tly
equal to the length of the shortest path.
We next show that our algorithm
an be programmed as a dis
rete event simulation.
0
+1
+1
+1
+1
+1
+1
433
434
stru
t City;
// forward de
laration, not definition.
stru
t Road{
City* toPtr;
// Where the road leads to
double length;
Road(City* ptr, double d){toPtr = ptr; length = d;}
};
stru
t City{
ve
tor<Road> roads;
double arrivalT;
// arrival time of first
y
list
City(){arrivalT = HUGE_VAL;}
// not arrived yet.
void arrive(){
if(arrivalT > sim::getTime()){
arrivalT = sim::getTime();
for(unsigned int i=0; i<roads.size(); i++){
sim::post(roads[i.length, [this,i(){roads[i.toPtr->arrive();});
}
}
}
};
stru
t RoadNetwork{
ve
tor<City>
ities;
RoadNetwork(
har* infilename) {
ifstream infile(infilename);
int n;
infile >> n;
ities.resize(n);
double dist;
int end1, end2;
while(infile >> end1){
infile >> end2 >> dist;
ities[end1.roads.push_ba
k(Road(&
ities[end2,dist));
ities[end2.roads.push_ba
k(Road(&
ities[end1,dist));
}
}
};
435
Dis
rete event simulation is a powerful paradigm. Using the
lasses developed in this
hapter,
you should be able to easily build some modest size simulations. We will see su
h an example
in the next
hapter.
436
The basi
ideas in dis
rete event simulation are as follows. Whatever system you wish
to simulate must rst be viewed as a
olle
tion of intera
ting entities. The intera
tions
onstitute the events. Ea
h event
an
ause the state of an entity to
hange, or
ause
additional future events to be posted. We express ea
h event as a lambda expression. When
the event happens, the asso
iated lambda expression exe
utes, and in this exe
ution we must
hange the state of the entities in the system or post additional events. Note that the
ode
for doing all this
ould be pla
ed textually inside the lambda expression, as was the
ase in
the restaurant and
oee shop simulations. Or inside the lambda expressions we
an just
pla
e a
all to a fun
tion whi
h
auses the state
hanges or posting of additional events, as
was the
ase in the shortest path algorithm simulation.
The
oding style for posting events and a
quiring resour
es is slightly tri
ky. Normally,
when we wish to perform one a
tion after another, we write the se
ond a
tion following the
rst. However, if event A
auses event B whi
h in turn
auses event C, then we would write
the lambda expression of C inside the lambda expression of B whi
h in turn
ould be in the
lamdba expression of A. Thus although the events happen in su
ession, in the
ode they
will appear nested. This needs some getting used to.
Note that lambda expressions make it
onvenient to express event posting and resour
e
a
quisition. You should make sure that you understand lambda expressions well, espe
ially
variable
apture.
Exer ises
1. Modify the restaurant simulation to report how many
ustomers left disappointed, how
long after the
losing time did the
ustomers stay around, the number of
ustomers in
the restaurant on the average.
2. Generalize the
oee shop problem so that there are several servers. This is also like
adding a waiting room to the restaurant. You will need to modify resour
e. Generalize
the
lass so that at most some k
lients
an be using the resour
e simultaneously. You
may nd it easier to do this if you do not keep tra
k of whi
h
lients are using the
resour
e, but just keep tra
k of how many
lients are using the resour
e.
3. Suppose every minute a
ustomer enters a store with a probability p. Suppose that on
the average ea
h
ustomer spends t minutes in the store. Then on the average, how
many
ustomers will you expe
t to see in the store? Little's law from queueing theory
says that this number will be pt. Modify the
oee shop simulation and verify Little's
law experimentally. The law requires that no
ustomers are turned away, and that the
average is taken over a long (really innite) time. So you should remove the
apa
ity
he
ks, and run the simulation for relatively long durations to
he
k. More
ode will
be needed to make all the measurements.
4. Write a simulation of a restaurant in whi
h
ustomers
an arrive in a group, rather
than individually. Suppose a group
an have upto 5 members, all sizes equally likely.
Suppose further that tables in the restaurant
an a
ommodate 4
ustomers, so if
a party of 5 arrives, then two adja
ent tables must be allo
ated. Thus, the party
must wait if two adja
ent free tables are not available. Write a simulation of su
h a
437
restaurant. Assume that the tables are in a single line, so tables i; i + 1 are adja
ent.
You will have to de
ide on how a table will be allo
ated if several tables are free: this
will ae
t how qui
kly you serve parties of 5 members.
5. Have an additional
ommand line argument whi
h gives the index of a destination
ity,
for the shortest path program. Modify the program so that it prints the shortest path
from the sour
e to the destination
ity, as a sequen
e of the numbers of the
ities on the
way. Basi
ally, in ea
h City you must store information about where the rst
y
list
arrived from. This will enable you to gure out how the shortest path arrives into a
City, re
ursively.
6. Modify the shortest path algorithm to use
ity names instead of
ity numbers in the
input le.
7. Build a simulator for a
ir
uit built using logi
gates. Consider the gates des
ribed in
Exer
ise 15 of Chapter 6. You should allow the user to build the
ir
uit on the graphi
s
window. You should also allow a delay to be entered for ea
h gate. A gate takes as
input values 1 or 0, and produ
es output values a
ording to its fun
tion. However, the
output value is reliably available only after its delay. Spe
i
ally, suppose some input
value
hanges at time t. Suppose this will
ause the output value to
hange. Then
the new
orre
t value will appear at the output only at time t + . During the period
from t to t + the value at the output will be undened. For this you should use the
value NAN supported as a part of the header le <
math>. The value NAN represents
\undened value", a
tually the name is an a
ronym for \Not A Number". This value
behaves as you might expe
t: do any arithmeti
with it and the result is NAN.
Chapter 26
Simulation of an airport
Suppose there are
omplaints about the e
ien
y of an airport in your
ity: say
ights get
delayed a lot. Is it possible to pinpoint the reason? Is it then possible to state the best
ure
to the problem: that you need to build an extra runway, or some extra gates, or perhaps
just build a
ompletely new, bigger airport? A simulation of the airport and how it handles
air
raft tra
an very mu
h help in making su
h de
isions.
The simulation will take as input information about the runways and other fa
ilities on
the airport, and about the air
raft arriving into the airport from the rest of the world. It
will then determine what happens to the air
raft as they move through the airport, what
delays they fa
e at dierent points. The average of these delays is perhaps an indi
ator of
the e
ien
y of the airport. To answer questions su
h as: how mu
h will an extra gate (or
runway or whatever) help, you simply build another simulation in whi
h the extra gate is
present, and
al
ulate the average delay for the new
onguration. In addition to textually
des
ribing what happens to ea
h air
raft as it progresses through the airport, it is also
desirable to show a graphi
al animation in whi
h we
an see the air
raft landing, taxiing or
waiting at gates. An animation is possibly easier to grasp { perhaps seeing the air
raft as
they move might dire
tly reveal what the bottlene
ks are.
The rst step in building a simulation is to make a
omputer model of the relevant aspe
ts
of the system being simulated. When you make a
omputer model, or a mathemati
al model,
of any entity, doubtless you have to throw away many details. A trivial example: the
olour
of the airport building is irrelevant as far as its ability to handle tra
, so that may be
ignored in our simulation. On the other hand, the number of runways in the airport is of
prime importan
e, and so
annot be ignored. Other fa
tors that perhaps
annot be ignored
in
lude the number of gates at whi
h air
raft
an park to take in and dis
harge passengers,
the layout of the taxiways that
onne
t the runways and the terminals. Other fa
tors that
are perhaps less important are the pla
ement of auxiliary servi
es (e.g. air
raft hangars) and
tra
asso
iated with these servi
es and how it might interfere with air
raft movements. In
general, the more details you in
orporate into your model, the more a
urate it is likely to
be. However, models with relatively few details might also be useful, if the details are
hosen
arefully.
In this
hapter, we will build a simulation of a simple airport. The simulation is very
simplisti
, but it does address several key problems that arise in su
h simulations.
438
439
It is possible to write a simulator whi
h simulates airports with arbitrary number of runways,
taxiways, gates and so on. However, for simpli
ity, we will
onsider the spe
i
airport
onguration shown in Figure 26.1.
In the gure, the two
rossing lines at the top are two runways. The other lines are
taxiways. The long horizontal line near the bottom is the main taxiway, and the nearly
verti
al segments on the sides we will refer to as the left and right taxiways respe
tively.
There are bran
hes going o the main taxiway to the gates. We have not shown the gates,
but they are supposed to be present at the end of these short bran
hes. So in this airport
there are meant to be 10 gates, whi
h we will number 0 through 9, right to left. The small
440
triangles are meant to represent air
raft. As you
an see there are three air
raft waiting, at
gates 0, 1, and 3, and three others on the runway and taxiways. If you ignore the bran
h
taxiways, the runways and the other taxiways
onstitute a single long path, starting in the
top left
orner, running
lo
kwise over itself to end in the top right
orner. We will
all
this the main path. Indeed, for simpli
ity, we will require that the main path be used in
the
lo
kwise dire
tion. Thus the runway starting at the top is the landing runway and the
runway ending at the top right is the takeo runway. The bran
h taxiways going to the
gates are expe
ted to be used in both dire
tions.
Our
onguration is rather simplisti
, ex
ept for the interse
ting runways. Interse
ting
runways are not rare, by the way { in fa
t the Mumbai airport has interse
ting runways,
whi
h is our inspiration for in
luding them. But of
ourse both the runways in Mumbai
an be used for takeos as well as landings, and the taxiways and gate pla
ements are more
elaborate.
26.1.1 Overall fun
tioning
At a high level, the operation of an airport
an be des
ribed as follows. Ea
h air
raft lands
and taxies to a gate. The air
raft then waits at the gate for a
ertain servi
e time. After
that the air
raft taxies to the runway and takes o. This entire pro
ess has to be
ontrolled
by the airport authorities so as to ensure safety and e
ien
y.
26.1.2 Safe operation and half-runway-ex
lusion
The gist of the safety requirements is: air
raft movement should be planned so that at all
times air
raft are well separated from ea
h other. A
ertain minimum separation is required
even as air
raft are taxiing. The separation between air
raft must be larger when they are
travelling at high speeds, as will be the
ase when they are landing or taking o. The
separation might depend upon the type/size of the air
raft. For simpli
ity we will assume
that there is just one type of air
raft and ignore this issue.
More formally, we will model the safety requirement as follows: we will break runways
and taxiways into segments and require that there be at most one air
raft on ea
h segment
at any time. Thus by
hoosing su
iently long segments we
an keep the air
raft well
separated. Here is the division into segments that we will use. The two runways will be
separate segments, and so will the the left and right taxiways. The main taxiway will be
broken up into segments at the points where the bran
h taxiways leave from it. Sin
e there
are 10 gates, the main horizontal taxiway will be split into 11 segments. The bran
h taxiways
will
onstitute separate segments by themselves.
We have an additional
ompli
ation be
ause our two runways overlap. The simplest way
to ensure safe operation would be to say that only one of the runways
an be used at any
time. However, to make the problem more interesting and realisti
, we will note that the
interse
tion is in the initial portion of the runways, and so we will require that the initial
halves of the runways should not be in use simultaneously. In other words, we will require
that if the initial half of the take o runway
ontains an air
raft then there should be no
air
raft in the initial half of the landing runway, and vi
e versa. We will
all this half-runway
ex
lusion.
441
An air
raft must begin its des
ent mu
h earlier than its landing time, and on
e the des
ent has begun,
the landing
annot be postponed in normal
ir
umstan
es. However our rst
ome rst serve strategy may
require a
ight arrival to be delayed. This is a short
oming of our simulation.
1
442
In addition, we may require several derived outputs. Let us dene the delay of an air
raft
to be the additional time it spent over and above when it
ould have departed had the airport
been
ompletely empty. So we might be required to
ompute the average delay. Su
h analyses
and extensions are left to the Exer
ises.
We will build a dis
rete event simulation using the sim
lass developed in Chapter 25. The
Resour
e
lass developed in that
hapter will also be useful.
The state of the airport system
an be des
ribed by the position of the dierent planes
on the dierent taxiways (or gates). So it would seem that these entities must be represented
in our simulation somehow. So we will have a taxiway
lass and a plane
lass. In addition,
we will also have an ATC
lass to represent air tra
ontrollers. The air tra
ontrollers
perform operations su
h as allo
ating gates to planes, and even permitting a plane to move
from one segment to another. Su
h
ontrol a
tions will be implemented in member fun
tions
in the ATC
lass. The plane
lass will basi
ally
ontain
ode that moves the plane forward
when needed.
26.2.1 Safe operation and half-runway-ex
lusion
We need to ensure that at most one air
raft o
upies any taxiway segment at any instant.
Thus it is natural to use the Resour
e
lass from Chapter 25. We will make ea
h taxiway
segment a resour
e whi
h must be a
quired by a plane before it moves onto it. This will
ensure that there is at most one plane on ea
h segment.
To implement half-runway-ex
lusion we will use the following tri
k. Whenever a plane
needs to land or take o, we will require it to reserve a
titious halfRW taxiway in addition to
reserving the landing or takeo runways respe
tively. After a plane has landed and traversed
half the runway, we want to allow another plane to start taking o. To enable this, we simply
release halfRW as soon as the plane gets to the middle of the landing runway! Same thing
for a plane taking o { it will also release halfRW when it gets to the middle of the takeo
runway.
26.2.2 Gate representation and allo
ation
We represent gate G impli
itly using bran
h taxiway G. Indeed, when a plane has to wait
at gate G it waits at the bottom end of bran
h taxiway G. Furthermore, when we want to
allo
ate gate G to a plane, we merely reserve bran
h taxiway G. With this we
an ensure that
at anytime a gate is used only by one plane.
To allo
ate a gate we merely examine all the bran
h taxiways and determine if any is free,
and if so reserve it. The plane then taxies to the end of that bran
h taxiway and waits. After
waiting for the servi
e time de
ided for the plane, the plane turns around and heads ba
k to
the main taxiway. Just as the plane is about to turn onto the main taxiway it releases the
reservation for the bran
h taxiway. After this the other planes
an use this gate.
443
The rst main data stru
ture is a ve
tor of all taxiway segments and the
titious taxiway
halfRW used to implement the half ex
lusion rule. Although we simulate an airport with
10 gates, it is
onvenient to express the
reation using a parameter nGates = 10. You will
see that we need 3*nGates + 6 taxiway segments in
luding halfRW. In addition, we will
use an air tra
ontroller obje
t at
of type ATC. Obje
ts representing air
raft are
reated
dynami
ally as the air
raft arrive into the airport. The main program is as follows.
onst int nGates = 10, nSegments = 6+3*nGates;
int main(int arg
,
har** argv){
initCanvas("Airport Simulator",1000,1000);
ve
tor<taxiway*> taxiways(nSegments);
onfigure_taxiways_and_runways(taxiways);
ATC at
(taxiways);
ifstream planeDataFile(argv[1);
post_plane_arrivals(at
, planeDataFile);
// in
luding halfRW
//
reates all taxiways
sim::pro
essAll();
getCli
k();
The fun
tion
onfigure taxiways and runways populates the array taxiways with taxiway
segments. We dis
uss this fun
tion in the next se
tion, whi
h also dis
usses the taxiway
lass in detail. After this the main program
reates an obje
t at
to represent air tra
ontrollers. Finally, the fun
tion post plane arrivals
reates plane arrival events. After
this sim:pro
essAll is
alled and the simulation unfolds.
The fun
tion post plane arrivals takes the plane data from a le whi
h is required to
be supplied as the rst
ommand line argument to the main program. The
reated events
will
all the pro
essArrival member fun
tion of at
. This
all will
reate the plane obje
ts.
The plane
lass is dis
ussed later.
void post_plane_arrivals(ATC &at
, ifstream &planeDataFile){
int arrivalT, servi
eT;
while(planeDataFile >> arrivalT){
planeDataFile >> servi
eT;
sim::post(arrivalT, [=,&at
(){at
.pro
essArrival(arrivalT, servi
eT);});
}
}
Instan
es of the taxiway
lass must serve two purposes: they must be visible on the s
reen
as lines, and the planes must be able to reserve them. So it is natural to derive the taxiway
lass from the Line
lass and the Resour
e
lass of the pre
eding
hapter.
444
The taxiway
onstru
tor rst
reates the Line representing the taxiway on the s
reen.
Ideally we should distinguish the on-s
reen line from the real taxiway, and provide details
about the real taxiway separately. For simpli
ity we have assumed that the on-s
reen taxiway
and the real taxiway will have same
oordinates on the s
reen as well as the ground (say the
units have been
onveniently sele
ted). In
onstru
ting a taxiway we also provide the time
required to traverse it in some hypotheti
al time units. Sin
e we know the length of the
taxiway we
al
ulate how mu
h an air
raft moves forward ea
h (hypotheti
al) step when on
this taxiway { this information is needed to perform the animation.
Note that the Resour
e
onstru
tor is not expli
itly
alled, so a
all with no arguments
will be inserted by the
ompiler. This will set the member inUse of the taxiway (derived
from Resour
e, Se
tion 25.3) to NULL, indi
ating that initially the taxiway is unreserved.
The fun
tion
onfigure taxiways and runways will instantiate taxiways to
reate the
main path and the bran
h taxiways. As mentioned earlier, the
onstru
tion is des
ribed
in terms of a
onstant nGates = 10 denoting the number of gates. The initial nGates+5
elements of taxiways respe
tively represent the landing runway, the right taxiway, the
nGates+1 segments of the main taxiway, the left taxiway, and the takeo runway (Figure 26.2). The nGates subsequent elements will represent the bran
h taxiways going toward
the gates, and the next nGates elements will represent the bran
h taxiways
oming ba
k
from the gates. Then we will have one more segment representing the
titious taxiway
halfRW.
The
ode below
reates the taxiway elements along with their geometri
al
oordinates for
display purposes. The names RW1X1 et
. are
onstants indi
ating the geometri
oordinates
of the appropriate taxiways, and the names tRW et
. are
onstants indi
ating the time to
traverse the appropriate taxiways.
void
onfigure_taxiways_and_runways(ve
tor<taxiway*> &taxiways){
taxiways[0 = new taxiway(RW1X1,RW1Y1,RW1X2,RW1Y2,tRW); // landing runway
taxiways[1 = new taxiway(RW1X2,RW1Y2,TWX1,TWY1,tVT);
// right taxiway
float twXdisp = ((float)TWX2-TWX1)/(nGates+1);
float twYdisp = ((float)TWY2-TWY1)/(nGates+1);
for(int i=0; i<= nGates; ++i){
// main taxiway: 11 segments
taxiways[2+i = new taxiway(int(TWX1+i*twXdisp), int(TWY1+i*twYdisp),
int(TWX1+(i+1)*twXdisp),
int(TWY1+(i+1)*twYdisp), tMT);
}
445
The taxiway numbering vis-a-vis gate numbering is shown in Figure 26.2. We have shown
the taxiway going to a gate as being distin
t from the taxiway going ba
k from the gate.
This is only for the
onvenien
e of drawing; physi
ally they are
oin
ident.
The ATC
lass instan
e at
represents the air tra
ontrollers. The at
ontrols the plane
movements, starting from pro
essing plane arrival in whi
h the plane obje
ts are
reated.
The
lass
ontains member fun
tions pro
essArrival, grantClearan
e and allo
ateGate
for these operations.
lass ATC{
ve
tor<taxiway*> &taxiways;
int planeId; // to number planes as they get generated
publi
:
ATC(ve
tor<taxiway*> &tw) : taxiways(tw), planeId(0) {}
void pro
essArrival(int arrivalT, int servi
eT);
void grantClearan
e(plane *p, int segment, int &gate);
void allo
ateGate(plane* p, int segment, int &gate);
};
The pro
essArrival fun
tion
reates plane obje
ts. After that, for ea
h
reated plane
obje
t, the fun
tion a
quires the runway and halfRW. After that, the plane is asked to move
446
leftTaxiway
rightTaxiway
rightTaxiway+1+G
G
fromGates + G
...
toGates + G
The movement of a plane on a single taxiway segment is implemented by the fun
tions
prepareToMove and moveOnSegment in the plane
lass. During the movement when the
plane
omes to the end of a taxiway segment, it will need to know from the at
what segment
to move on next (e.g. whether to turn on a bran
h taxiway) and also ask that the segment
be reserved. For this, the plane will
all the grantClearan
e member fun
tion of ATC. After
grantClearan
e nishes its work, the plane is ready to move, and so plane::prepareToMove
447
an be
alled again.
The fun
tion grantClearan
e determines where to dire
t the plane next and what resour
es to reserve for it, based upon the plane position, i.e. the segment on whi
h the plane
is, and whi
h gate the plane has been allo
ated.
void ATC::grantClearan
e(plane *p){
if(p->segment == rightTaxiway)
allo
ateGate(p);
else if(p->segment == rightTaxiway + 1 + p->gate){
taxiways[p->segment->release();
p->prepareToMove(toGates + p->gate);
}
else if(p->segment == toGates + p->gate){
sim::log()<< " Plane " << p->id << " at gate " << p->gate
<< " will wait for " << p->servi
eT << endl;
sim::post(p->servi
eT, [=(){
// wait for servi
e
p->prepareToMove(fromGates + p->gate);
});
}
else if(p->segment == fromGates + p->gate){
taxiways[rightTaxiway + 2 + p->gate->a
quire([=(){
taxiways[toGates + p->gate->release();
p->prepareToMove(rightTaxiway + 2 + p->gate);
});
}
else if(p->segment == leftTaxiway){
taxiways[takeOff->a
quire([=(){
taxiways[halfRW->a
quire([=(){
taxiways[leftTaxiway->release();
p->prepareToMove(takeOff);
});
});
}
else if(p->segment == takeOff){
taxiways[takeOff->release();
p->hide();
sim::log() << " plane " << p->id << " left." << endl;
delete p;
}
else {
// default
ase
taxiways[p->segment+1->a
quire([=(){
taxiways[p->segment->release();
p->prepareToMove(p->segment+1);
});
}
}
448
If p->segment equals rightTaxiway, then the plane is at the end of the right taxiway.
So a gate must be reserved for it. As dis
ussed earlier, reserving gate G is equivalent to
reserving bran
h taxiway towards gate G, i.e. taxiway toGate + G (Se
tion 26.2.2, also see
Figure 26.2). This is done by the fun
tion allo
ateGate whi
h we dis
uss later. After the
gate allo
ation is done, allo
ateGate will signal the plane to move forward.
If p->segment equals rightTaxiway + 1 + p->gate, then we know that the plane has
overed 1 + p->gate segments of the main taxiway, and is thus at the right position to
turn towards the gate allo
ated to it (Figure 26.2). Furthermore, taxiway segment toGate
+ p->gate has already been reserved for it. Thus it
an relese the
urrent segment on whi
h
it is, and start moving on segment toGate + p->gate.
If p->segment equals toGate + p->gate, then we know that the plane is at the bottom
end of the bran
h taxiway. Here it must wait for its stipulated servi
e time, i.e. p->servi
eT.
After this, it
an start moving on the segment going ba
k to the taxiway, i.e. segment
fromGate + p->gate.
If p->segment equals fromGate + p->gate, then we know that the plane has returned
ba
k to the main taxiway after waiting at the gate. So now we must a
quire the next
segment of the main taxiway, i.e. segment rightTaxiway + 2 + p->gate. After that we
must release the gate, i.e. segment toGate + p->gate so that other planes
an use the gate.
And after this, we
an start moving on segment rightTaxiway + 2 + p->gate.
If p->segment equals leftTaxiway, then the plane is about to take o. So we must
reserve the takeo runway, and halfRW. After that we release the left taxiway, and we
an
start moving on the takeo runway.
If p->segment equals takeOff, then the plane has nished takeo. So we must now hide
it, and re
laim the memory given to it.
If p->segment has none of the above values, then no spe
ial a
tions are needed. The
plane merely needs to move into the next segment, whi
h must be reserved for it. Then the
urrent segment on whi
h the plane is, must be released. The plane is then asked to move
to the reserved segment. This is the default
ase, given at the bottom of grantClearan
e.
Finally we dis
uss the fun
tion allo
ateGate.
void ATC::allo
ateGate(plane* p){
p->gate = -1;
// means not allo
ated.
for(int i=0;i<nGates;++i){
if (taxiways[toGates + i->reserve()){
p->gate = i;
break;
}
}
if(p->gate >= 0)
// if gate allo
ation su
eeded
taxiways[p->segment+1->a
quire([=(){
taxiways[p->segment->release();
p->prepareToMove(p->segment+1);
});
else
sim::post(1,[=(){allo
ateGate(p);});
// else try again
}
449
The gate allo
ation pro
edure as dis
ussed earlier is simple: we reserve the smallest numbered
unreserved gate. If reservation is su
essful, then we try to move forward. For this, we a
quire
the next segment. When that is a
quired, we release the
urrent segment, and then try to
move on the new segment. If gate reservation is not su
essful, then we try reservation again
after one step.
The air
raft are implemented using a plane
lass. An air
raft must appear on the s
reen as
a part of the animation. So we inherit from the Turtle
lass. Indeed, our air
raft appear
on the s
reen as turtles. We
ould have dened a more air
raft like visual appearan
e by
using the polygon
lass, but that is left for the exer
ises. To keep tra
k of whi
h taxiway
segment the plane is on at any time instant we will have a data member segment. We will
have another data member timeToSegmentEnd, in whi
h we keep tra
k of the number of
steps we need to move forward in order to rea
h the end of the
urrent segment. In addition,
we need to note the gate allo
ated for the air
raft. For this we use an integer data member
gate. Finally, the plane will need to know about the taxiways and the at
, so we have data
members to hold those referen
es.
lass plane : publi
Turtle {
publi
:
int id;
// identifying number for plane
int arrivalT;
// arrival time
int servi
eT;
// how long the air
raft waits at the gate
int segment;
// index of taxiway segment the air
raft is on
int timeToSegmentEnd; // how far from the end of the segment
int gate;
// id of allo
ated gate
ve
tor<taxiway*> &taxiways;
ATC &at
;
publi
:
plane(int i, int at, int st, ve
tor<taxiway*> &tw, ATC &at
0)
: id(i), arrivalT(at), servi
eT(st), taxiways(tw), at
(at
0) {
hide();
penUp();
gate = -1;
// indi
ates gate not allo
ated
}
void prepareToMove(int newsegment);
void moveOnSegment();
friend
lass ATC;
};
Note that we have made the ATC
lass a friend be
ause it needs to know where a plane is
et
. A plane is
reated in the ATC
lass as dis
ussed earlier.
The basi
a
tion performed by a plane is moving on a taxiway. When the at
determines
that a plane must move on
ertain segment of the taxiway, it
alls the prepareToMove
450
member fun
tion of the plane
lass. This does minor housekeeping and aligns the plane with
the dire
tion of the taxiway. The data member timeToSegmentEnd is also set to the number
of steps in whi
h the
urrent segment is to be traversed. After that the plane is ready to
move.
void plane::prepareToMove(int newSegment){
segment = newSegment;
Position linestart = taxiways[segment->getStart();
moveTo(linestart.getX(), linestart.getY());
Position lineend = taxiways[segment->getEnd();
fa
e(lineend.getX(), lineend.getY());
timeToSegmentEnd = taxiways[segment->traversalT;
sim::post(0, [=(){this->moveOnSegment();});
}
At the end, the moveOnSegment member fun
tion is
alled. This
auses the plane to take
one step on the segment taxiway. To traverse the entire segment, the fun
tion is
alled
repeatedly, for ea
h value of timeToSegmentEnd as it is de
remented down to 0. At the end,
the plane has rea
hed the end of the segment, and so
learan
e must be sought from the air
tra
ontrollers for future a
tion. So at
.grantClearan
e is
alled.
There is one slight
ompli
ation to be handled: when the plane is taking o or landing,
then the taxiway halfRW must be released when the plane has rea
hed the middle of the
segment.
void plane::moveOnSegment(){
if(timeToSegmentEnd != 0){
if((segment == landing || segment == takeOff)
&& timeToSegmentEnd == taxiways[segment->traversalT/2){
taxiways[halfRW->release();
}
forward(taxiways[segment->stepsize);
--timeToSegmentEnd;
sim::post(1, [=(){moveOnSegment();});
}
else
at
.grantClearan
e(this);
}
26.7 Deadlo ks
A deadlo
k is a te
hni
al term used to des
ribe a system in whi
h one entity e is waiting
to reserve a resour
e held by entity e whi
h in turn is waiting to reserve a resour
e held by
and entity e and so on, till some entity en in this sequen
e is waiting to reserve a resour
e
held by e . Noti
e that in this
ase no entity
an make progress, be
ause all are waiting for
ea
h other. As an example, Figure 26.7 shows
ars deadlo
ked on roads in a
ity. Note that
the roads are one ways, as shown. The
ars in the top road are waiting for the spa
e ahead
1
451
There are many ways in whi
h we
ould have designed this program. For example, note that
the
lass ATC does not
ontain any interesting data members. Thus you might
hoose not
452
to have this
lass at all and instead in
lude the member fun
tions from this
lass into the
plane
lass itself. This would simplify the
ode a bit; you
ould dire
tly a
ess the members
in plane in writing fun
tions su
h as ATC::pro
essArrival. However, we have preferred to
have two
lasses be
ause we felt it is better to put air tra
ontrol a
tions in an ATC
lass.
Another possibility is to not use the sim and Resour
e
lasses at all. Write the
ode
from the point of view of the air tra
ontrollers who examine the planes at ea
h step and
advan
e them as needed. You are asked to do this in an exer
ise.
Exer ises
1. Modify the simulation program to print out the average air
raft delay.
2. Dene a better plane
lass in whi
h the on s
reen image looks like an air
raft rather
than a triangle.
3. Suppose we wish to ensure that as mu
h as possible, an air
raft must land at its arrival
time. Thus, while granting halfRW to a departing plane, we must
he
k whether no
plane will want to land during the interval in whi
h the departing plane will use halfRW.
Devi
e a good me
hanism to do this. Hint: perhaps you
an reserve the landing runway
and halfRW a bit earlier than needed?
4. The program given in the text uses so
alled busy waiting to allo
ate gates, i.e. if a
gate is not
urrently available, the plane retries after 1 step. It will be more e
ient
if the plane
an await the release of any gate. Develop a
lass to represent su
h a
resour
e group. A resour
e group models a sequen
e of obje
ts, ea
h of whi
h
an be
either reserved or unreserved. On a reserve request, one of the unreserved obje
ts
must be allo
ated, i.e. the requesting entity should be set as its owner. If all obje
ts
are
urrenly reserved, then the reserve request is deemed to fail and should thus return
false. In that
ase the entity may await its release. When any of the obje
ts be
omes
available, that should get reserved for the waiting entity. Use this in the simulation
ode.
5. Show that our strategy of reserving resour
es ensures that there is no deadlo
k. Spe
ifi
ally, show that at every step some air
raft will make progress, and that there will
not exist entities e ; : : : ; en where ei is waiting for a resour
e
urrently held by entity
ei
n.
6. Suppose we reserve halfRW rst and then the take o or landing runways. Constru
t
an input sequen
e (the le arrivals.txt) su
h that there is a deadlo
k.
7. Perform the airport simulation without using the sim and Resour
e
lasses, as des
ribed in Se
tion 26.8. Do you think this will run faster than the simulation we have
presented, or slower? What if there is no need to produ
e a graphi
al output, i.e. only
a textual re
ord is required?
8. Suppose we do not want to divide the taxiway into segments. Instead, suppose we
will allow a plane to move a
ertain stepsize at ea
h step while keeping a
ertain safe
0
+1 mod
453
distan
e behind the plane ahead, if any. Implement this. The other rules must still
be followed, i.e. the half-runway-ex
lusion rule and the rule that there
an be at most
one air
raft on ea
h runway at any instant. Also, there
an be only one air
raft on any
bran
h taxiway.
9. Simulate an airport with 2 runways that do not interse
t. Assume the same tra
as
that for an airport with interse
ting runways. Dene the delay of an air
raft as the
a
tual time it spends on the airport less the time it would spend if no other air
raft
was present in the airport. Compute the total delay for all air
raft in both models.
In
rease the tra
i.e. arrivals per unit time and see how the total delay
hanges for
the interse
ting and non-interse
ting runways.
10. If an air
raft is not allowed to land when it arrives at the airport, it must
y in a
ir
ular path of total duration some T and then try again. This is a more realisti
model than what we have in the text. Simulate this model.
11. Consider the shortest path algorithm of Se
tion 25.4. Suppose that we are also given
the geometri
oordinates for ea
h vertex of the graph. Show a visual simulation of
the algorithm, i.e. a turtle should move along ea
h edge as if it were a
y
list.
Chapter 27
Non-linear simultaneous equations
Suppose you want to
onstru
t a parallelopiped box of volume 1010
m , surfa
e area 700
m and having a base whose diagonal is 22
m. What are the lengths of the sides of the box?
If u ; u ; u denote the side lengths in
m,
learly we have u u u = 1010, 2(u u + u u +
u u ) = 700 and u + u = 22 . We have 3 equations in 3 unknowns, but unfortunately these
equations are non-linear! In Se
tion ?? we saw how to solve linear simultaneous equations,
but this problem is mu
h more di
ult. Indeed it is fair to say that solving non-linear
simultaneous equations is bit of an art. While, there is no single guaranteed method for
solving non-linear equations in many variables, there are some strategies whi
h seem to
often work. On
e su
h strategy is the Newton-Raphson method (NRM). We have already
studied NRM in Se
tion 8.4 for the one dimensional
ase. Its generalization to multiple
dimensions is pre
isely what we need and we will study it in this
hapter.
After studying NRM for multiple dimensions, we will
onsider a more elaborate problem:
given a
hain of links of dierent lengths,
ompute the
onguration in whi
h it hangs if
suspended from some xed pegs. We will see that NRM solves this problem ni
ely.
The exer
ises give more appli
ations of NRM in multiple dimensions.
3
2
1
2
2
In one dimension, NRM is used to nd the root of a fun
tion f of one variable, i.e. nd u
su
h that f (u) = 0. The higher dimensional
ase is a natural generalization. We are now
given n fun
tions f ; : : : ; fn ea
h of n variables, and we want to nd their
ommon root, i.e.
a set values u ; : : : ; un su
h that fi(u ; : : : ; un) = 0 for all i.
As you might see, this is really the same as solving simultaneous, possibly non-linear
equations. Any equation in n unknowns
an be written so that the right hand side is 0, but
then we
an treat what is on the left hand side as a fun
tion of the unknowns. Indeed, our
equations for the box problem
an be stated in this form as follows:
f (u ; u ; u ) =
u u u 1010
=0
(27.1)
f (u ; u ; u ) = 2(u u + u u + u u ) 700 = 0
(27.2)
f (u ; u ; u ) =
u + u 484
=0
(27.3)
Indeed the
ommon root u = (u ; u ; u ), of f ; f ; f will pre
isely give us the side lengths
of the box we want to
onstru
t.
1
2
1
454
2
2
455
1
1
f
1
f1
u
u1
ur 1
(27.4)
1
1
ur
ur
10 50u
So we indeed
hoose u = = 0:2. The new value of u be
omes 20+0.2=20.2, and
the new value of f be
omes 20:2 10 5 1010 = 0. In this
ase, the error in the rst
equation has
ompletely vanished. Things will not be this good in general, but as you might
remember from Se
tion 8.4 that we
an expe
t f to get
loser to 0 than it was.
But of
ourse, nothing really for
es us to only
hange u . Thus, if we are allowed to vary
all the variables, then equation 27.4 generalizes as:
1
10
50
Think about
hanging 1 rst, and then 2 and so on. Initially we are at ( 1
ur 2
ur 3
ur ). After
hanging 1 by get
to the point whi
h we will
all ( 1
ur 2
ur 3
ur ). In this movement, we have
hanged
f1
1. From the new point we
hange 2 by 2. The
hange that this
auses in 1 is
1 by about u1
1
about
f1
u2
0;
0;
f1
u1
ur
and
ur
1+
u
ur
f1
u2
ur0
;u
ur
ur0
;u
u2
456
f1
u
u1
ur 1
f1
f1
f1
+ u
u2 + u u3
2
ur
3
ur
Again, we want f1 = f1
ur , and try to pi
k u1; u2; u3 to satisfy
f
f
f
f1
ur = 1 u1 + 1 u2 + 1 u3
u1
ur
u2
ur
u3
ur
(27.5)
(27.6)
(27.12)
j ur
We solve these to get (u ; : : : ; n), and from these we
an
al
ulate ujnext = uj
ur + uj ,
for all j .
It is
ustomary to
onsider the above equations in matrix form. Dene an n n matrix
fi
A in whi
h aij = u
uj . Dene an n element ve
tor b where bi = fi (u
ur ; : : : ; un
ur).
j
ur
Let u denote the ve
tor of unknowns (u ; : : : ; un). Then the above expressions
an be
written in the form
A(u) = b
in whi
h A; b are known and we solve for u. The matrix A is said to be the Ja
obian
matrix for the problem. Further, it is
ustomary to dene ve
tors u
ur = (u
ur ; : : : ; un
ur)
and unext = (u next; : : : ; unnext). Then our next guess
omputation is simply:
unext = u
ur + u
Next we
omment on when we should terminate the pro
edure, and how to make the
rst guess.
1
457
27.1.2 Termination
We should terminate
p the algorithm when all fi are
lose to zero. A standard way of doing this
is to require that f + ; fn be
ome smaller than some that we
hoose, say = 10
if we use float, and even smaller if we use double to represent our numbers. In keeping
with
p our interpretation that fi is the error, the quantity (f ; : : : ; fn ) is the ve
tor error, and
f + ; fn is the 2-norm or the Eu
lidean length of the ve
tor error.
2
1
2
1
Suppose you are given a
hain of n links, where the ith link has length Li , i = 0; : : : ; n 1.
Say the
hain is hung from pegs at points (x ; y ) and (xn; yn) whi
h are known. What is
the shape attained by the
hain when it
omes to rest, hung in this manner? The links in
the
hain need not have equal lengths.
0
27.2.1 Formulation
Let xi ; yi denote the
oordinates of the left endpoint of link i, and of
ourse xi ; yi are
then the
oordinates of the right endpoint, where i = 0; : : : ; n 1. As dis
ussed above, we
already know the values of (x ; y ) and (xn; yn), these are the
oordinates of the pegs from
whi
h the
hain is suspended. The other xi ; yi are the unknows we must solve for. We are
given the lengths Li of the links, thus the variables xi; yi must satisfy the following equations.
(xi xi ) + (yi yi) Li = 0
(27.13)
We also need to
onsider the for
es on the links. Suppose that Fi is the (ve
tor) for
e
exerted by link i 1 on link i (F is the for
e exerted on link 0 by the left peg, and Fn is
the for
e exerted by link n 1 on the right peg). Note of
ourse that by Newton's third law,
+1
+1
+1
+1
458
if one obje
t exerts a for
e F on another, the latter exerts a for
e of F on the former. So
ea
h link i has a for
e Fi a
ting on its left endpoint, and a for
e Fi a
ting on its right
endpoint. Further, there is its weight, Wi, also ve
tor, whi
h a
ts at its
enter. When the
hain is at rest, total for
e on ea
h link must be zero, as well as the total torque.
Thus for ea
h link we have Fi Fi + Wi = 0. Suppose Fi = (hi ; vi), i.e. hi and
vi are the horizontal and verti
al
omponents of Fi . Be
ause Wi is only verti
al, we
an
write it as Wi = (0; wi). The weight a
ts downwards, so perhaps we should write wi as
the y
omponent. However, do note that in the
oordinate system of our graphi
s s
reen y
in
reases downwards. Hen
e we do not have the negative sign. Further, we will assume that
the weight is proportional to the length, and so we will write wi = Li . Now balan
ing the
horizontal
omponent we get hi hi = 0, i.e. all these variables are identi
al! Thus we
ould write a
ommon variable h instead of them. Balan
ing the verti
al
omponent we get,
for all i:
vi vi + Li = 0
(27.14)
Finally we need to balan
e the torque as well. For this we need to
onsider the right endpoint
to be the
enter. Remember that the torque due to a for
e F equals the magnitude of the
for
e times the perpendi
ular distan
e from the
enter to the line of the for
e. The torque
due to the horizontal
omponent of Fi is simply the horizontal
omponent times the verti
al
distan
e to the horizontal
omponent. Thus it is hi (yi yi) = h(yi yi). This torque is
in the
lo
kwise dire
tion. The torque due to the verti
al
omponent is similar, vi (xi xi ),
but in the
ounter
lo
kwise dire
tion. The distan
e to the line of the weight is (xi xi )=2,
and so the torque due to it is Li (xi xi)=2, also in the
ounter
lo
kwise dire
tion. But
the total torque,
onsidered in say the
lo
kwise dire
tion, must be zero. Thus we get:
h(yi
yi) vi (xi
xi ) Li (xi
xi )=2 = 0
(27.15)
Equations (27.13), (27.14), and (27.15) apply to ea
h link, and thus we have 3n equations over
all. The unknowns are x ; : : : ; xn (noting that x ; xn are known) and similarly y ; : : : ; yn ,
and h, and v ; : : : ; vn. Thus there are a total of (n 1)+(n 1)+1+(n +1) = 3n unknowns.
Thus the number of unknowns and the number of equations mat
h; however, our equations
(27.13) and (27.15) are not linear. So we need to use the Newton Raphson method.
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
459
27.2.3 Experien
e
We
oded up the algorithm and set the initial values as per the guessing strategy des
ribed
above. We then ran the algorithm. After ea
h iteration, we plotted the ne
kla
e
onguration
on our graphi
s s
reen. As you
an see, the
onguration qui
kly seems to rea
h a stable
point. Indeed, we also printed out the square error, and it got
lose to zero fairly qui
kly.
27.3 Remarks
As you experiment with NRM you might noti
e that the error norm (as dened in Se
tion 27.1.1) does not ne
essarily de
rease in ea
h iteration. This is understandable, the error
norm is guaranteed to de
rease only if the equations su
h as (27.6) hold exa
tly.
It is possible to show, however, that the ve
tor u does indeed give the exa
t dire
tion
in whi
h to move from u
ur for whi
h the rate of redu
tion of the error norm is the largest
possible. Thus there exists an su
h that the error norm at u
ur + u will be stri
tly
smaller than that at u
ur . We
an try to roughly nd this by starting with = 1 (whi
h
is equivalent to taking the full step, ie. the basi
NRM), and su
essively halving it till we
nd a point u
ur + u where the error norm is lower than at u
ur .
Appendix A
Installing Simple
pp
It should be possible to install simple
pp on any system whi
h has X Windows (X11) installed. We have installed simple
pp on Ubuntu, Ma
OS X, and Mi
rosoft Windows running
Cygwin/X. To install, download
www.
se.iitb.a
.in/~ranade/simple
pp.tar
460
Appendix B
Managing Heap Memory
In Se
tion 19.1 we dis
ussed heap memory. We mentioned that managing heap memory is
tri
ky. The simplest way to use heap memory is to use STL
lasses su
h as ve
tors, maps,
queues, strings. These obje
ts hide the memory management from the user, and provide a
onvenient interfa
e whi
h is generally adequate.
However, if you need to manage the memory yourself, you need to gure out what kind
of sharing you want to allow. In Se
tion ?? we gave a solution in whi
h we ensured that ea
h
allo
ated obje
t is pointed to by exa
tly one pointer. As a result, we
an tell fairly easily
when the allo
ated obje
t is no longer needed and
an be returned to the heap (Se
tion ??).
This is in fa
t the memory management idea used in STL, but it exe
utes behind the s
enes.
However, the
onstraint that ea
h obje
t be pointed to by at most one pointer is not
always e
ient or
onvenient. Say we have a large tree T. Let L be a subtree in it. Suppose
that we wish to
onstru
t another tree D whi
h also
ontains L. Then it is natural to share
the subtree: we should make the appropriate pointer in D point to L rather than needing to
make another
opy of L to use as part of D. This will require less memory, and potentially
save the time required to
opy. A similar example a
tually arises in a program for
omputing
the symboli
derivative of an expression. Consider the rule for dierentiating produ
ts:
d(uv )
= v du + u dv
dx
dx
dx
When we represent symboli
expressions as trees (Se
tions 22.1.3 and 24.1), the produ
e uv
will be represented by a tree with u; v being the left and right subtrees, and
learly, u; v will
appear as subtrees in the formula for the derivative, spe
i
ally the left subtree of the right
subtree and the left subtree of the left subtree, see Figure B.1, (a) and (b). So it would be
natural to ask:
an these two trees, the tree for the original expression and the tree for the
derivative, share subtrees, as shown in Figure B.1(
)?
The di
ulty in sharing resour
es is that it is harder to tell when a resour
e is not needed.
If we de
ide we dont need the tree denoting the original expression any longer, we
annot
free the memory used by it, be
ause that memory might be holding parts of the derivative,
whi
h we might still need. One way to solve this problem is to use referen
e
ounting
461
462
*
*
u
v
v
du/dx
(a) u*v
dv/dx
(b) d(u*v)/dx
D
*
*
u
v
du/dx
dv/dx
463
The basi
idea is to asso
iate a referen
e
ount with ea
h obje
t, in this
ase ea
h node of
the tree. The referen
e
ount of an obje
t is simply the number of pointers pointing to that
obje
t, indi
ating how useful that obje
t is. Ensuring that the referen
e
ount is
orre
tly
maintained is tri
ky, but we rst des
ribe what we would like to happen abstra
tly. If we
add a new pointer to point to an obje
t, we want one to be added to its referen
e
ount. If
we remove a pointer, then we want one subtra
ted. When the
ount of an obje
t X drops to
zero, we de
ide that X is no longer useful, and so we return the memory of X to the heap.
Note that X might itself be pointing to an obje
t Y. In this
ase we know that the pointer
to Y out of X will no longer be of use. So the referen
e
ount of Y should get de
remented.
If the referen
e
ount of Y thus drops to zero, we
an return Y also ba
k to the heap, and so
on.
Coming ba
k to our derivative example, suppose initially we only have the produ
t tree.
Suppose the tree is pointed to by a variable T. Then every node, in
luding the root is pointed
to by 1 pointer. Hen
e at this point we want the referen
e
ounts of all nodes to be 1. Note
that we do not asso
iate referen
e
ounts with variables su
h as T if they are in an a
tivation
re
ord rather than in the heap. For simpli
ity we will assume that T is indeed in the a
tivation
re
ord.
Suppose next we
reate the derivative. Say the nodes of the derivative tree are all on
the heap, but its root is pointed to by a variable D in the a
tivation re
ord. Suppose we
did
reate the derivative tree su
h that it shares nodes with the original produ
t tree, as
in Figure B.1(
). In this pi
ture, the roots of the subtrees u; v have 2 pointers
oming in.
Hen
e after
reation we would like the roots of these two nodes to have referen
e
ounts of
2 ea
h.
Now suppose we write T=NULL. This would make the root of the original expression lose
the only pointer it had. So we would de
rement the referen
e
ount of the root. We would
nd that the root of the original expression has referen
e
ount 0. So we
an release the
memory of the root ba
k to the heap. This would
ause the pointers out of the root (of
the original expression) to be
ome useless. Thus we would like the referen
e
ounts of the
obje
ts they point to to be de
remented. Thus at this point the referen
e
ounts of the
trees of u; v would both be
ome 1. If after this we set D=NULL then if our referen
e
ounting
me
hanism is working all referen
e
ounts will be
ome 0 and everything would be returned
to the heap.
It is not too di
ult to implement referen
e
ounting manually. To ea
h node we add a
referen
e
ount data member, and we have listed out the
onditions under whi
h this must
be in
remented or de
remented. However, the
ode is
umbersome, and unless it is designed
well, its use will also be
umbersome.
This
lass provides a standard solution for referen
e
ounting. This
lass and the
lass
weak ptr are known as smart pointers and are available in the Boost Smart Ptr library
available from www.boost.org. These pointers are also a part of C++11, and
an be a
essed
through the GNU C++
ompiler g++ by supplying the option -std=gnu++0x. Sin
e our
464
ompiler
ommand s++ really
alls g++, the option
an also be supplied to s++ to get the
fun
tionality. In addition, in your programs you need to in
lude the header le <memory>.
A shared ptr is really a small stru
ture that
ontains the real pointer, and of
ourse other
data needed to manipulate referen
e
ounts. The dereferen
ing and assignment (and other)
operators are overloaded for shared ptrs so that they refer to the real pointer
ontained
inside and also do the bookkeeping needed for referen
e
ounting. Thus in many ways a
shared ptr is like an ordinary pointer, and
an be used almost as
onveniently.
Ea
h shared pointer has a member fun
tion use
ount whi
h returns the referen
e
ount
of what the shared pointer points to. Note that in the
ontext of shared pointers, we dene
the referen
e
ount of an obje
t to be the number of shared pointers pointing to it. You
need not
on
ern yourself with exa
tly where the referen
e
ount is stored; just rely on the
guarantee provided to you.
B.2.1 Syntheti
example
Figure B.2(a) shows an example of use of shared pointers, and the output produ
ed when
the program is run.
The rst group of statements
reates and initializes two shared pointers s1, s2 to two
instan
es of A allo
ated on the heap. When ea
h instan
e is
reated, the
onstru
tor of
A prints the address of the instan
e. In our exe
ution these happened to be respe
tively
0x804
008 and 0x804
030. Ea
h obje
t A
ontains a shared ptr to another A obje
t. The
last statement of the group sets s1->aptr to s2. This has the ee
t that now s1->aptr
points to whatever s2 was pointing. After this we print the referen
e
ounts. As you
an
see s1 points to 0x804
008, and nothing else points to it. However, s2 as well as s1->aptr
point to the same instan
e 0x804
030. Thus the referen
e
ount of s1 is 1, and those of the
other two are both 2.
In the se
ond group of statements we set s2 to point to a new instan
e on the heap.
The
reation
auses its address 0x804
058 to be printed. Note that s2 was earlier pointing
to 0x804
030, so we should expe
t its referen
e
ount to drop by 1. This indeed happens.
Now the three pointers s1, s2, s1->aptr are pointing to unique obje
ts, and the referen
e
ounts 1 1 1 are printed for them.
In the third group we set s1=NULL. Sin
e s1 was earlier pointing to 0x804
008, its
referen
e
ount whi
h was 1 should drop to 0. This should
ause this obje
t to be deleted
and returned to the heap. This indeed happens, the destru
tor prints a message saying this.
Note further that when 0x804
008 is returned, the
ontained pointer s1->aptr is no longer
valid. Thus the referen
e
ount of the obje
t 0x804
030 pointed to by it should also be
ome
0, and that should get destroyed. This also happens, as shown by the statement printed by
the destru
tor. At the end of this group we print the referen
e
ounts of s1 and s2 only,
sin
e s1->aptr is now invalid. These indeed
ome out as 0 1, whi
h is
orre
t be
ause s1 is
NULL and s2 indeed points to 0x804
008, and is the only one to point to it.
The last print statement indi
ates that 0x804
008 also gets deleted. This happens be
ause when the program exits the s
ope, delete
ommands are issued on all lo
al variables
of the
urrent a
tivation frame. Thus a delete
ommand is issued on s1, s2. Provided a
shared pointer is non NULL, a delete on a shared pointer
auses the referen
e
ount of the
pointed obje
t to de
rement, and if it de
rements to 0 to delete that obje
t as well. This is
465
s1 = NULL;
// Group 3
out << s1.use_
ount() << " " << s2.use_
ount() << endl << endl;
466
467
468
2. Che
k assignments to Exp* variables. If other Exp* variables were being assigned, then
therefore there should be no problem be
ause both are
onverted to spE. However, there
an be new expressions that were assigned to Exp* variables. These now have to be
expli
itly
onverted to spE type. So you will have to do this.
With these two steps, your program should work. Add
ode to the
onstru
tors to observe
when they are
alled, and add destru
tors so that you know when they are
alled as well.
You should observe that while taking the derivative of a produ
t uv the expressions for u
and v are not
opied. So they must be shared. You
an also see that the nodes are destroyed
when the program terminates. So there is no memory leak either.
B.2.4 Weak pointers
Consider a program fragment that uses the stru
t A from Figure B.2:
int main(){
shared_ptr<A> s1(new A), s2(new A);
s1->aptr = s2;
s2->aptr = s1;
s1 = NULL;
s2 = NULL;
}
At this point s1 If you exe
ute this, you will merely get:
Creating A: 0x804
008
Creating A: 0x804
030
Even when the program nishes, you will not get any deallo
ations to happen, as you did
in the last line of the output of Figure B.2. Let us tra
e the exe
ution to see why. Clearly,
the
reation of s1,s2
aused the messages about
reating A to be printed. After that, the
instru
tion s1->aptr = s2; s2->aptr = s1;
auses s1,s2->aptr to point to 0x804
008,
and s2,s1->aptr to point to 0x804
030. Thus the referen
e
ounts of all 4 shared pointers
are 2. Now
onsider what happens when we set s1 = NULL; { one referen
e to 0x804
008
goes away. But it still has 1 referen
e, and hen
e no delete happens. When we set s2 =
NULL; next, { one referen
e to 0x804
030 goes away. But this also has one referen
e. Thus
even after we set s1, s2 to NULL, the obje
ts at 0x804
008 and 0x804
030
ontinue to have
referen
e
ount 1, the pointer inside the rst
ontributes the
ount to the other and vi
e
versa. But our program
annot a
ess these obje
ts, and they havent been returned to a
heap: so we have a memory leak.
B.2.5 Solution idea
This problem
an only be solved using so
alled the
lass weak ptr in
onjun
tion with
shared ptr.
The basi
idea is to break every pointer
y
le by putting one weak ptr in it. A weak ptr
is a pointer whi
h does not in
rement the referen
e
ount. However, if the obje
t pointed to
469
by the weak pointer is deleted, then the weak pointer be
omes NULL. So whenever you wish
to traverse a weak pointer W, you
an rst
he
k if *W is not NULL and only then traverse.
If you are working in a setting in whi
h there are multiple threads, then you might need to
lo
k the pointer rst.
Managing heap memory in C++ is an evolving eld. As a novi
e programmer, your needs
will probably be met by the
lasses in STL. If for some reason you need to go beyond that,
ideas su
h as shared ptr (and also weak ptr if ne
essary) will likely be adequate. There is
work on so
alled garbage
olle
tion strategies, but that is beyond the s
ope of this book.
Appendix C
Libraries
The term library is used to refer to a single le whi
h
olle
ts together several obje
t modules.
Suppose you have
onstru
ted obje
t modules g
d.o, l
m.o. Then you
an put them into
a single library le. On unix, this
an be done using the program ar, and it is
ustomary use
the sux .a for library (ar
hive) les, and so you might
hoose to
all your library g
dl
m.a.
This
an be done by exe
uting
ar r
s g
dl
m.a g
d.o l
m.o
Here the string r
s indi
ates some options that need to be spe
ied, whi
h we will not
dis
uss here. This
ommand will
ause g
dl
m.a to be
reated.
When
ompiling, you
an mention the library le on the
ommand line, prexing it with
a -L; modules from it will get linked as needed. In fa
t you
ould send the le to your friends
who wish to use your g
d and l
m fun
tions, along with an header le, say g
dl
m.h, whi
h
ontains the de
larations of g
d and l
m (but not the denitions). This is the preferred
me
hanism for sharing
ode. Note that your friends will not be able to easily know how your
fun
tions work, be
ause you need not send them the
orresponding .
pp les.
C.0.1 Linking built-in fun
tions
You
an now guess how built-in fun
tions su
h as sqrt or are linked to your program. They
are in libraries, whi
h s++ supplies when needed! Commands su
h as sqrt are
ontained
in the
math library that is supplied as a part of C++. Our
ompiler s++ automati
ally
in
ludes the
orresponding library while it
ompiles your programs. Of
ourse, this is not the
entire story { you need to have the prototype for sqrt and other fun
tions at the beginning
of your program.
These prototypes are present in a le
alled math.h, whi
h you
an insert into your
program by putting the following line at the beginning of your program:
#in
lude <
math>
470
471
Be
ause of this the le graphi
sim.h is pi
ked up from some pla
e known to s++ and is
pla
ed in your le in pla
e of this line. This le
ontains the line #in
lude <
math.h>
whi
h
auses the le
math.h to be in
luded!
Appendix D
Reserved words in C++
472
Appendix E
Operators and operator overloading
We will dis uss some of the less frequently used operators, and then onsider operator overloading.
C++ allows bitwise logi
al operations to be performed on variables of integer types. For
simpli
ity we will only
onsider operations on the unsigned types.
E.1.1 Or
The operator | is the bitwise OR operator, i.e. p | q returns a number that is obtained by
taking the binary representations of p,q, and
omputing the OR of the
orresponding bits.
Note that the logi
al OR of two bits is a 1 if and only if at least one of the bits is 1. Here is
an example.
unsigned int p=10, q=6, r;
r = p | q;
473
474
E.1.2 And
The operator & performs bitwise logi
al AND. Note that the logi
al AND of two bits is 1 i
both bits are 1. Thus for p,q as dened above, if we write:
unsigned int s = p & q;
s would get the
the variable t would get the bit pattern 0000000000000000000000000001100, whi
h is the
bit pattern for 12. Thus t would be 12 after the statement.
E.1.4 Complement
Finally the operator ~ is the (unary) bitwise
omplement operator. The
omplement of a bit
is 1 if and only if the bit is 0. Thus if we write
unsigned int u = ~p;
the bit pattern for u would have 0s wherever p had 1s and vi
e versa. Thus we would have
1s in all positions ex
ept
P thei positions of pla
e value 2 and 8. Thus the value of u after the
statement would be ( i 2 ) 2 8 = 4294967285.
31
=0
the variable v would get the bit pattern 0000000000000000000001010000000. This has the
numeri
al value 10 2 = 640.
6
475
the variable w would get the bit pattern 0000000000000000000000010100000. This has the
numeri
al value 160.
where lhs and rhs are expressions. The operator
auses the evaluation of both the expressions, and the value of rhs is used as the result of the
omma expression.
The
omma operator
an be used to for
e the evaluation of multiple expressions in settings
where synta
ti
ally only one expression is expe
ted.
A
ommon use is to have multiple in
rement and de
rement operations in a for statement.
for (int y = 10, x=0; y>0; y--, x++)
out << x << endl;
out << "The average is: " << sum/ ount << endl;
476
We have dis
ussed the basi
ideas of operator overloading in Se
tion 16.4 and Se
tion 16.5.
Here we dis
uss some details.
The following prex unary operators
an be overloaded
+ - * & ! ~ ++ --
For any operator in the list above, overloading
an be done either by dening a member
fun
tion operator in the
lass of the operand taking no argument, or by dening a fun
tion
operator taking a single argument of the type of the operand.
The unary sux operators ++, --
an also be overloaded. You again dene a member or
ordinary fun
tion operator like the prex versions. However to distinguish from the prex
versions, you also have an extra int argument whi
h you ignore. This might seem arbitrary,
and it
ould indeed be
onsidered a ha
k.
Appendix F
The
stringstream
lass
The
lass iostream is used to dene obje
ts su
h as
in and
out on whi
h we
an use the
extra
tion operators >> and << respe
tively to read or write data. The obje
ts are
alled
streams, be
ause data
ows in and out of them.
A stringstream is a stream obje
t, but it is
onstru
ted out of a string. To use it, you
need to in
lude the header <sstream>. This is espe
ially useful in extra
ting numbers from
strings or
onverting numbers to strings.
As an example, here is a program that takes two double numbers as
ommand line
arguments and prints their produ
t.
#in
lude <sstream>
int main(int arg
,
har *argv[){
double x,y;
stringstream(argv[1) >> x;
stringstream(argv[2) >> y;
out << x*y << endl;
}
In this we have used the stringstream fun
tionality provided in C++, by in
luding <sstream>.
The fun
tion stringstream takes a single argument s whi
h is a
hara
ter string, and
onverts it to an input stream (su
h as
in). Now we
an use the >> operator to extra
t elements.
Thus stringstream(argv[1) >> x; would extra
t a double value from the se
ond word
typed on the
ommand line. Similarly a double value would be extra
ted into y from the
third word. Thus if you typed
a.out 4 5e3
477
478
stringstream t;
t << x*y <<' '<< y*z;
out << t.str() << endl;
As you
an see, in this we have made multiple extra
tions from the same stringstream. This
is allowed. Basi
ally everything that you
an do with streams is allowed on stringstreams.
The stringstream t is used for output, and we have put multiple values into it. Finally, the
member fun
tion str allows us to extra
t the string out of a stringstream, whi
h
an be
printed out if desired.
Appendix G
The C++ Prepro
essor
479
Appendix H
Lambda expressions
A lambda expression is a nameless fun
tion whi
h
an be
onstru
ted pretty mu
h anywhere
in your
ode and subsequently used. The following expression, for example, represents a
fun
tion whi
h
ompares integers by their absolute values:
[(int a, int b){return abs(a) < abs(b)}
whi
h will evaluate to true, sin
e j3j < j 5j. But more usefully, su
h an expression
an be
used wherever a fun
tion is needed, e.g. as the
omparison fun
tion for sorting. Thus the
following
ode fragment is legal.
#in
lude <algorithm> // so that sort
an be used
int a[100;
...
ode to initialize a...
sort(a,a+100, [(int a, int b){return abs(a) < abs(b)});
This will indeed sort the array a in non-de
reasing order of the absolute values. As you
an see, this is more
ompa
t than dening a named fun
tion, or dening a
lass and overloading the fun
tion
all operator (Se
tions 20.4.2,20.4.3). For example, the
all to sort in
Se
tion 20.4.3
ould have been written as
sort(sve
.begin(), sve
.end(), [(
onst student& a,
onst student& b){
return a.rollno < b.rollno;});
480
481
Lambda expressions
an be spe
ied in many ways. We rst dis
uss the form used above.
[(parameter-list){body}
In this, parameter-list gives the list of parameters that the fun
tion needs, and the body
give the
ode that is to be exe
uted. You have already seen examples of this above.
Note that we have not expli
itly stated the return type of the fun
tion. C++ will infer
this on the basis of the return statements in body. Sometimes, C++ may not be able to
infer
orre
tly, in whi
h
ase you
an spe
ify the return type as follows.
[(parameter-list) -> return-type {body}
Sin
e 1
an have many types, the above
laries that we mean a fun
tion whi
h returns an
int.
H.1.1 The type of a lambda expression
A lambda expression
an be
onsidered to have the type
std::fun
tion<return-type(parameter-types)>
where return-type is the return type of the lambda expression and parameter-types are
the types of the parameters (
omma separated). To use this you must in
lude the header
le <fun
tional>.
For a use of this see Se
tion 25.1.3.
H.1.2 Variable
apture
The body of a lambda expression may a
ess names whi
h are dened outside the lambda
expression, but are visible in the s
ope in whi
h the lambda expression is dened. Su
h names
are sometimes
alled free names of the lambda expression (as opposed to those variable names
that are dened or bound inside the lambda expression). The free names
an either denote
the values of the
orresponding variables at the time the lambda expression was dened, or
denote the referen
es to the
orresponding variables. The former is
alled \
apture of free
names by value" and the latter \
apture of free names by referen
e". To denote
apture by
value, we simply give the names in the initial [, to denote
apture by referen
e, we give the
names prexed by &. Here is an example.
int main(){
int m=10;
std::fun
tion<void()> f = [m(){
out << m << endl;};
std::fun
tion<void()> g = [&m(){
out << m << endl;};
482
m++;
f();
g();
In this f,g
apture m by value and by referen
e respe
tively. Thus the
all f() will print
10, whi
h is the value of m at the time f got dened. The
all g() on the other hand will
print 11, whi
h is the
urrent value of m sin
e g has
aptured m by referen
e.
If you wish to
apture a by referen
e and b by value, you may spe
ify the
apture as
[&a,b. If you want all to be
aptured by value (referen
e) you may spe
ify the
apture as
[= ([&). If you want all to be
aptured by value ex
ept for a,b, you may write [=,&a,&b,
and analogously.
Note that writing the
apture as [ spe
ies no
apture.
For more examples of variable
apture see Chapter 25.
H.1.3 Dangling referen
es
If you
apture a variable by referen
e, and the variable is deallo
ated between the
apture
and the use, then we have the problem of a dangling referen
e. Thus
apture by referen
e
must be done
arefully.
H.1.4 Capturing this
The this pointer, i.e. the pointer to the obje
t whose member fun
tion is being exe
uted
should be
aptured by value, after all, what we need is the
ontent of the pointer. Furthermore, on
e you
apture this, you
an refer to members of the obje
t by giving the names
dire
tly, without prexing them with this->.