Introduction to Programming Languages Programming in C C Scheme Prolog C and SOA 5th Edition Yinong Chen download pdf
Introduction to Programming Languages Programming in C C Scheme Prolog C and SOA 5th Edition Yinong Chen download pdf
com
https://ebookfinal.com/download/introduction-to-programming-
languages-programming-in-c-c-scheme-prolog-c-and-soa-5th-
edition-yinong-chen/
OR CLICK BUTTON
DOWNLOAD EBOOK
https://ebookfinal.com/download/programming-languages-principles-and-
practices-3rd-edition-kenneth-c-louden/
ebookfinal.com
https://ebookfinal.com/download/medicine-in-rural-china-c-c-chen/
ebookfinal.com
https://ebookfinal.com/download/c-programming-in-linux-2nd-edition-
david-haskins/
ebookfinal.com
https://ebookfinal.com/download/c-programming-program-design-
including-data-structures-5th-edition-d-s-malik/
ebookfinal.com
A Complete Guide to Programming in C 1st Edition Ulla
Kirch-Prinz
https://ebookfinal.com/download/a-complete-guide-to-programming-
in-c-1st-edition-ulla-kirch-prinz/
ebookfinal.com
https://ebookfinal.com/download/mastering-c-database-programming-1st-
edition-jason-price/
ebookfinal.com
https://ebookfinal.com/download/programming-with-ansi-c-2nd-edition-
bhushan-trivedi/
ebookfinal.com
https://ebookfinal.com/download/programming-in-prolog-fifth-edition-w-
f-clocksin/
ebookfinal.com
Introduction to Programming Languages
Fifth Edition
Yinong Chen
Arizona State University
Kendall Hunt
publishing company
Cover image courtesy of© Shutter stock, Inc. Used under license.
Kendall Hunt
publis�ing company
www .kendallhunt.com
Send all inquiries to:
4050 West mark Drive
Dubuque, IA 52004-1840
Copyright© 2003, 2006, 2012, 2015 by Yinong Chen and Wei-Tek Tsai
2017 by Kendall Hunt Publishing Company
ISBN 978-1-5249-1699-2
Preface .............................................................................................................................................. x1
Chapter 1 Basic Principles of Programming Languages.................................................................... 1
1.1 Introduction............................................................................................................................ 2
1.1.1 Programming concepts and paradigms 2
1.1.2 Program performance and features of programming languages 3
1.1.3 Development of programming languages 4
1.2 Structures of programming languages ................................................................................... 8
1.2.1 Lexical structure 8
1.2.2 Syntactic structure 9
1.2.3 Contextual structure 9
1.2.4 Semantic structure 9
1.2.5 BNF notation 9
1.2.6 Syntax graph 11
1.3 Data types and type checking .............................................................................................. 12
1.3.1 Data types and type equivalence 13
1.3.2 Type checking and type conversion 13
1.3.3 Orthogonality 14
1.4 Program processing and preprocessing ................................................................................ 16
1.4.1 Interpretation and compilation 16
1.4.2 Preprocessing: macro and inlining 18
* 1.5 Program development .......................................................................................................... 23
1.5.1 Program development process 23
1.5.2 Program testing 24
1.5.3 Correctness proof 27
1.6 Summary .............................................................................................................................. 29
1.7 Homework and programming exercises .............................................................................. 31
Chapter 2 The Imperative Programming Languages, C/C++ ......................................................... 39
2.1 Getting started with CIC++.................................................................................................. 40
2.1.1 Write your first CIC++ program 40
2.1.2 Basic input and output functions 41
2.1.3 Formatted input and output functions 42
2.2 Control structures in CIC++ ................................................................................................. 44
2.2.1 Operators and the order of evaluation 44
111
2.2.2 Basic selection structures (if-then-else and the conditional expression) 45
2.2.3 Multiple selection structure (switch) 46
2.2.4 Iteration structures (while, do-while, and for) 49
2.3 Data and basic data types in CIC++ ..................................................................................... 51
2.3.1 Declaration of variables and functions 51
2.3.2 Scope rule 52
2.3.3 Basic data types 53
2.4 Complex types ..................................................................................................................... 56
2.4.1 Array 56
2.4.2 Pointer 58
2.4.3 String 60
2.4.4 Constants 69
2.4.5 Enumeration type 70
2.5 Compound data types........................................................................................................... 73
2.5.1 Structure types and paddings 73
2.5.2 Union 75
2.5.3 Array of structures using static memory allocation 77
2.5.4 Linked list using dynamic memory allocation 80
2.5.5 Doubly linked list 84
2.5.6 Stack 86
2.6 Standard input and output, files, and file operations ........................................................... 89
2.6.1 Basic concepts of files and file operations 89
2.6.2 File operations in C 90
2.6.3 Flush operation in C 95
2.7 Functions and parameter passing ......................................................................................... 97
2.8 Recursive structures and applications ................................................................................ 101
2.8.1 Loop structures versus recursive structures 101
2.8.2 The fantastic-four abstract approach of writing recursive functions 102
2.8.3 Hanoi Towers 103
2.8.4 Insertion sorting 106
2.8.5 Merge sort algorithm 108
2.8.6 Quick sort algorithm 110
2.8.7 Tree operations 110
2.8.8 Gray code generation 114
2.9 Modular design .................................................................................................................. 116
2.10 Case Study: Putting All Together ...................................................................................... 118
iv
2.11 Summary ............................................................................................................................ 125
2.12 Homework, programming exercises, and projects ............................................................. 127
Chapter 3 The Object-Oriented Programming Language, C++ ................................................... 147
3.1 A long program example: a queue and a priority queue written in C++ ........................... 148
3.2 Class definition and composition ....................................................................................... 151
3.2.1 Class definition 151
3.2.2 Scope resolution operator 152
3.2.3 Objects from a class 153
3.2.4 Definition of constructor and destructor 153
3.3 Memory management and garbage collection ................................................................... 154
3.3.1 Static: global variables and static local variables 155
3.3.2 Runtime stack for local variables 156
3.3.3 Heap: dynamic memory allocation 159
3.3.4 Scope and garbage collection 159
3.3.5 Memory leak detection 164
3.4 Inheritance ......................................................................................................................... 171
3.4.1 Class containment and inheritance 171
3.4.2 Inheritance and virtual function 174
3.4.3 Inheritance and hierarchy 177
3.4.4 Inheritance and polymorphism 191
3.4.5 Polymorphism and type checking 193
3.4.6 Polymorphism and late binding 194
3.4.7 Type Casting in C++ 194
3.5 Function and Operator Overloading .................................................................................. 196
3.5.1 Function overloading 196
3.5.2 Operator overloading 197
3.6 File Operations in C++ ...................................................................................................... 203
3.6.1 File objects and operations in C++ 203
3.6.2 Ignore operation in C++ 208
3.7 Exception Handling ........................................................................................................... 209
3.8 Case Study: Putting All Together ...................................................................................... 213
3.8.1 Organization of the program 213
3.8.2 Header files 214
3.8.3 Source fi1es 216
*3.9 Parallel computing and multithreading .............................................................................. 224
3.9. l Basic concepts in parallel computing and multithreading 224
V
3.9.2 Generic features in C++ 224
3.9.3 Case Study: Implementing multithreading in C++ 226
3.10 Summary ............................................................................................................................ 230
3.11 Homework, programming exercises, and projects ............................................................. 231
Chapter 4 The Functional Programming Language, Scheme ....................................................... 241
4.1 From imperative programming to functional programming .............................................. 242
4.2 Prefix notation.................................................................................................................... 244
4.3 Basic Scheme terminology ................................................................................................ 246
4.4 Basic Scheme data types and functions ............................................................................. 249
4.4.1 Number types 249
4.4.2 Boolean 250
4.4.3 Character 251
4.4.4 String 252
4.4.5 Symbol 252
4.4.6 Pair 252
4.4.7 List 254
4.4.8 Application of Quotes 255
4.4.9 Definition of procedure and procedure type 256
4.4.10 Input/output and nonfunctional features 258
*4.5 Lambda-calculus ................................................................................................................ 260
4.5.1 Lambda-expressions 260
4.5.2 A-procedure and parameter scope 261
4.5.3 Reduction rules 261
4.6 Define your Scheme procedures and macros ..................................................................... 262
4.6.1 Unnamed procedures 263
4.6.2 Named procedures 263
4.6.3 Scopes of variables and procedures 263
4.6.4 Let-form and unnamed procedures 265
4.6.5 Macros 266
4.6.6 Compare and contrast imperative and functional programming paradigms 268
4.7 Recursive procedures ......................................................................................................... 270
4.8 Define recursive procedures on data types ........................................................................ 272
4.8.1 Number manipulations 272
4.8.2 Character and string manipulations 276
4.8.3 List manipulations 277
4.9 Higher-order functions....................................................................................................... 279
Vl
4.9.1 Mapping 280
4.9.2 Reduction 283
4.9.3 Filtering 284
4.9.4 Application of filtering in query languages 286
4.10 Summary ............................................................................................................................ 287
4.11 Homework, programming exercises, and projects ............................................................. 289
Chapter 5 The Logic Programming Language, Prolog .................................................................. 299
5.1 Basic concepts of logic programming in Prolog ................................................................ 299
5.1.1 Prolog basics 300
5.1.2 Structures of Prolog facts, rules, and goals 302
5.2 The Prolog execution model .............................................................................................. 303
5.2.1 Unification of a goal 303
5.2.2 Example of searching through a database 305
5.3 Arithmetic operations and database queries ...................................................................... 306
5.3.1 Arithmetic operations and built-in functions 306
5.3.2 Combining database queries with arithmetic operations 308
5.4 Prolog functions and recursive rules .................................................................................. 310
5.4.l Parameter passing in Prolog 310
5.4.2 Factorial example 311
5.4.3 Fibonacci numbers example 311
5.4.4 Hanoi Towers 312
5.4.5 Graph model and processing 313
5.4.6 Map representation and coloring 314
5.5 List and list manipulation .................................................................................................. 316
5.5.1 Definition of pairs and lists 316
5.5.2 Pair simplification rules 317
5.5.3 List membership and operations 318
5.5.4 Knapsack problem 321
5.5.5 Quick sort 322
5.6 Flow control structures ...................................................................................................... 323
5.6.l Cut 324
5.6.2 Fail 327
5.6.3 Repeat 328
*5.7 Prolog application in semantic Web .................................................................................. 330
5.8 Summary ............................................................................................................................ 331
5.9 Homework, programming exercises, and projects ............................................................. 333
vii
Chapter 6 Fundamentals of the Service-Oriented Computing Paradigm .................................... 341
6.1 Introduction to C# .............................................................................................................. 341
6.1.1 Getting started with C# and Visual Studio 341
6.1.2 Comparison between C++ and C# 343
6.1.3 N amespaces and the using directives 343
6.1.4 The queue example in C# 345
6.1.5 Class and object in C# 346
6.1.6 Parameters: passing by reference with re f&out 349
6.1.7 Base classes and constructors 350
6.1.8 Constructor, destructor, and garbage collection 350
6.1.9 Pointers in C# 351
6.1.10 C# unified type system 352
6.1.11 Further topics in C# 353
6.2 Service-oriented computing paradigm ............................................................................... 353
6.2.l Basic concepts and terminologies 353
6.2.2 Web services development 355
6.2.3 Service-oriented system engineering 356
6.2.4 Web services and enabling technologies 357
6.3 *Service providers: programming web services in C# ...................................................... 358
6.3.l Creating a web service project 359
6.3.2 Writing the service class 360
6.3.3 Launch and access your web services 361
6.3.4 Automatically generating a WSDL file 363
6.4 Publishing and searching web services using UDDI ......................................................... 365
6.4.1 UDDI file 365
6.4.2 ebXML 367
6.4.3 Ad hoc registry lists 368
6.5 Building applications using ASP.Net ................................................................................ 368
6.5.1 Creating your own web browser 368
6.5.2 Creating a Windows application project in ASP.Net 369
6.5.3 Developing a website application to consume web services 374
6.6 Silverlight and Phone Applications Development ............................................................. 377
6.6.1 Silverlight Applications 377
6.6.2 Developing Windows Phone Apps Using Silverlight 380
6.7 Cloud computing and big data processing ......................................................................... 389
6.7.1 Cloud computing 389
6.7.2 Big data 392
Vlll
6.8 Summary ............................................................................................................................ 394
6.9 Homework, programming exercises, and projects ............................................................. 395
Appendix A Basic Computer Architectures and Assembly Language Programming .................... 401
A. l Basic computer components and computer architectures .................................................. 401
A.2 Computer architectures and assembly programming ......................................................... 402
A.3 Subroutines and local variables on stack ........................................................................... 407
Appendix B Programming Environments Supporting C, C++, Scheme, and Prolog ..................... 409
B. l Introduction to operating systems ...................................................................................... 409
B.2 Introduction to Unix and CIC++ programming environments........................................... 412
B.2.1 Unix and Linux operating systems 412
B.2.2 Unix shell and commands 413
B.2.3 Unix system calls 417
B.2.4 Getting started with GNU GCC under the Unix operating system 419
B.2.5 Debugging your CIC++ programs in GNC GCC 421
B.2.6 Frequently used GCC compiler options 424
B.2.7 CIC++ operators 424
B.2.8 Download programming development environments and tutorials 426
BJ Getting started with Visual Studio programming environment ......................................... 426
B.3.1 Creating a CIC++ project in Visual Studio 427
B.3.2 Debugging your CIC++ programs in Visual Studio 429
B.4 Programming environments supporting Scheme programming ........................................ 430
B.4.1 Getting started with DrRacket 430
B.4.2 Download DrRacket programming environment 431
B.5 Programming environments supporting Prolog programming .......................................... 432
B.5.1 Getting started with the GNU Prolog environment 432
B.5.2 Getting started with Prolog programming 433
B.5.3 Download Prolog programming development tools 435
Appendix C ASCII Character Table ................................................................................................... 437
Bibliography ........................................................................................................................................... 439
Index ........................................................................................................................................... 443
ix
Preface
We all have witnessed the rapid development of computer science and its applications in many domains,
particularly in web-based computing (Web 2.0), cloud computing, big data processing, and mobile
computing. As a science discipline, the fundamentals of computer science, including programming
language principles and the classic programming languages, are stable and do not change with the
technological innovations. C, C++, Scheme/LISP, and Prolog belong to the classic programming languages
that were developed several decades ago and are still the most significant programming languages today,
both in theory and in practice. However, the technologies that surround these languages have been changed
and improved, which give these languages new perspectives and new applications. For example, C++ is
extended to generic classes and writing multithread programs. Functional programming principles are
widely used in database query languages and the new object- and service-oriented programming languages
such as C#.
This text is intended for computer science and computer engineering students in their sophomore year of
study. It is assumed that students have completed a basic computer science course and have learned a high
level programming language like C, C++, or Java.
Most of the content presented in the text has been used in the "Introduction to Programming Languages"
course taught by the author in the School of Computer Science at the University of the Witwatersrand at
Johannesburg, and in the Computer Science and Engineering programs at Arizona State University. This
text is different from the existing texts on programming languages that either focus on teaching
programming paradigms, principles, and the language mechanisms, or focus on language constructs and
programming skills. This text takes a balanced approach on both sides. It teaches four programming
languages representing four major programming paradigms. Programming paradigms, principles, and
language mechanisms are used as the vehicle to facilitate learning of the four programming languages in a
coherent way. The goals of such a programming course are to make sure that computer science students are
exposed to different programming paradigms and language mechanisms, and obtain sufficient
programming skills and experience in different programming languages, so that they can quickly use these
or similar languages in other courses.
Although there are many different programming paradigms, imperative, object-oriented, functional, and
logic programming paradigms are the four major paradigms widely taught in computer science and
computer engineering departments around the world. The four languages we will study in the text are the
imperative C, object-oriented C++, functional Scheme, and logic Prolog. At the end of the course, students
should understand
• the language structures at different layers (lexical, syntactic, contextual, and semantic), the control
structures and the execution models of imperative, object-oriented, functional, and logic
programming languages;
• program processing (compilation versus interpretation) and preprocessing (macros and inlining);
• different aspects of a variable, including its type, scope, name, address, memory location, and value.
More specific features of programming languages and programming issues are explored in cooperation with
the four selected languages. Students are expected to have understood and be able to
• write C programs with complex data types, including pointers, arrays, and generic structures, as
well as programs with static and dynamic memory allocation;
xi
• apply the object-oriented features such as inheritance and class hierarchy, polymorphism and
typing, overloading, early versus late binding, as well as the constructors, the destructor and the
management of static memory, stack, and heap in C++;
• apply the functional programming style of parameter passing and write Scheme programs requiring
multiple functions;
• apply the logic programming style of parameter passing, write Prolog facts, rules, and goals, and
use multiple rules to solve problems;
• be able to write programs requiring multiple subprograms/procedures to solve large programming
problems;
• be able to write recursive subprograms/procedures in imperative, object-oriented, functional, and
logic programming languages.
The text has been revised and improved throughout in each of the new editions. In the second edition, the
major change made was the inclusion of programming in Service-Oriented Architecture (SOA). Since the
publication of the first edition in 2003, SOA programming has emerged as a new programming paradigm,
which has demonstrated its strength to become a dominating programming paradigm. All major computing
companies, including HP, IBM, Intel, Microsoft, Oracle, SAP, and Sun Microsystems, have moved into
this new paradigm and are using the new paradigm to produce software and even hardware systems. The
need for skill in SOA programming increases as the deployment of SOA applications increases. This new
SOA paradigm is not only important in the practice of programming, but it also contributes to the concepts
and principles of programming theory. In fact, SOA programming applies a higher level of abstraction,
which requires fewer technical details for building large software applications. We, the authors of this book,
are leading researchers and educators in SOA programming. The inclusion of the new chapter on C# and
SOA programming makes the text unique, which allows teaching of the most contemporary programming
concepts and practice. The new chapter also gives professors a new component to choose from, which adds
flexibility for them to select different contents for different courses. As SOA has developed significantly in
the past 10 years, this chapter is also updated in the fourth edition to include an introduction to Silverlight
animation, phone application development, cloud computing, and big data processing.
In the third edition, we completely rewrite Chapter 5. We also discuss the modem applications of Prolog in
the semantic web (Web 3.0) and its relationship with the currently used web semantic languages RDF
(Resource Description Framework) and OWL (Web Ontology Language). Semantic web is considered the
next generation of web that allows the web to be browsed and explored by both humans and computer
programs. In the third edition revised print, this chapter is further expanded with the latest computing
technologies in cloud computing and big data processing.
Since the publication of the second edition in 2006, the programming environment for Chapter 4, on
Scheme, has been changed from DrScheme to DrRacket. The change does not affect the examples in the
text. They all work in the new DrRacket environment, except certain notational issues. We have updated
Chapter 4 to match the changes made in DrRacket.
As parallel computing and multithreading are becoming more and more important in software development,
the third edition adds a new section on parallel computing and multithreading in C++, in Chapter 3. A
MapReduce example is used as a case study for explaining multithreading in C++. The parallel computing
concept is also emphasized in Chapter 4, where the eager evaluation and higher functions Map and Reduce
are linked to parallel computing.
In the fourth edition, we added a number of new sections and many examples throughout Chapters 1, 2, 3,
4, 5, and 6 to explain the more difficulty concepts. In Chapter 1, macro expansion and execution are
explained in more detail and the order of executions are discussed and showed on different programming
environments. More complex examples of syntax graphs are given in Section 1.2. In Chapter 2, structure
padding is discussed in Section 2.5. The file operations in Section 2.6 are extended. More recursive
xii
examples are given in Section 2.7. A case study that puts a11 together is given in a new section at the end of
the chapter. In Chapter 3, a new subsection on memory leak detection is added in Section 3.3 on memory
management. Section 3.4 on inheritance is extended with a new subsection on type casting. A new section
on C++ file operations is added as Section 3.5. In Chapter 4, the application of filtering in query languages
is added in Section 4.9. In the new editions, Chapter 5 is further extended to include web application and
phone application development in C#. It also extends the discussions to cloud computing and big data
processing.
In the fifth edition, changes and revisions are made throughout the book. In Chapter 2, more data structures
are discussed, including doubly linked list, graphs, and trees. Chapter 3, Object-Oriented Programming
Language C++, is significantly extended to include inheritance, type casting, function overloading and
operator overloading, and C++ file operations. A new section 3.8 Case Study is included to put together all
the concepts and programming mechanisms learned in this chapter. In Appendix B, tutorials on using GNU
GCC environment and Visual Studio environment to edit and debug programs are added.
This edition of the text is organized into six chapters and three appendices. Chapter 1 discusses the
programming paradigms, principles, and common aspects of programming languages. Chapter 2 uses C as
the example of the imperative programming paradigm to teach how to write imperative programs. It is
assumed that students have a basic understanding of a high-level programming language such as C, C++,
or Java. Chapter 3 extends the discussion and programming from C to C++. The chapter focuses on the
main features of object-oriented programming paradigms, including class composition, dynamic memory
management and garbage collection, inheritance, dynamic memory allocation and deallocation, late
binding, polymorphism, and class hierarchy. Chapters 4 and 5 take a major paradigm shift to teach
functional and logic programming, respectively. Students are not required to have any knowledge of
functional and logic programming to learn from these two chapters. Chapter 6 gives a brief introduction to
C# and service-oriented computing paradigm. A full discussion of the service-oriented computing paradigm
is given in another book by the authors: Service-Oriented Computing and Web Software Integration.
Assignments, programming exercises, and projects are given at the end of each chapter. The sections with
an asterisk(*) are optional and can be skipped if time does not permit covering all the material. Chapters 4
and 5 are self-contained and can be taught independently, or in a different order.
The three appendices provide supplementary materials to the main text. In Appendix A, the basic computer
organization and assembly language programming are introduced. If students do not have a basic computer
science background, the material should be covered in the class. Appendix B starts with basic Unix
commands. If the class uses a Unix-based laboratory environment, students can read the appendix to get
started with Unix. Then the appendix introduces the major programming language environments that can
be used to support teaching the four programming languages, including GNU GCC/G++ for C and C++,
Visual Studio for C and C++, DrRacket for Scheme, and GNU Prolog. Appendix C gives the ASCII code
table that is referred to in various parts of the text.
The text consists of six chapters, which can be considered reconfigurable components of a course. A half
semester course(25-30 lecture hours) can teach from two to three chapters, and a full semester course can
teach four to five chapters of choice from the text. Chapter 3(C++) is closely related to Chapter 2. If Chapter
3 is selected as a component of a course, Chapter 2, or a part of Chapter 2, should be selected as well. Other
chapters are relatively independent of each other and can be selected freely to compose a course.
For a half-semester course, sensible course compositions could include: (Chapters 1, 2, 3);(Chapters 2, 3);
(Chapters 1, 2, 6); (Chapters 2, 3, 6); (Chapters 1, 4, 5); and (Chapters 4, 5). For a full semester course,
sensible course compositions could include: (chapters 1, 2, 3, 4, 5); (chapters 1, 2, 3, 4, 6); (Chapters 1, 2,
3, 5, 6); and(Chapters 2, 3, 4, 5, 6).
I wish to thank all those who have contributed to the materials and to the formation of this text. Particularly,
I would like to thank my colleagues Scott Hazelhurst and Conrad Mueller of the University of the
Xlll
Witwatersrand, and Richard Whitehouse of Arizona State University who shared their course materials
with me. Parts of these materials were used in teaching the programming languages course and in preparing
this text. Thomas Boyd, Joe DeLibero, Renee Turban, and Wei-Tek Tsai of Arizona State University
reviewed the drafts of the text and made constructive suggestions and useful comments. Other instructors
using this text have given me invaluable feedback and improvement suggestions, including Janaka
Balasooriya, Calvin Cheng, Mutsumi Nakamura, and Yalin Wang. My teaching assistants helped me in the
past few years to prepare the assignments and programming exercises; particularly, I would like to thank
Ruben Acuna, Raynette Brodie, Justin Convery, Gennaro De Luca, Garrett Gutierrez, and Kevin Liao.
The text was written and revised while I was carrying out a full university workload. I am thankful to my
family. I could not imagine that I would be able to complete the task without their generous support by
allowing me to use most of the weekends in the past year to write the text.
Although I have used the materials in teaching the programming languages courses at the University of the
Witwatersrand, Johannesburg and at Arizona State University for several years, the text was put together
in a short period of time. There are certainly many errors of different kinds. I would appreciate it if you
could send me any corrections, comments, or suggestions for improving the text. My email address
dedicated to dealing with the responses to the text is <yinong.chen@asu.edu>. Instructors who use the text
can contact the author for instructional support, including lecture slides in PowerPoint format and the
solutions to the assignments.
Yinong Chen
December 2016
XIV
Chapter 1
Basic Principles of Programming Languages
Although there exist many programming languages, the differences among them are insignificant compared
to the differences among natural languages. In this chapter, we discuss the common aspects shared among
different programming languages. These aspects include:
• programming paradigms that define how computation is expressed;
• the main features of programming languages and their impact on the performance of programs
written in the languages;
• a brief review of the history and development of programming languages;
• the lexical, syntactic, and semantic structures of programming languages, data and data types,
program processing and preprocessing, and the life cycles of program development.
At the end of the chapter, you should have learned:
• what programming paradigms are;
• an overview of different programming languages and the background knowledge of these
languages;
• the structures of programming languages and how programming languages are defined at the
syntactic level;
• data types, strong versus weak checking;
• the relationship between language features and their performances;
• the processing and preprocessing of programming languages, compilation versus interpretation,
and different execution models of macros, procedures, and inline procedures;
• the steps used for program development: requirement, specification, design, implementation,
testing, and the correctness proof of programs.
The chapter is organized as follows. Section 1.1 introduces the programming paradigms, performance,
features, and the development of programming languages. Section 1.2 outlines the structures and design
issues of programming languages. Section 1.3 discusses the typing systems, including types of variables,
type equivalence, type conversion, and type checking during the compilation. Section 1.4 presents the
preprocessing and processing of programming languages, including macro processing, interpretation, and
compilation. Finally, Section 1.5 discusses the program development steps, including specification, testing,
and correctness proof.
1
1.1 Introduction
1.1.1 Programming concepts and paradigms
Millions of programming languages have been invented, and several thousands of them are actually in use.
Compared to natural languages that developed and evolved independently, programming languages are far
more similar to each other. This is because
• different programming languages share the same mathematical foundation (e.g., Boolean algebra,
logic);
• they provide similar functionality (e.g., arithmetic, logic operations, and text processing);
• they are based on the same kind of hardware and instruction sets;
• they have common design goals: find languages that make it simple for humans to use and efficient
for hardware to execute;
• designers of programming languages share their design experiences.
Some programming languages, however, are more similar to each other, while other programming
languages are more different from each other. Based on their similarities or the paradigms, programming
languages can be divided into different classes. In programming language's definition, paradigm is a set
of basic principles, concepts, and methods for how a computation or algorithm is expressed. The major
paradigms we wilJ study in this text are imperative, object-oriented, functional, and logic paradigms.
The imperative, also called the procedural, programming paradigm expresses computation by fully
specified and fully controlled manipulation of named data in a stepwise fashion. In other words, data or
values are initially stored in variables (memory locations), taken out of (read from) memory, manipulated
in ALU (arithmetic logic unit), and then stored back in the same or different variables (memory locations).
Finally, the values of variables are sent to the 1/0 devices as output. The foundation of imperative languages
is the stored program concept-based computer hardware organization and architecture (von Neumann
machine). The stored program concept will be further explained in the next chapter. Typical imperative
programming languages include all assembly languages and earlier high-level languages like Fortran,
Algol, Ada, Pascal, and C.
The object-oriented programming paradigm is basically the same as the imperative paradigm, except that
related variables and operations on variables are organized into classes of objects. The access privileges of
variables and methods (operations) in objects can be defined to reduce (simplify) the interaction among
objects. Objects are considered the main building blocks of programs, which support language features like
inheritance, class hierarchy, and polymorphism. Typical object-oriented programming languages include
Smalltalk, C++, Java, and C#.
The functional, also called the applicative, programming paradigm expresses computation in terms of
mathematical functions. Since we express computation in mathematical functions in many of the
mathematics courses, functional programming is supposed to be easy to understand and simple to use.
However, since functional programming is very different from imperative or object-oriented programming,
and most programmers first get used to writing programs in imperative or object-oriented paradigms, it
becomes difficult to switch to functional programming. The main difference is that there is no concept of
memory locations in functional programming languages. Each function will take a number of values as
input (parameters) and produce a single return value (output of the function). The return value cannot be
stored for later use. It has to be used either as the final output or immediately as the parameter value of
another function. Functional programming is about defining functions and organizing the return values of
one or more functions as the parameters of another function. Functional programming languages are mainly
2
based on the lambda calculus that will be discussed in Chapter 4. Typical functional programming
languages include ML, SML, and Lisp/Scheme.
The logic, also called the declarative, programming paradigm expresses computation in terms of logic
predicates. A logic program is a set of facts, rules, and questions. The execution process of a logic program
is to compare a question to each fact and rule in the given fact and rulebase. If the question finds a match,
we receive a yes answer to the question. Otherwise, we receive a no answer to the question. Logic
programming is about finding facts, defining rules based on the facts, and writing questions to express the
problems we wish to solve. Prolog is the only significant logic programming language.
It is worthwhile to note that many languages belong to multiple paradigms. For example, we can say that
C++ is an object-oriented programming language. However, C++ includes almost every feature of C and
thus is an imperative programming language too. We can use C++ to write C programs. Java is more object
oriented, but still includes many imperative features. For example, Java's primitive type variables do not
obtain memory from the language heap like other objects. Lisp contains many nonfunctional features.
Scheme can be considered a subset of Lisp with fewer nonfunctional features. Prolog's arithmetic
operations are based on the imperative paradigm.
Nonetheless, we will focus on the paradigm-related features of the languages when we study the sample
languages in the next four chapters. We will study the imperative features of C in Chapter 2, the object
oriented features of C++ in Chapter 3, and the functional features of Scheme and logic features of Prolog
in Chapters 4 and 5, respectively.
1.1.2 Program performance and features of programming languages
A programming language's features include orthogonality or simplicity, available control structures, data
types and data structures, syntax design, support for abstraction, expressiveness, type equivalence, and
strong versus weak type checking, exception handling, and restricted aliasing. These features will be further
explained in the rest of the book. The performance of a program, including reliability, readability,
writability, reusability, and efficiency, is largely determined by the way the programmer writes the
algorithm and selects the data structures, as well as other implementation details. However, the features of
the programming language are vital in supporting and enforcing programmers in using proper language
mechanisms in implementing the algorithms and data structures. Table 1.1 shows the influence of a
language's features on the performance of a program written in that language.
Readability/
L� Efficiency Reusability Writability Reliability
Simplicity/Orthogonality ✓ ✓ ✓ ✓
Control structures ✓ ✓ ✓ ✓
Typing and data structures ✓ ✓ ✓ ✓
Syntax design ✓ ✓ ✓
Support for abstraction ✓ ✓ ✓
Expressiveness ✓ ✓
Strong checking ✓
Restricted aliasing ✓
Exception handling ✓
3
The table indicates that simplicity, control structures, data types, and data structures have significant impact
on all aspects of performance. Syntax design and the support for abstraction are important for readability,
reusability, writability, and reliability. However, they do not have a significant impact on the efficiency of
the program. Expressiveness supports writability, but it may have a negative impact on the reliability of the
program. Strong type checking and restricted aliasing reduce the expressiveness of writing programs, but
are generally considered to produce more reliable programs. Exception handling prevents the program from
crashing due to unexpected circumstances and semantic errors in the program. All language features will
be discussed in this book.
1.1.3 Development of programming languages
The development of programming languages has been influenced by the development of hardware, the
development of compiler technology, and the user's need for writing high-performance programs in terms
of reliability, readability, writability, reusability, and efficiency. The hardware and compiler limitations
have forced early programming languages to be close to the machine language. Machine languages are the
native languages of computers and the first generation of programming languages used by humans to
communicate with the computer.
Machine languages consist of instructions of pure binary numbers that are difficult for humans to remember.
The next step in programming language development is the use of mnemonics that allows certain symbols
to be used to represent frequently used bit patterns. The machine language with sophisticated use of
mnemonics is called assembly language. An assembly language normally allows simple variables, branch
to a label address, different addressing modes, and macros that represent a number of instructions. An
assembler is used to translate an assembly language program into the machine language program. The
typical work that an assembler does is to translate mnemonic symbols into corresponding binary numbers,
substitute register numbers or memory locations for the variables, and calculate the destination address of
branch instructions according to the position of the labels in the program.
This text will focus on introducing high-level programming languages in imperative, object-oriented,
functional, and logic paradigms.
The first high-level programming language can be traced to Konrad Zuse's Plankalkiil programming system
in Germany in 1946. Zuse developed his Z-machines Zl, Z2, Z3, and Z4 in the late 1930s and early 1940s,
and the Plankalkill system was developed on the Z4 machine at ETH (Eidgenossisch Technische
Hochschule) in Ztirich, with which Zuse designed a chess-playing program.
The first high-level programming language that was actually used in an electronic computing device was
developed in 1949. The language was named Short Code. There was no compiler designed for the language,
and programs written in the language had to be hand-compiled into the machine code.
The invention of the compiler was credited to Grace Hopper, who designed the first widely known
compiler, called AO, in 1951.
The first primitive compiler, called Autocoder, was written by Alick E. Glennie in 1952. It translated
Autocode programs in symbolic statements into machine language for the Manchester Mark I computer.
Autocode could handle single letter identifiers and simple formulas.
The first widely used language, Fortran (FORmula TRANslating), was developed by the team headed by
John Backus at IBM between 1954 and 1957. Backus was also the system co-designer of the IBM 704 that
ran the first Fortran compiler. Backus was later involved in the development of the language Algol and the
Backus-Naur Form (BNF). BNF was a formal notation used to define the syntax of programming languages.
Fortran II came in 1958. Fortran III came at the end of 1958, but it was never released to the public. Further
versions of Fortran include ASA Fortran 66 (Fortran IV) in 1966, ANSI Fortran 77 (Fortran V) in 1978,
4
ISO Fortran 90 in 1991, and ISO Fortran 95 in 1997. Unlike assembly languages, the early versions of
Fortran allowed different types of variables (real, integer, array), supported procedure call, and included
simple control structures.
Programs written in programming languages before the emergence of structured programming concepts
were characterized as spaghetti programming or monolithic programming. Structured programming is a
technique for organizing programs in a hierarchy of modules. Each module had a single entry and a single
exit point. Control was passed downward through the structure without unconditional branches (e.g., goto
statements) to higher levels of the structure. Only three types of control structures were used: sequential,
conditional branch, and iteration.
Based on the experience of Fortran I, Algol 58 was announced in 1958. Two years later, Algol 60, the first
block-structured language, was introduced. The language was revised in 1963 and 1968. Edsger Dijkstra is
credited with the design of the first Algol 60 compiler. He is famous as the leader in introducing structured
programming and in abolishing the goto statement from programming.
Rooted in Algol, Pascal was developed by Niklaus Wirth between 1968 and 1970. He further developed
Modula as the successor of Pascal in 1977, then Modula-2 in 1980, and Oberon in 1988. Oberon language
had Pascal-like syntax, but it was strongly typed. It also offered type extension (inheritance) that supported
object-oriented programming. In Oberon-2, type-bound procedures (like methods in object-oriented
programming languages) were introduced.
The C programming language was invented and first implemented by Dennis Ritchie at DEC between 1969
and 1973, as a system implementation language for the nascent Unix operating system. It soon became one
of the dominant languages at the time and even today. The predecessors of C were the typeless language
BCPL (Basic Combined Programming Language) by Martin Richards in 1967 and then the B written by
Ken Thompson in 1969. C had a weak type checking structure to allow a higher level of programming
flexibility.
Object-oriented (00) programming concepts were first introduced and implemented in the Simula
language, which was designed by Ole-Johan Dahl and Kristen Nygaard at the Norwegian Computing Center
(NCC) between 1962 and 1967. The original version, Simula I, was designed as a language for discrete
event simulation. However, its revised version, Simula 67, was a full-scale general-purpose programming
language. Although Simula never became widely used, the language was highly influential on the modern
programming paradigms. It introduced important object-oriented concepts like classes and objects,
inheritance, and late binding.
One of the object-oriented successors of Simula was Smalltalk, designed at Xerox PARC, led by Alan Kay.
The versions developed included Smalltalk-72, Smalltalk-74, Smalltalk-76, and Smalltalk-80. Smalltalk
also inherited functional programming features from Lisp.
Based on Simula 67 and C, a language called "C with classes" was developed by Bjarne Stroustrup in 1980
at Bell Labs, and then revised and renamed as C++ in 1983. C++ was considered a better C (e.g., with
strong type checking), plus it supported data abstraction and object-oriented programming inherited from
Simula 67.
Java was written by Ja mes Gosling, Patrick Naughton, Chris Warth, Ed Frank, and Mike Sheridan at Sun
Microsystems. It was called Oak at first and then renamed Java when it was publicly announced in 1995.
The predecessors of Ja va were C++ and Smalltalk. Java removed most non-object-oriented features of C++
and was a simpler and better object-oriented programming language. Its two-level program processing
concept (i.e., compilation into an intermediate bytecode and then interpretation of the bytecode using a
small virtual machine) made it the dominant language for programming Internet applications. Java was still
5
not a pure object-oriented programming language. Its primitive types, integer, floating-point number,
Boolean, etc., were not classes, and their memory allocations were from the language stack rather than from
the language heap.
Microsoft's C# language was first announced in June 2000. The language was derived from C++ and Java.
It was implemented as a full object-oriented language without "primitive" types. C# also emphasizes
component-oriented programming, which is a refined version of object-oriented programming. The idea is
to be able to assemble software systems from prefabricated components.
Functional programming languages are relatively independent of the development process of imperative
and object-oriented programming languages. The first and most important functional programming
language, Lisp, short for LISt Processing, was developed by John McCarthy at MIT. Lisp was first released
in 1958. Then Lisp 1 appeared in 1959, Lisp 1.5 in 1962, and Lisp 2 in 1966. Lisp was developed
specifically for artificial intelligence applications and was based on the lambda calculus. It inherited its
algebraic syntax from Fortran and its symbol manipulation from the Information Processing Language, or
IPL. Several dialects of Lisp were designed later, for example, Scheme, InterLisp, FranzLisp, MacLisp, and
ZetaLisp.
As a Lisp dialect, Scheme was first developed by G. L. Steele and G. J. Sussman in 1975 at MIT. Several
important improvements were made in its later versions, including better scope rule, procedures (functions)
as the first-class objects, removal of loops, and sole reliance on recursive procedure calls to express loops.
Scheme was standardized by the IEEE in 1989.
Efforts began on developing a common dialect of Lisp, referred to as Common Lisp, in 1981. Common
Lisp was intended to be compatible with all existing versions of Lisp dialects and to create a huge
commercial product. However, the attempt to merge Scheme into Lisp failed, and Scheme remains an
independent Lisp dialect today. Common Lisp was standardized by the IEEE in 1992.
Other than Lisp, John Backus's FP language also belongs to the first functional programming languages.
FP was not based on the lambda calculus, but based on a few rules for combining function forms. Backus
felt that lambda calculus's expressiveness on computable functions was much broader than necessary. A
simplified rule set could do a better job.
At the same time that FP was developed in the United States, ML (Meta Language) appeared in the United
Kingdom. Like Lisp, ML was based on lambda calculus. However, Lisp was not typed (no variable needs
to be declared), while ML was strongly typed, although users did not have to declare variables that could
be inferentially determined by the compiler.
Miranda is a pure functional programming language developed by David Turner at the University of Kent
in 1985-1986. Miranda achieves referential transparency (side effect-free) by forbidding modification to
global variables. It combines the main features of SASL (St. Andrews Static Language) and KRC (Kent
Recursive Calculator) with strong typing similar to that of ML. SASL and KRC are two earlier functional
programming languages designed by Turner at the University of St Andrews in 1976 and at the University
of Kent in 1981, respectively.
There are many logic-based programming languages in existence. For example, ALF (Algebraic Logic
Functional language) is an integrated functional and logic language based on Hom clauses for logic
programming, and functions and equations for functional programming. Godel is a strongly typed logic
programming language. The type system is based on a many-sorted logic with parametric polymorphism.
RELFUN extends Horn logic by using higher-order syntax, first-class finite domains, and expressions of
nondeterministic, nonground functions, explicitly distinguished from structures.
6
The most significant member in the family of logic programming languages is the Horn logic-based
Prolog. Prolog was invented by Alain Colmerauer and Philippe Roussel at the University of Aix-Marseille
in 1971. The first version was implemented in 1972 using Algol. Prolog was designed originally for natural
language processing, but it has become one of the most widely used languages for artificial intelligence.
Many implementations appeared after the original work. Early implementations included C-Prolog,
ESLPDPRO, Frolic, LM-Prolog, Open Prolog, SB-Prolog, and UPMAIL Tricia Prolog. Today, the
common Prologs in use are AMZI Prolog, GNU Prolog, LPA Prolog, Quintus Prolog, SICSTUS Prolog,
SNI Prolog, and SWI-Prolog.
Distributed computing involves computation executed on more than one logical or physical processor or
computer. These units cooperate and communicate with each other to complete an integral application. The
computation units can be functions(methods) in the component, components, or application programs. The
main issues to be addressed in the distributed computing paradigms are concurrency, concurrent computing,
resource sharing, synchronization, messaging, and communication among distributed units. Different levels
of distribution lead to different variations. Multithreading is a common distributed computing technique
that allows different functions in the same software to be executed concurrently. If the distributed units are
at the object level, this is distributed 00 computing. Some well-known distributed 00 computing
frameworks are CORBA (Common Object Request Broker Architecture) developed by OMG (Object
Management Group) and Distributed Component Object Model(DCOM) developed Microsoft.
Service-oriented computing (SOC) is another distributed computing paradigm. SOC differs from
distributed 00 computing in several ways:
• SOC emphasizes distributed services(with possibly service data) rather than distributed objects;
• SOC explicitly separates development duties and software into service provision, service
brokerage, and application building through service consumption;
• SOC supports reusable services in (public or private) repositories for matching, discovery and
(remote or local) access;
• In SOC, services communicate through open standards and protocols that are platform independent
and vendor independent.
It is worthwhile noting that many languages belong to multiple computing paradigms; for example, C++ is
an 00 programming language. However, C++ also includes almost every feature of C. Thus, C++ is also
an imperative programming language, and we can use C++ to write C programs.
Java is more an 00 language, that is, the design of the language puts more emphasis on the object
orientation. However, it still includes many imperative features; for example, Java's primitive type
variables use value semantics and do not obtain memory from the language heap.
Service-oriented computing is based on the object-oriented computing. The main programming languages
in service-oriented computing, including Java and C#, can be used for object-oriented software
development.
The latest development in programming languages is the visual/graphic programming. MIT App Inventor
(http://appinventor.mit.edu/) uses drag-and-drop style puzzles to construct phone applications in Android
platform. Carnegie Mellon's Alice (http://www.alice.org/) is a 3D game and movie development
environment on desktop computers. It uses a drop-down list for users to select the available functions in a
stepwise manner. App Inventor and Alice allow novice programmers to develop complex applications using
visual composition at the workflow level. Intel's loT Service Orchestration Layer is a workflow language
that allows quick development of loT (Internet of Things) applications on Intel's loT platforms, such as
Edison and Galileo(http://O1org.github.io/intel-iot-services-orchestration-layer/).
7
Microsoft Robotics Developer Studio (MRDS) and Visual Programming Language (VPL) are specifically
developed for robotics applications (https://en.wikipedia.org/wiki/Microsoft_Robotics_Developer_
Studio). They are milestones in software engineering,robotics,and computer science education from many
aspects. MRDS VPL is service-oriented; it is visual and workflow-based; it is event-driven; it supports
parallel computing; and it is a great educational tool that is simple to learn and yet powerful and expressive.
Unfortunately,Microsoft stopped its development and support to MRDS VPL in 2014,which lead to many
schools' courses, including ASU FSEl00 course,using VPL without further support.
To keep ASU course running and also help the other schools,Dr. Yinong Chen,Gennaro De Luca,and the
development team at IoT and Robotics Education Laboratory at ASU took the challenge and the
responsibility to develop a new visual programming environment at Arizona State University in 2015: ASU
VIPLE, standing for Visual IoT/Robotics Programming Language Environment. It is designed to support
as many features and functionalities that MRDS VPL supports as possible, in order to better serve the
MRDS VPL community in education and research. To serve this purpose, VIPLE also keeps similar user
interface,so that the MRDS VPL development community can use VIPLE with little learning curve. VIPLE
does not replace MRDS VPL. Instead, it extends MRDS VPL in its capacity in multiple aspects. It can
connect to different physical robots, including LEGO EV3 and any robots based on the open architecture
processors. ASU VIPLE software and documents are free and can be downloaded at: http://
neptune.fulton.ad.asu.edu/WSRepository/VIPLE/
8
1.2.2 Syntactic structure
Syntactic structure defines the grammar of forming sentences or statements using the lexical units. An
imperative programming language normally offers the following kinds of statements:
• Assignments: An assignment statement assigns a literal value or an expression to a variable.
• Conditional statements: A conditional statement tests a condition and branches to a certain
statement based on the test result (true or false). Typical conditional statements are if-then, if-then
else, and switch (case).
• Loop statements: A loop statement tests a condition and enters the body of the loop or exits the
loop based on the test result (true or false). Typical loop statements are for-loop and while-loop.
The formal definition of lexical and syntactic structures will be discussed in Section 1.2.5.
1.2.3 Contextual structure
Contextual structure (also called static semantics) defines the program semantics before dynamic
execution. It includes variable declaration, initialization, and type checking.
Some imperative languages require that all variables be initialized when they are declared at the contextual
layer, while other languages do not require variables to be initialized when they are declared, as long as the
variables are initialized before their values are used. This means that initialization can be done either at the
contextual layer or at the semantic layer.
Contextual structure starts to deal with the meaning of the program. A statement that is lexically correct
may not be contextually correct. For example:
String str = "hello";
inti O;
int j = i + str;
All declaration statements are lexically correct, but the last statement is contextually incorrect because it
does not make sense to add an integer variable to a string variable.
More about data type, type checking, and type equivalence will be discussed in Section 1.3.
1.2.4 Semantic structure
Semantic structure describes the meaning of a program, or what the program does during the execution.
The semantics of a language are often very complex. In most imperative languages, there is no formal
definition of semantic structure. Informal descriptions are normally used to explain what each statement
does. The semantic structures of functional and logic programming languages are normally defined based
on the mathematical and logical foundation on which the languages are based. For example, the meanings
of Scheme procedures are the same as the meanings of the lambda expressions in lambda calculus on which
Scheme is based, and the meanings of Prolog clauses are the same as the meanings of the clauses in Hom
logic on which Prolog is based.
1.2.5 BNF notation
BNF (Backus-Naur Form) is a meta language that can be used to define the lexical and syntactic structures
of another language. Instead of learning BNF language first and then using BNF to define a new language,
we will first use BNF to define a simplified English language that we are familiar with, and then we will
learn BNF from the definition itself.
9
A simple English sentence consists of a subject, a verb, and an object. The subject, in tum, consists of
possibly one or more adjectives followed by a noun. The object has the same grammatical structure. The
verbs and adjectives must come from the vocabulary. Formally, we can define a simple English sentence
as follows:
<sentence> :: = <subject><verb><object>
<subject> :: = <noun> I <article><noun> I <adjective><noun>
<article><adjective><noun>
<adjective> :: = <adjective> I <adjective><adjective>
<object> :: = <subject>
<noun>:: = table I horse I computer
<article> :: = the I a
<adjective> :: = big I fast I good I high
<verb>:: = is I makes
In the definitions, the symbol ": : =" means that the name on the left-hand side is defined by the expression
on the right-hand side. The name in a pair of angle brackets "<>" is nonterminal, which means that the
name needs to be further defined. The vertical bar "I" represents an "or" relation. The boldfaced names are
terminal, which means that the names need not be further defined. They form the vocabulary of the
language.
We can use the sentence definition to check whether the following sentences are syntactically correct.
fast high big computer is good table 1
the high table is a good table 2
a fast table makes the high horse 3
the fast big high computer is good 4
good table is high 5
a table is not a horse 6
is fast computer good 7
The first sentence is syntactically correct, although it does not make much sense. Three adjectives in the
sentence are correct because the definition of an adjective recursively allows any number of adjectives to
be used in the subject and the object of a sentence. The second and third sentences are also syntactically
correct according to the definition.
The fourth and fifth sentences are syntactically incorrect because a noun is missing in the object of the
sentences. The sixth sentence is incorrect because "not" is not a terminal. The last sentence is incorrect
because the definition does not allow a sentence to start with a verb.
After we have a basic understanding of BNF, we can use it to define a small programming language. The
first five lines define the lexical structure, and the rest defines the syntactic structure of the language.
<letter> :: = alblcldlelflglhliljlklllmlnlolplqlrlsltlulvlwlxlylz
<digit> :: = O 1112 I 3 I 4 15 I 6 I 7 I 8 I 9
<symbol> :: = l@I. 1�1?1#1$
<char> <letter>l<digit>l<symbol>
<operator> : : = +l-l*l/1%1<1>1==1<=1>=1andlorlnot
<identifier> : : = <letter>I <identifier><char>
<number> : := <digit>l<number><digit>
<item> : := <identifier>l<number>
10
<expression> ::= <itern> I (<expression>) I
<expression><operator><expression>
<branch> ::= if <expr>then {<block>} I
if <expr>then {<block>}else {<block>}
<switch> : : = switch<expr>{<sbody>}
<sbody> <cases> I <cases>; default :<block>
<cases> case<value>:<block> I <cases> ; case<value>:<block>
<loop> : : = while <expr>do {<block>}
<assignment> ::= <identifier>=<expression>;
<statement> ::= <assignment>l<branch>l<loop>
<block> ::= <statement>i<block>;<statement>
Now we use the definition to check which of the following statements are syntactically correct.
suml = O; 1
while suml <= 100 do { 2
suml = suml + (al + a2) * (3b % 4*b); } 3
if suml == 120 then 2sum - suml else sum2 + suml; 4
p4#rd_2 = ((la + a2) * (b3 % b4))/ (c7 - c8); 5
foo.bar = (al + a2 - b3 - b4); 6
(al/ a2) = (c3 - c4); 7
According to the BNF definition of the language, statements 1 and 2 are correct. Statements 3 and 4 are
incorrect because 3b and 2sum are neither acceptable identifiers nor acceptable expressions. Statement 5 is
inwrr�d. Stalt:m1�ut 6 is incorrect because an identifier must sta11 with a letter. Statement 7 i3 incorrect
because the left-hand side of an assignment statement must be an identifier.
1.2.6 Syntax graph
BNF notation provides a concise way to define the lexical and syntactic structures of programming
languages. However, BNF notations, especially the recursive definitions, are not always easy to understand.
A graphic form, called a syntax graph, also known as railroad tracks, is often used to supplement the
readability of BNF notation. For example, the identifier and the if-then-else statement corresponding to the
BNF definitions can be defined using the syntax graphs in Figure 1.1. The syntax graph for the identifier
requires that an identifier start with a letter, may exit with only one letter, or follow the loops to include any
number of letters, digits, or symbols. In other words, to check the legitimacy of an identifier, we need to
travel through the syntax graph following the arrows and see whether we can find a path that matches the
given identifier. For instance, we can verify that len 23 is a legitimate identifier as follows. We travel
through the first <letter> once, travel through the second <letter> on the back track twice, travel
through the <symbol> once, and finally travel through the <digit> twice, and then we exit the definition.
On the other hand, if you try to verify that 23 len is a legitimate identifier, you will not be able to find a
path to travel through the syntax graph.
Using the if-then-else syntax graph in Figure 1. 1, we can precisely verify whether a given statement is a
legitimate if-then-else statement. The alternative route that bypasses the else branch signifies that the else
branch is optional. Please note that the definition of the if-then-else statement here is not the same as the if
then-else statement in C language. The syntax graph definitions of various C statements can be found in
Chapter 2.
11
---+ <letter>
identifier <letter>
<digit>
r
<symbol>
value
<array'>
<bool>
<number>
_., <object>
-+ __...,
siring
II
►
<Unicode character except
quote and backslash>
L.� -.--
-. [-L <value
array
>-
�
i 1 - ...
\
<backspace>
<formfeect=-
-►
·►
' .
<newline> -.
< carriage-return.> --llt
_____. true --tlj
< ho rizontal-tab> ➔
booi---c=: fake ----.,I _.,
b'·•
► <4-h ex- c1i. gits> •
1
numb;- ;.
Jl < .. digit0l-9>
-<cli.git>
:f
--:'i":-----------r--
h ��-c-. <di&t> t ►
12
1.3.1 Data types and type equivalence
A data type is defined by the set of primary values allowed and the operations defined on these values. A
data type is used to declare variables (e.g., integer, real, array of integer, string, etc.).
Type checking is the activity of ensuring that the data types of operands of an operator are legal or
equivalent to the legal type.
Now the question is, what types are equivalent? For example, are int and short types, and int andfloat types
in C language equivalent? Are the following operations legal in C?
13
C++ and Java are strongly typed languages. Functional programming languages are normally weakly typed
because mathematical functions are not typed. However, ML is strongly typed, although users did not have
to declare variables that could be inferentially determined by the compiler.
On the other hand, in a weakly typed language:
• not every name has to be associated with a single type at the compilation time;
• structural equivalence is used; and
• a variable of a subtype is acceptable, and implicit type conversion, called coercion, is allowed.
Type T l is considered the subtype of T2, if the set of data of T l is a subset of data of T2 and the set of
operations of T l is a subset of operations of T2. For example, an integer type can be considered a subtype
of floating-point type. C, Scheme, and Prolog are weakly typed programming languages. Typeless
languages like BCPL are weakly typed.
If a data type is a subtype of another type, it does not mean that the two types have the equivalent or similar
structure. For example, in a 32-bit computer, an integer number 3 is represented in a simple binary form,
as shown in the upper part of Figure 1.3. On the other hand, a floating-point number is normally represented
in a three-segment format (IEEE 7 54 Standard), with the leftmost bit representing the sign (positive or
negative), the next 8 bits representing the exponent, and the remaining 23 bits representing the fraction of
the number, as shown in the lower part of Figure 1.3.
inti 3 1 00000000000000000000000000000011 1
float j
sign fraction
Figure 1.3. Integer and floating-point numbers have different internal structures.
In a strongly typed programming language, you can still use different types of variables in a single
expression. In this case, one has to do explicit type conversion. In CIC++, explicit type conversion is called
typecasting: the destination type in parentheses is placed before the variable that has an incompatible type.
In the following statement, explicit conversion is used to convert variable s of salary type and variable a
of age type into int type:
i = (int)s + (int)a;
Strong type checking trades flexibility for reliability. It is generally considered a good policy and is used in
most recent programming languages.
1.3.3 Orthogonality
The word orthogonality refers to the property of straight lines meeting at right angles or independent
random variables. In programming languages, orthogonality refers to the property of being able to combine
various language features systematically. According to the way the features are combined, we can
distinguish three kinds of orthogonality: compositional, sort, and number orthogonality.
Compositional orthogonality: If one member of the set of features S1 can be combined with one member
of the set of features S2, then all members of S1 can be combined with all members of S2, as shown in Figure
1.4.
14
Figure 1.4. Compositional orthogonality.
For example, assume that S1 is the set of different kinds of declarations: (1) plain, (2) initializing, and (3)
constant. For example
(1) type x i; (2) type x i = 5; (3) const type x i = 5;
Sort orthogonality: If one member of the set of features S1 can be combined with one member of the set
of features S2, then this member of S1 can be combined with all members of S2, as shown in Figure 1.5.
For example, if we know that int i is allowed (the combination plain-int), then, according to sort
orthogonality, plain boolean, plain int, plain float, and plain array will be allowed, that is:
(1) boolean b;
(2) int i;
(3) float f;
(4) array a[] ;
imply
imply
16
source object
program code loader
(binary)
compiler other
binaries
�a
Figure 1.7. Compilation-based program processing.
simulator
or
hardware
The advantage of interpretation is that a separate program-processing phase (compilation) is saved in the
program development cycle. This feature allows the program to be updated without stopping the system.
The interpreter can immediately and accurately locate any errors. However, the execution speed with
interpretation is slower than the execution of machine code after the compilation. It is also more difficult
to interpret programs written in very high-level languages.
To make use of the advantages of both compilation and interpretation, Java offers a combined solution to
program processing. As shown in Figure 1.8, Java source program is first translated by a compiler into an
assembly language-like intermediate code, called bytecode. The bytecode is then interpreted by an
interpreter called Java Virtual Machine (NM). The advantage of using the intermediate code is that the
compiler will be independent of the machine on which the program is executed. Thus, only a single compiler
needs to be written for all Java programs running on any machine. Another advantage is that the bytecode
is a low-level language that can be interpreted easily. It thus makes NM small enough to be integrated into
an Internet browser. In other words, Java bytecode programs can be transferred over the Internet and
executed in the client's browser. This ability makes Java the dominant language for Internet application
development and implementation.
17
memory for internal data structures when each method is first called. In other words, JIT compilation lies
between the complete compilation and statement-by-statement interpretation.
Unlike the Java environment, Visual Studio is language agnostic. Although C# is considered its flagship,
Visual Studio is not designed for a specific language. Developers are open to use the common libraries and
functionality of the environment while coding their high-level application in the language of their choice.
1.4.2 Preprocessing: macro and inlining
Many programming languages a1low programmers to write macros or inline procedures that preserve the
structure and readability of programs while retaining the efficiency. The purpose of program
preprocessing is to support macros and inline procedures. The preprocessing phase is prior to the code
translation to the assembly or machine code. The preprocessor is normally a part of the compiler.
Just-In-Time
C# compiler
compiler
CIC++ compiler
JIT simulator
F# compiler IL in or
Scheme compiler CLR hardware
Prolog compiler
X compiler Common
Intermediate Language
languages compilers Language Runtime
x = MAXVAL + QUADFN(5,16);
y MAXVAL - QUADFN(l,3);
The last two statements will be replaced by the macro preprocessor as:
X = 100 + 5*5 + 16*16 - 2*5*16;
18
Macros are more efficient than procedure calls because the body part of the macro is copied into the
statement where the macro is called. No control flow change is necessary. This process is also called
inlining. On the other hand, if the macro is implemented as a procedure/function:
#define MAXVAL 100
int QUADFN(a,b) {return a*a + b*b - 2*a*b;}
x = MAXVAL + QUADFN(5,16);
y MAXVAL - QUADFN(l,3);
a procedure will cause a control flow change that usually needs to save the processor environment, including
registers and the program counter (return address), onto the stack. This process sometimes is called out
lining. Obviously, inlining is much more efficient than out-lining.
Macro preprocessing is a very simple and straightforward substitution of the macro body for the macro
name. It is the programmer's responsibility to make sure that such a simple substitution will produce the
correct code. A slight overlook could lead to a programming error. For example, if we want to define a
macro to obtain the absolute value of a variable, we write the following macro in C:
#define abs(a) ((a<0) ? -a : a)
where the C statement ((a<0) ? -a : a) returns -a if a<0; otherwise, it returns a.
This macro definition of the absolute-value function looks correct, but it is not. For example, if we call the
macro in the following statement:
j = abs(2+5); II we expect 7 to be assigned to j.
The statement does produce a correct result. However, if we call the macro in the following statement:
j = abs(2-5); II we expect +3 to be assigned to j.
The statement will produce an incorrect result. The reason is that the macro-processor will replace
"abs(2-5)" by "((a<0) ? -a : a)" and then replace the parameter "a" by "2-5", resulting in the
statement:
j = ((2-5 < 0) ? -2-5 : 2-5);
Since (2-5 < o) is true, this statement will produce the result of -2-5 = -7, which is assigned to the
variable j. Obviously, this result is incorrect, because we expect +3 to be assigned to j.
Examine a further example. If we write a statement:
j = abs(-2-5);
The macro-processor will replace"abs(-2-5)" by"((a<0) ? -a a)" and then replace the parameter
"a" by "-2-5," resulting in the statement:
j = ( (-2-5 < 0) ? --2-5 : -2-5);
The "--2" in the preprocessed statement may result in a compiler error.
The problem in this abs(a) macro is that we expect the expression that replaces a to be a unit. All
operations within the unit should be done before -a is performed. This is the case when we write a function
or procedure. However, the macro replacement does not guarantee this order of operation, and the
programmer must understand the difference. A correct version of the abs(a) macro is:
#define abs(a) ((a<0) ? -(a) : a) II correct version of abs(a) macro
19
Putting the "a" in a pair ofparentheses guarantees that the operations within a are completed before -a is
performed.
Owing to the nature of simple textual replacement, a macro may cause side effects. A side effect is an
unexpected or unwanted modification ofa state. When we use a global variable to pass values between the
caller procedure and the called procedure, a modification ofthe global variable in the called procedure is a
side effect.
Next, examine the side effect in the correctly defined abs(a) macro discussed earlier. Ifwe call the macro
in the following code:
i 3;
j = abs(++i); // we expect 4 to be assigned to j.
According to the way a macro is preprocessed, the following code will be produced by the macro-processor:
i 3;
j = ( (++i < 0) ? -(++i) : ++i);
When the second statement is executed, variable i will be incremented twice. According to the definition
of the expression ++i, variable i will be incremented every time before i is accessed. There is another
similar expression in CIC++: i++, which increments variable i every time after i is accessed. Similarly,
CIC++ have expressions --i and i--, etc.
In the earlier statement, variable i will be accessed twice: first when we assess (++i < 0) , and then the
second ++i will be accessed after the condition is assessed as false. As a result, number 5, instead of4, will
be assigned to the variable j.
Macros can be used to bring (inline) a piece ofassembly language code into a high-level language program.
For example:
#define PORTIO asm \
\
asm mov al, 2 \
asm mov dx, 0xD007 \
asm out al, dx \
The back slash \ means that there is no line break when performing macro replacement. When we make a
macro call to PORTIO in the program, this macro call will be replaced by:
_asm { asm mov al, 2 _asm mov dx, 0xD007 _asm out al, dx}
where _asm is the CIC++ keyword for assembly instructions. If the compiler is translating the program
into assembly code, nothing needs to be done with a line that starts with _asm. Ifthe compiler is translating
the program into machine code, the compiler will call the assembler to translate this line into machine code.
For the execution of macros, different runtimes (execution engines) may process the translated code
indifferent order. As an example, we consider the following code, which has two pairs of functions and
macros.
/* Side effect, Macro versus Function */
#include <stdio.h>
#pragma warning(disable : 4996) // comment out if not in Visual Studio
#define macl(a,b) a*a + b*b - 2*a*b
20
#define mac2(a,b) a*a*a + b*b*b - 2*a*b
int funcl(int a, int b) return (a*a + b*b - 2 * a*b); }
int func2(int a, int b) return (a*a*a + b*b*b - 2 * a*b);
main() {
int a, b, i, j, fncout, macout;
printf("Please enter two integers\n");
scanf("%d%d", &a, &b);
i = a;
j = b;
fncout = funcl(++i, ++j);
printf("i = %d\tj = %d\n", i, j);
i = a;
j = b;
macout = macl(++i, ++j);
printf("i = %d\tj = %d\n", i, j);
printf("fncoutl = %d\tmacoutl = %d\n\n", fncout, macout);
i = a;
j = b;
fncout = func2(++i, ++j);
printf("i = %d\tj = %d\n", i, j);
i = a;
j = b;
macout = mac2(++i, ++j);
printf("i = %d\tj = %d\n", i, j);
printf("fncout2 = %d\tmacout2 = %d\n", fncout, macout);
Each of the pairs, (macl , funcl) and (mac2, func2), is supposed to implement the same functionality and
give the same output. Ifwe run the code on Visual Studio 2013, the outputs are as follows:
Please enter two integers
5
6
i = 6 j 7
i = 8 j 9
fncoutl 1 macoutl 1
i = 6 j = 7
i = 9 j = 10
fncout2 475 macout2 = 1549
How are these outputs generated? We will manually trace the execution as follows. After the macro
replacement, the two macro calls will be replaced by the following statements, respectively:
macout = macl(++i, ++j); ➔ macout = ++i*++i + ++j*++j - 2*++i*++j;
macout = mac2(++i, ++j); ➔ macout = ++i*++i*++i + ++j*++j*++j - 2*++i*++j;
21
In Visual Studio, the order of execution is to apply to all the unary operations (++) first, in the order of their
appearances, before doing any arithmetic calculations at all.
For funcl and macl, the calculations are done as follows, respectively:
funcl: 6*6 + 7*7 - 2*6*7 36 + 49 - 84 = 1
macl: 8*8 + 9*9 - 2*8*9 = 64 + 81 - 144 = 1
In this example, funcl and macl happen to have generated the same result. This is pure coincidence. For
func2 and mac2, the calculations are done as follows, respectively, which generated different results:
funcl: 6*6*6 + 7*7*7 - 2*6*7 = 216 + 343 - 84 475
macl: 9*9*9 + 10*10*10 - 2*9*10 = 729 + 1000 - 180 = 1549
Now, we run the same code on GNU GCC. The following results are generated:
5
6
i = 6 j 7
i = 8 j 9
fncoutl 1 macoutl -31
i = 6 j = 7
i = 9 j = 10
fncout2 475 macout2 = 788
As can be observed, the function implementations generate the same results as that generated on Visual
Studio. However, the outputs of the macros on GCC are completely different from that on Visual Studio.
The reason is that GCC uses a different order of evaluations. Now we explain how macoutl = -31 and
macout2 = 788 are generated.
GCC calculates the unary operations for the operands of each operator in pair and makes the same variable
in the operation to have the same value.
For macl : macout = ++i *++i + ++j *++j - 2*++i *++j; the macro is evaluated as follows:
macl: 7*7 + 8*8 - (2*8)*9 = 49 + 64 - 144 = -31
where the value of the first pair of variables ++i is 7 and the value of the second pair of variables ++i is 8.
Then, 2 and ++i will form a pair, resulting (2*8), and its result will form a pair with the last ++i, which
obtains a value 9.
For mac2: macout = ++i*++i*++i + ++j*++j*++j - 2*++i*++j; the macro is evaluated as follows:
macl: (7*7)*8 + (8*8)*9 - (2*9)*10 = 392 + 576 - 180 = 788
Notice that the macros generate different values when there exist side effects, for example, when ++i is
used as the input. If no side effects are involved, the macros should generate the same results as their
function implementations, and macros should generate the same results running different execution
environments.
This discussion shows that macros are similar to, and yet different from, procedures and functions, and that
both writing macros (ensuring correctness) and using macros (understanding the possible side effects) can
be difficult and challenging. Can we write and use macros (obtain better efficiency) exactly in the same
way as we write and use procedures and functions (obtain the same simplicity)? Efforts have been made to
do that, and we are making good progress. As mentioned earlier, in Scheme, we can write macros in the
22
same way in which we write procedures. However, we still cannot use macros in exactly the same way we
use procedures. This issue will be discussed in the chapter on Scheme programming (Chapter 4). In C++,
"inline" procedures/functions are allowed. All that is needed is to add a keyword -inline (in C) and inline
(in C++) in front of the definition of a procedure/function. For example:
_inline int sum(int i, int j) {
return i + j;
However, the inline procedure/function is slightly different from a macro. A macro call will always be
replaced by the body of the macro definition in the preprocessing stage. The macro-processor will not check
the syntax and semantics of the definition. On the other hand, for an inline procedure/function call, the
compiler (not the preprocessor) will try to replace the call by its body. There are two possibilities: If the
procedure/function is simple enough, the compiler can do the replacement and can guarantee the correctness
of the replacement; that is, the inlined code must work exactly in the same way as an ordinary
procedure/function call. If the body of the procedure is too complicated (e.g., uses many variables and
complex expressions), the compiler will not perform inlining. The inline procedure in this case will work
like an ordinary procedure.
Java has a similar mechanism called final method. If a method is declared final, it cannot be overridden in
a subclass. This is because a call to a final method may have been replaced by its body during compilation,
and thus late binding cannot associate the call with a method redefined in a subclass.
Postconditions on outputs:
23
(Vxij)(Vxik) ((XijE I/\ XijE I Aj < k) ➔ Xij::; Xik),
The design step translates what needs to be done (functional specification) into how to do it (procedural
steps or algorithm). For example, devising a sorting algorithm to meet the specification belongs to the
design step. An algorithm is usually written in a pseudo language that does not have the mechanical details
of a programming language. A pseudo language focuses on clear and accurate communication with
humans, instead of humans and machines.
The implementation step actualizes or instantiates the design step using a real programming language.
Writing programs in real programming languages is the main topic of this text and will be discussed in
much more detail in the following chapters.
The testing and correctness proof step tries to show that the implementation does the work defined in the
design step or in the specification step. The development process has to return to the implementation or
design steps if the implementation does not meet the requirements of the design or the specification.
The verification and validation step tries to show that the implementation meets the specification or the
user's requirements. The development has to return to design or specification steps if necessary.
In fact, numerous refined phases and iterations within and between these steps can occur during the entire
development cycle.
1.5.2 Program testing
In this and the next subsections, we present more details of the testing and correctness proof step, and
related techniques in the program development process.
A test case is a set of inputs to a program and the expected outputs that the program will produce if the
program is correct and the set of inputs is applied to the program. We also use the input case to refer to the
input part of a test case. Program testing is the process of executing a program by applying predesigned
test cases with the intention of finding programming errors in a given environment. Testing is related to the
environment. For example, assume that your program runs correctly on a GNU GCC CIC++ environment.
If you move your program to a Visual Studio CIC++ environment, you need to retest your program because
the environment has been changed. Debugging is the process of finding the locations and the causes of
errors and fixing them.
If a program has a limited number of possible inputs, we could choose all possible inputs as the test cases
to test the program. This approach is called exhaustive testing. If the outputs are correct for all test cases,
we have proved the program's correctness.
However, in many cases, a program can take an unlimited number of inputs, or the number of test cases is
too big to conduct exhaustive testing. We have two ways to deal with the problem: use incomplete testing
or use a formal method to prove the program's correctness.
If incomplete testing is used, the question is how we should choose (generate) the limited subset of test
cases. Functional testing and structural testing are two major approaches used to generate incomplete test
cases. In functional testing, we try to generate a subset of test cases that can cover (test) all functions or
subfunctions of the program under test. Functional testing is also called black-box testing because we
generate test cases without looking into the structure or source code of the program under test. In structural
testing, we try to generate a subset of test cases that can cover (test) particular structures of the program
under test. For example, we can try to cover all:
• statements in the program,
• branches of the control flow, or
24
• paths from the program's entry point to the program's exit point.
Structural testing is also called glass-box testing or white-box testing because it requires detailed
knowledge of the control structure and the source code of the program under test.
Both functional testing and structural testing can be considered so-called partition testing. Partition testing
tries to divide the input domain ofthe program under test into different groups so that the inputs in the same
group are equivalent in terms oftheir testing capacity. For example, ifwe are conducting functional testing,
we can consider all inputs that will cause the same subfunction to be executed as a group. On the other
hand, if we are conducting structural testing, we can consider all inputs that will cause the same program
path to be executed as a group. Then, we choose:
• one or several test cases from each group of inputs, and
• one or several inputs on the boundaries between the groups
to test the program. For example, if an integer input is partitioned into two groups--negative and
nonnegative-then zero is on the boundary and must be chosen as an input case. Obviously, if the partition
is done properly, partition testing will have a fair coverage of all parts of the program under test.
Program testing is a topic that can take an entire semester to study. We do not attempt to teach the complete
program testing theory and practice in this section. In the rest of the section, we will use a simple example
to illustrate some of the important concepts related to the topic.
Example: The gcd function in the following C program is supposed to find the greatest common divisor of
two nonnegative integers.
#include <stdio.h>
int gcd (int nO, int mO) { // nO >= 0 and mO >= 0 and (nO * 0 or mO * 0)
int n, m;
n = nO;
m = mO;
while (n != 0 && n != m) { // (n * 0) AND (n * m)
if (n < m)
m = m - n;
else
n = n - m;
return m;
main() {
int i, j, k;
scanf("%d\n%d", &i, &j); // input integers i and j from the keyboard
k = gcd(i, j); // call function gcd
printf("%d\n", k); // output the greatest common divisor found
First, we "randomly" pick out the following test cases to test the program.
Input (i, j) Expected Output k Actual
Output k
25
( 6, 9) 3 3
( 10, 5) 5 5
( 0, 4) 4 4
( 5, 7) 1 1
( 8, 29) 1 1
We find that the actual output equals the expected output in all these test cases. Now the question is, is this
program correct?
As we know, testing can never prove the correctness of a program unless all possible test cases have been
used to test the program. However, a set of test cases that can cover different parts of the program can
certainly increase the confidence ofthe correctness ofthe program.
Now we apply structural testing to generate systematically a better set oftest cases. We assume that we aim
at covering all the branches in the gcd function. To make sure we cover all branches, we first draw the
function's flowchart, as shown in Figure 1.10. A flowchart is an abstraction of a program that outlines the
control flows ofthe program in a graphic form. In the flowchart in Figure 1.10, each branch is marked with
a circled number.
no ®
no
no
m = m - n; n = n - m; return m;
® ®
return
26
(0, 0) II This case is not allowed according to the precondition.
(0, 2), (0, 3), (0, 9), (0, 10)
(2, 0), (2 2), (2, 3), (2, 9), (2, 10)
(3, 0), (3, 2), (3, 3), (3, 9), (3, 10)
(9, 0), (9, 2), (9, 3), (9, 9), (9, 10)
(10, 0), (10, 2), (10, 3), (10, 9), (10, 10)
We can apply all these test cases to test the program. We can also reduce the number of test cases by
partitioning the input cases into groups: two input cases belong to the same group if they cover the same
branches in the same order. Table 1.2 lists the groups, a representative from each group, the expected output
of the representative input case, and the branches covered by the groups.
Groups partitioned Representative Expected gcd output Branches covered
(0, 2),(0, 3),(0, 9),(0, 10) (0, 2) 2 CD®®>
(2 2),(3, 3), (9, 9),(10, 10) (2 2) 2 CD®CV®>
(2, 0),(3, 0), (9, 0),(10, 0) (2, 0) 2 CD®®@®
(2, 3),(2, 9), (3, 10), (9, 10) (2, 3) 1 CD®®©®@®®>
(2, 10), (3, 9) (2, 10) 2 CD®®©®CV®>
(3, 2),(9, 2), (10, 3),(10, 9) (3, 2) 1 CD®®@®©@®>
(9, 3), (10, 2) (9, 3) 3 CD®®CV@®®>
Table 1.2. Input case partitions and branch coverage.
If we choose the representative input from each group and the expected output as the test cases, we obtain
a test case set that can cover all the branches in the flowchart. Applying this set of test cases, we will find
that the input case (2, 0) will not be able to produce the expected output. In fact, a dead-looping situation
will occur. Thus, we successfully find that the program is incorrect.
1.5.3 Correctness proof
To prove the correctness of a program, we need to prove that, for all predefined inputs (inputs that meet
the preconditions), the program produces correct outputs (outputs that meet the postconditions).
Program proof consists of two steps: partial correctness and termination. A program is partially correct
if an input that satisfies the preconditions is applied to the program, and if the program terminates, the
output satisfies the postconditions. A program terminates if, for all inputs that meet the preconditions, the
program stops in finite execution steps. A program is totally correct ( total correctness) if the program is
partially correct and the program terminates.
The idea of partial correctness proof is to prove that any input that meets the preconditions at the program
entry point will be processed, step by step through the statements between the entry point and the exit point,
and the postconditions will be satisfied at the exit point. Obviously, if there is no loop in the program, it is
not too hard to do the step-by-step proof. However, if there is a loop in the program, the step-by-step
approach will not work. In this case, we need to use the loop invariant technique to derive the condition to
be proved through the loop. A loop invariant is a condition that is true in each iteration of the loop. To
prove a condition is a loop invariant, we can use mathematical induction: We prove that the condition is
true when the control enters the loop for the first time (iteration 1 ). We assume that the condition is true at
iteration k, and prove that the condition will be true at the iteration k+ 1.
27
Finding the loop invariant that can lead to the postconditions is the most challenging task of the correctness
proof. You must have a deep understanding of the problem in order to define a proper loop invariant.
Proving the termination is easy if we design the loops following these guidelines. The loop variable:
• is an enumerable variable (e.g., an integer);
• has a lower bound (e.g., will be greater than or equal to zero);
• will strictly decrease. Strictly decrease means that the value of the loop variable must be strictly
less than the value of the variable in the previous iteration. The "<" relation is strict, while "s" is
not.
If you do not follow the guidelines, you may have trouble proving the termination of even a simple program.
In an exercise given at the end of the chapter, a very simple example is given where many inputs have been
tried, and the program always stops. However, so far, nobody can prove that the program terminates for all
inputs.
Now we will study a similar example that we used in the last section to illustrate the proof concepts that we
discussed here. The program is given in a pseudo language. Since we do not actually have to execute the
program, we do not have to give the program in a real programming language.
gcd (nO, mO)
II precondition: (n02 0 A m02 0) A (nO* 0 v mO* 0) A (nO, mo are integer)
n = nO;
m = mO;
while n * 0 do
II loop invariant: (n 2A m 2 0) A (n * 0 v m * 0) A
II max{u: uJn and uJm} = max{u: uJnO and uJmO}
if n $; m
then m = m - n
else swap (n, m)
output(m)
II postconditions: m = max{u: uJnO and uJmO}
To prove the partial correctness, we need to prove, for any integer pair (nO, mO) that meets the
preconditions, the loop invariant is true in every iteration of the loop. When the control completes all the
iterations of the loop and reaches the exit point of the program, the postconditions will be true. As discussed,
finding the loop invariant is the most difficult part. Now we are given the condition that should be a loop
invariant, and we only need to prove that the condition is indeed a loop invariant. The given condition is:
(n 2A m 2 0) A (n * 0 v m * 0) A
max{u: uln and ulm} = max{u: ulnO and uJmO}
We need to prove it is a loop invariant. We can use mathematical induction to prove it in the following
steps:
( 1) Prove the condition is true at the iteration 1: it is obvious.
(2) Assume the condition is true at iteration k.
(3) Prove the condition is true at iteration k+ 1.
Since the conversion made in each iteration is:
28
gcd(n, m) ⇒ gcd(n, m - n), or
gcd(n, m) ⇒ gcd(m, n)
According to mathematical facts:
gcd(n, m) gcd(n, m - n), and
gcd(n, m) = gcd(m, n)
Thus, from iteration k to iteration k+ 1, the condition will remain to be true. Therefore, we have proved the
condition is a loop invariant.
We then need to prove that the loop invariant leads to the postconditions. It can be easily seen from the
program that if the loop invariant is true when the control leaves the loop, the postconditions will indeed
be true. Thus, we have proved the partial correctness of the program.
To prove that the program terminates, we can use the following facts:
(1) The loop variable is (n, m). The loop variable is enumerable.
(2) If we consider the dictionary order, that is:
(n, m) > (n, m - n), if n :2: m // e.g. (3, 6) > (3, 3) in dictionary order
(n, m) > (m, n), if n > m. // (6, 3) > (3, 6) in dictionary order
we can see that there is a strictly decreasing order on the loop variable (n, m) based on the dictionary
order.
(3) There is a lower bound on the value that (n, m) can take, that is ( O , O) •
Since the loop variable (n, m) is enumerable, it decreases strictly, and there is a lower bound ( o, o);
the loop must terminate.
In an exercise given at the end of the chapter, a variation of the gcd program is suggested. Try to prove its
partial correctness and its termination.
1.6 Summary
In this chapter, we introduced in Section 1.1 the concepts of the four major programming paradigms:
imperative, object-oriented, functional, and logic. We looked at the impact of language features on the
performance of the programs written in the language. We briefly discussed the development of languages
and the relationships among different languages. We then discussed in Section 1.2 the structures of
programs at four levels: lexical, syntactic, contextual, and semantic. We illustrated our discussion on the
lexical and syntactic levels by introducing the BNF notation and syntax graph. We used BNF notation to
define the lexical and syntactic structures of a simple programming language. In Section 1.3, we studied
the important concepts in programming languages, including data types, type checking, type equivalence,
and type conversion. Orthogonality was used to examine the regularity and simplicity of programming
languages. Finally, in Section 1.4, we briefly discussed program processing via compilation, interpretation,
and a combination of the two techniques. The emphasis was on the macro and inline procedures/functions
in CIC++. We studied how to define and use macros, and what their strengths and weaknesses are when
compared to ordinary procedures/functions. Section 1.5 outlined the program development process and
discussed programming testing and proof techniques through examples.
29
1.7 Homework and programming exercises
1. Multiple Choice. Choose only one answer for each question. Choose the best answer if more than
one answer is acceptable.
1.1 Stored Program Concept (von Neumann machine) is one of the most fundamental concepts in
computer science. What programming paradigm most closely follows this concept?
□ imperative □ object-oriented □ functional □ logic
1.2 What computing paradigm can solve a problem by describing the requirements, without writing code
in a step-wise fashion to solve the problem.
□ imperative □ functional □ object-oriented □ logic
1.3 What computing paradigm enforces stateless (no variable allowed) programming?
□ imperative □ object-oriented □ functional □ service-oriented
1.4 What is a feature of object-oriented computing?
□ stateless □ state encapsulation □ platform-independent □ side-effect free
1.5 In contrast to Web 1.0, what is the key function of Web 2.0?
□ Web is the computing platform □ Web supports graphic display
□ Web supports semantic analysis □ Web is accessed over HTTP protocol
1.6 Because of hardware constraints, early programming languages emphasized
□ efficiency □ orthogonality □ reliability □ readability
1.7 What factor is generally considered more important in modern programming language design?
□ readability □ writability □ efficiency □ None
1.8 The main idea of structured programming is to
□ reduce the types of control structures. □ increase the types of control structures.
□ make programs execute faster. □ use BNF to define the syntactic structure.
1.9 Implicit type conversion is commonly refer to as:
□ typing coercion casting paradigm
1.10 In the following pseudo code, which programming language allows the mixed use of data types?
int i = 1; char c = 'a'; II declaration and initialization
c = c + i; II execution of an assignment statement
□ Ada DC □ Java □ All of them
1.11 In the layers of programming language structure, which layer performs type checking?
□ lexical □ syntactic □ contextual □ semantic
1.12 How many different identifiers can the following BNF ruleset generate?
<char> : := a I b I c I • • • I x I y I z
<identifier> <char> I <char> <identifier>
□ 0 □ 1 □ 26 □ more than 26
31
1.13 Which of the following statement is correct if a language is strongly typed. Select all that apply.
□ Each variable in a program has a single type associated with it.
□ Variable type can be unknown at compilation time.
□ Type errors are always reported.
□ Coercion is automatically allowed.
1.14 Which command (construct) has a loop when expressed in syntax graphs?
□if-else □ switch for □ □ while
1.15 The contextual structure of a programming language defines
□ how to form lexical units from characters.
□ how to put lexical units together to form statements.
□ the static semantics that will be checked by the compiler.
□ the dynamic semantics of programs under execution.
1.16 If a program contains an error that divides a number by zero at the execution time. This error is a
□ lexical error □ syntactic error □ contextual error □ semantic error
1.17 Interpretation is not efficient if
□ the source program is small.
□ the source program is written in an assembly language.
□ the difference between source and destination is small.
□ multi-module programming is used.
1.18 What is the difference between an inline function and a macro in C++?
□ There is no difference between them.
□ The inline functions are for Java only. There are no inline functions in C++.
□ Inlining is a suggestion to the compiler, while a macro definition will be enforced.
□ A macro definition is a suggestion to the compiler, while inlining will be enforced.
1.19 Macros-Processing takes place during which phase?
□ Editing □ Preprocessing □ Compilation □ Execution
1.20 Assume a function requires 20 lines of machine code and will be called 10 times in the main program.
You can choose to implement it using a function definition or a macro definition. Compared with the
function definition, macro definition will lead the compiler to generate, for the entire program,
□ a longer machine code but with shorter execution time.
□ shorter machine code but with longer execution time.
□ the same length of machine code, with shoter execution time.
r
32
3. Use the library and Internet resources to compile a table of programming languages. The table must
include all programming languages mentioned in Section 1.1.3 on the development ofprogramming
languages. The table should contain the following columns:
• name ofthe programming language,
• year the language was first announced,
• authors/inventors ofthe language,
• predecessor languages (e.g., C++ and Smalltalk are predecessors ofJava),
• programming paradigms (e.g., Java belongs to imperative and object-oriented programming
paradigms).
4. What is strong type checking, and what are its advantages? List a few languages that use nearly
strong type checking in their program compilations.
5. What is weak type checking, and what are its advantages? List a few languages that use weak type
checking in their program compilations.
6. What is orthogonality? What are the differences between compositional, sort, and number
orthogonality?
7. Compare and contrast a macro and an inline function in C++. Which one is more efficient in
execution time? Which one is easier for programmers to write?
8. Compare and contrast the C++ inline function and Java's final method.
9. Which type equivalence leads to strong type checking, structural equivalence, or name equivalence?
Explain your answer.
10. Use BNF notation to define a small programming language that includes the definition of variable,
math-operator, math-expression, condition-operator, condition-expression, assignment statement,
loop statement, switch statement, and a block of statements. A variable must start and end with a
letter (an unusual requirement). A single letter is a valid variable. The definition of the expression
must be able to handle nested expressions like 2 * ( x + y) and 5 * ( ( u + v) * ( x - y) ) . The
language must include the following statements:
Assignment: It assigns (the value of) a variable or an expression to a variable. For example, x
2*(y + z).
Conditional: if-then and if-then-else. The condition in the statement can be simply defined
as an expression.
33
Block: One statement is a block. Zero or multiple statements in curly braces and separated by";"is
also a block, according to number orthogonality. For example, i=i + 2; is a block. { i= i+2; for
( k= O; k<i; k= k+l) { i= i + 1; s = 2 * i} } is also a block.
11. The following syntax graph defines the identifiers of a programming language, where alpha is the
set of characters"a"through"z"and"A"through"Z,"digit is the set of characters"O"through"9,"
and underscore is the character" ."
alpha
digit
� underscore �
11.1 Which of the following strings can be accepted by the syntax graph (choose all correct answers)?
□ -FooBar25 □ 2-SfooBar- □ Foo&Bar □ 12.5 □ Foo2bar
11.2 Give the syntax graph that defines the identifiers always starting with a letter from the set alpha.
11.3 Give the BNP definition equivalent to the syntax graph of the question above.
main()
int a, b;
scanf("%d %d", &a, &b);
if (b < min)
printf("input out of range\n");
else
a = minl(a, b++);
printf("a = %d, b %d\n", a, b);
a = rnin2(a, b++);
printf("a = %d, b %d\n", a, b);
12.1 Give the exact C code of the statement"a min 1 (a, b++) ; "after macro processing.
12.2 Give the exact C code of the statement "a min2 (a, b++) ; "after macro processing.
12.3 Give the exact C code of the statement "if (b < min) "after macro processing.
12.4 Assume 60 and -30 are entered as inputs. What is the exact output of the program?
34
12.5 Assume 50 and 30 are entered as inputs. What is the exact output of the program?
13. Assume a programming language has two sets of features. S1 is the set of three different kinds of
declarations: (1) plain, (2) initializing, and (3) constant. That is,
(1) typex i; (2) typex i = 5; (3) const typex i = S;S2 is the set of data types: (a) bool,
(b) int, (c) float, (d) array, (e) char.
13 .1 If the language guarantees sort orthogonality 1 in Figure 1.5, and we know that int i is allowed
(the combination plain-int), list the allowed combinations of the features of the two sets that can be
implied by the sort orthogonality.
13.2 If the language guarantees sort orthogonality in Figure 1.6, and we know that const array a is
allowed (the combination constant-array), list the allowed combinations of the features of the two
sets that can be implied by the sort orthogonality.
13.3 If the language guarantees compositional orthogonality, list the combinations of the two sets of
features allowed. Write a simple C program that exercises a11 the declarations in this question. Each
declared variable must be used at least once in the program. The purpose of the program is to test
whether C supports compositional orthogonality. The program thus does not have to be semantically
meaningful. Test the program on Visual Studio or GNU GCC, and submit a syntax error-free
program. Note: a variable can be declared only once. You must use different variable names in the
declarations.
14.l Enter the program in a development environment (e.g., Visual C++ or GNU GCC). Save the file as
assignl .c. If you are not familiar with any programming environment, please read Section B.2 of
AppendixB.
14.2 Compile and execute the program.
35
14.3 Read Chapter 2, Section 2.1. Modify the given program. Change <stdio.h> to <iostream>.
Change print£ to cout and change scan£ to cin, etc. Save the file as assignl.cpp
14.4 Com pile and execute the program.
15. Macros are available in most high-level programming languages. The body of a macro is simply used
to replace a macro call during the preprocessing stage in compilation. A macro introduces an "inline"
function that is normally more efficient than an "out-line" function. However, macros suffer from
side effects, unwanted or unexpected modifi cations of variables. Macros should be used cautiously.
The main purpose of the following program is to demonstrate the differences between a function and
a macro. Other purposes include learning different ways of writing comments, formatted input and
output, variable declaration and initialization, unary operation ++, macro definition/call, function
definition/call, if-then-else and loop structures, etc. Study the following program carefully and
answer the following questions.
/* The purpose of this program is to compare and contrast a function
to a macro. It shows the side effects of a macro and an incorrect
definition of a macro. The macros/functions absl(x), abs2(x), and
abs3(x) are supposed to return the absolute value of x. */
#define absl(a) ((a<0) ? -(a) : (a)) // macro definition
#define abs2(a) ((a<0) ? -a : a) // macro definition
#include <stdio.h>
int abs3(int a) { // function definition
return ((a<0) ? -(a) (a)); II--> if(a < 0) return -a else return
a;
main()
int il = 0, i2 = 0, i3 = 0, jl = 0, j2 0, j3 0;
printf("Please enter 3 integers\n");
scanf("%d %d %d", &il, &i2, &i3);
while (il != 123) { // 123 is used as sentinel
jl absl(++il 2); // call a macro
j2 = abs2(++i2 - 2); II call a macro
j3 = abs3(++i3 - 2); II call a function
printf("jl = %ct, j2 = %d, j3 = %d\n", jl, j2, j3);
printf("Please enter 3 integers\n");
scanf("%d %d %d", &il, &i2, &i3);
15.1 Desk check (manually trace) the program. What would be the outputs of the program when the
following sets of inputs are applied to the program?
il, i2, i3 9, 9, 9 jl, j2, j3 =
il, i2, i3 -5, -5, -5 jl, j2, j3 =
il, i2, i3 0, 0, 0 jl, j2, j3 =
15.2 Enter the program into a programming environment and execute the program using the inputs given
in the previous question. What are the outputs of the program?
36
Exploring the Variety of Random
Documents with Different Content
which we had on the subject: “I did not know when I left the Hotel de Ville
after the proclamation of the new government, whether I should not find the
police waiting to arrest me when I reached my home,” was what he said.
Had the Empress personally gone to the Corps Législatif and given
orders to sweep away the mob about to invade it, and to arrest Trochu, it is
probable that the Parisians, cowed by her personal courage, would have
acclaimed her, and cried out: “Vive l’Impératrice!” It is certain that no one
would have harmed her, but Eugénie lost her presence of mind upon finding
herself so utterly abandoned, and fled from the Tuileries, forgetting
everything in the disorder of that moment.
Vague news concerning the disaster of Sedan had reached Paris in the
course of the evening of the 2nd of September, rumours with no official
authority to explain them, but which, nevertheless, circulated everywhere.
Later on the Empress was reproached for not acting at once upon them by
rallying around her the few partisans that were still left to the Empire. But
she was not to blame for this apparent inactivity, because it was only the
next day that she received the telegram from the Emperor confirming the
dreadful news.
Among the diplomatic corps it had been known earlier, and commented
upon as it deserved. In the late afternoon of the 3rd of September, I went
out, and directed my steps towards the Tuileries. The palace seemed quite
peaceful. The usual sentinels that were guarding it were all at their posts,
and a crowd on the Place de la Concorde was neither numerous nor hostile,
certainly nothing that pointed to insurrection.
Among the curious people that were standing in front of the palace I
could hear remarks and comments on the catastrophe of the day before, but
what struck me was that these remarks were not hostile to the Empire; on
the contrary, words of regret were continually expressed, and many
sympathised with the Emperor, and especially the Prince Imperial. After
having waited for some time I turned my steps towards the Cercle de la Rue
Royale, where, meeting some friends, I told them that I was surprised to
find the capital so quiet, and that I thought that the Empress would be well
advised if she took advantage of this sympathetic attitude of the public, to
attempt to negotiate a peace. Every well-wisher of France felt that peace
was indispensable in order to avoid worse calamities. I was very much
surprised when a man whom I knew to be well informed as a rule, replied
that very probably the next day would see a proposition promulgated to
depose the Emperor. He added the remarkable news—which surely was
absurd—that this would be done at the secret instigation of the Regent, who
believed the Prince Imperial’s only chance of ascending the throne
consisted in the removal of his father from the political scene.
I could not bring myself to believe such an unfair canard. Whatever has
been said to the contrary since, Napoleon was always popular with a large
section of people; the Parisian workmen especially liked him, and felt
grateful for the care with which he had seen to their welfare. It is true there
were some who screamed that he was responsible for the military disasters
which had overtaken the country, but these belonged to that section of
unruly spirits that take every possible opportunity to attack every
government. It must not be forgotten that in spite of the Lanterne and other
revolutionary organs of the same kind, the influence wielded by the press
had not reached the power it now possesses; after eighteen years of
Imperialistic rule, the country was disciplined and trained to obedience, and
it is most probable that had the Emperor personally been able to make an
appeal to it, it would have responded heartily. If the Regent could have
obtained the liberation of her husband, and so secured his help to conclude
peace with Prussia, such an ending to the campaign might have been
possible at that particular moment—it was certainly not the time to talk of
the sovereignty of the people and of bowing to the will of the country.
The evening passed off quietly. I walked along the boulevards after
eleven o’clock; the night was beautiful, and the streets as animated as usual.
I could not discern much consternation among the crowds, everyone
seemed only to be more subdued than had been the case lately. And when I
left my house on the morning of the 4th there were certainly no signs
whatever of a revolution in the streets, nor any atmosphere of impending
disaster.
I was living in the Avenue de l’Impératrice, now Avenue du Bois de
Boulogne, and as I reached the Champs Elysées, I found that everything
was as quiet as usual. The fountains were playing in front of the Palais de
l’Industrie, children were romping in the walks, and there was no indication
that anything unusual was going on. I went to breakfast at the Cercle, and it
was only after leaving that I was accosted by a friend on the Place de la
Concorde who told me that the Corps Législatif had been invaded by the
mob. Curious as I am by nature, I turned my steps towards the Palais
Bourbon, and found really an enormous crowd assembled there; but even
then, there was nothing hostile in its attitude, it was rather good-humoured
than anything else. Some leaders, however, were shouting: “La déchéance!
La déchéance,” at the top of their voices. No one seemed to offer any
resistance, and the attitude of the deputies, when I managed to enter the
gallery reserved to the Corps Diplomatique in order to obtain a view of
what was going on inside the House, was rather one of surprise than
anything else. Amidst the hum of voices could be heard the deep tones of
M. Jules Ferry urging those present to go to the Hotel de Ville and to
proclaim the Republic, but with the exception of Jules Favre, and of M. de
Kératry, no one seemed to share his opinion. I am convinced that if, at that
moment, the Regent had occupied the Palais Bourbon with a military force,
the Revolution would never have succeeded, and to this day I fail to
understand how it was that no member of the government had the presence
of mind to take upon himself the responsibility for such a measure, which
might have changed the whole history of France. It is quite certain that even
when the three leaders of the Revolutionary movement started for the Hotel
de Ville, they did not possess the sympathy of many of their colleagues,
rather, the latter only wanted the support of the government then in power,
to get rid of them. None would have objected to the arrest of these three
men, had there been found but one person strong enough to put such a
measure into execution.
The fact is, the majority of the members of the Corps Législatif seemed
to be quite dazed by what was happening; they did not at all understand
what was going on. I am convinced that they left the hall where the sitting
had taken place, without having realised that it was for the last time. As
soon, however, as they had done so, the mob invaded the Palais; but the
scenes of disorder that are asserted to have followed, never took place. I
remained some time unobserved at my post, and failed to see the excesses
of which some speak as occurring. Of course, shouts were heard, a boy of
about eighteen years old sat down in the Presidential armchair, and rang the
bell with all his might, but this was done more in childish amusement than
anything else. I repeat that the slightest appearance of a military force
would have restored order at once, and this makes the subsequent events
more unpardonable still.
After having spent about an hour watching the scenes that attended the
end of the Legislature which, under Napoleon III., had ruled France for
eighteen years, I left the Palais Bourbon and turned my steps towards the
Tuileries. There the crowd was more hostile, especially the Garde
Nationale. The men had turned their rifles upside down, and some of them
were screaming aloud they would never fire against “la nation.” Now and
then a cry resounded: “La déchéance! La déchéance,” and the accents of the
Marseillaise made themselves heard; but it must be remarked that no cries
of “Vive la République!” were to be noticed, at least I did not hear any.
Another strange feature of this pacific revolution was that the mutineers
were in small bands, which were each followed by a considerable crowd of
onlookers, which probably would have dispersed at sight of the first
company of soldiers. The police had mysteriously vanished, and the whole
aspect of the crowd was good-natured in the extreme; it was composed of as
many women, children and dogs as of insurgés, and seemed more on
amusement bent than on anything else. Even when the gates of the Tuileries
were at last forced, and the mob found itself in the big courtyard, it did not
attempt to enter the interior of the Palace; the people merely walked about
the garden and the inner courtyard that led from the Carrousel to the private
gardens. Had the Empress remained she would not even have noticed the
invasion, and the best proof of what I say here lies in the fact that when the
members of the new government arrived a few hours later in the Tuileries,
they found everything in the same state as usual; nothing had been
disturbed, and even the papers forgotten by the Empress on her writing
table had been left untouched, the servants were all there, but had only
taken care to take off their liveries, with the alacrity which people of their
class always display in turning against their former masters as soon as
misfortune comes in any shape or form.
I was one of the persons who visited the Tuileries on the evening of that
memorable 4th of September, which saw the fall of Napoleon III.’s dynasty.
No one knew at that moment what had happened to the Empress, nor where
she had fled, and rumours were going about in some quarters that she had
tried to join the Emperor, and in others that she had directed her steps
towards Metz with the intention of seeking a refuge with the army of
Bazaine, and establishing there the seat of government.
When I visited the Palace I found that no one there believed she had
gone away for ever; indeed—and this is a detail that I believe has never
been recorded elsewhere—I found one of her maids preparing her bed just
as usual! It was evident the flight had been a hurried one. In the private
rooms, letters never meant to be seen by a stranger’s eye were scattered
about; a gold locket with the portrait of a lovely woman, the Duchesse
d’Albe; another one with that of a baby in long robes, the first picture of the
Prince Imperial; one small golden crucifix; a note just begun, and addressed
no one knows now to whom, but of which the first words ran thus: “Dans la
terrible position où je me trouve, je ne——” The writing stopped there;
evidently she who had started it had been interrupted by the bearer of some
evil message, and there it lay forgotten, in the midst of the tragedy which
had put an end to so many things and to so many hopes.
The Revolution of the 4th of September was especially remarkable for
the inconsiderable impression it produced in Paris itself. Life went on just
as usual, and save for a few expressions of wonder, no one seemed quite to
realise the importance of it. The capital began to prepare for the siege,
rather with mirth than anything else. To tell the truth no one seemed to
believe in its possibility, and I remember one day, when visiting a friend
who was living on the Quai Malaquais, she pointed to the Seine flowing
softly under her windows, saying at the same time: “Croyez-vous que les
Prussiens arriveront devant mes fenêtres comme les Normands jadis sont
entrés à Paris?” (“Do you think that the Prussians will arrive in front of my
windows as the Normans entered Paris in days of yore?”)
I reproduce this remark just to show how very little those in the capital
realised either the present or the future at this particular moment.
Another thing which struck me, was that existence out of doors seemed
to go on much as usual, in spite of the bad news that continued to pour in.
The theatres were full, and people seemed to make the most of the late
summer days that were coming to a close. There was very little excitement,
and the feeling that predominated was one of curiosity. Some people were
departing, but not in large numbers, and it was only towards the end of
September that people began seriously to look at the situation. By that time
I had already left Paris. I went on the 15th of September, hoping to return in
January, not suspecting then that the war would drag on as it did. I, together
with many reasonable people, still hoped that the new government would
see the necessity of ending a hopeless struggle before it was too late.
All my suppositions turned out to be wrong, however, and it was only
towards the end of February that I was once more to find myself at my old
post, by which time the unfortunate Emperor, languishing in captivity,
seemed to be forgotten, and the Republic had grown to be an established
fact.
CHAPTER VII
Paris was already invested when I succeeded in leaving it with the help of a
diplomatic passport, and it was in Vienna that I read in the papers the news
of the useless interview that took place between Prince, at that time still
Count, Bismarck, with M. Jules Favre at Férrières. I never understood how
the German Chancellor, who at that time had not the slightest intention to
conclude peace, consented to receive the representative of a government
which he had not acknowledged. I was told later on, that it was at the
request of the King of Prussia he had given his assent to Favre’s arrival at
the German headquarters.
The results of this hopeless attempt are well known. Jules Favre talked
as only an advocate can talk. But he pleaded sentimental reasons where
hard facts only had to be considered. When he returned to Paris, it was with
the conviction that as the government of the Défense Nationale was neither
strong enough nor respected enough to compel the country to accept a
shameful peace, the only thing was to allow matters to drift.
A good many of my friends, and of my colleagues, had elected to remain
in the capital, and there await the end of the war, and I must own that I
regretted later on that I had not been given the same opportunity. That
period was most interesting, and I have always felt that to understand the
genesis of the events which happened later on, one ought to have
experienced those months of anxiety, when the great capital was abandoned
to her fate, with the Prussian guns levelled against her.
I was not, however, left entirely without news, and as regularly as was
possible received letters from besieged Paris, sent either by balloon or by
carrier pigeons. I have kept them all, and from their pages now give extracts
which will give an idea of the feelings of the Parisians during the trial they
had to undergo.
September 25th, 1870.
“My very dear Friend,—You will be wondering what is happening to us, and I do not want to let pass the present
opportunity to send you some news concerning us. We are now quite resigned to the prospect of a siege, and the only
question that is agitating the public mind is how long it will last. The most contradictory rumours are spread, and some of
them even attribute to Jules Favre the intention of trying to restore the Empire, after having assured himself that he would
remain its Prime Minister. Of course this is nothing but humbug, and I only mention it to you to show you to what extent
public imagination can cajole itself. What is not humbug, however, is the difficulty the government finds in attempting
anything in the way of peace negotiations. It begins to see the great mistake which was made when a small minority
overthrew the Empire so unexpectedly. Had it been left standing, all the onus of the disastrous peace, which, whether
France likes it or not, will have to be concluded, would have fallen upon its shoulders, whilst at the present moment, it is
the Défense Nationale which will bear the brunt of anger at the dismemberment of our France. This may sound the death
knell of the Republic, and those who are at its head know it but too well. I think that the unlucky phrase of Jules Favre,
when he said that he would never give up ‘un pouce de notre territoire, ni une pierre de nos forteresses,’ was more a
calculated pronouncement than the result of an enthusiasm too strong to think of the consequences its imprudent words
might have. He wanted to ward off the evil moment when he would be called upon to do that which the Empire he had
helped to overthrow would have done had it been left in power; and feeling this to be inevitable, had tried to keep the
knowledge of this bitter fact from the public. One begins to realise the mistake one has made, I repeat it, but
unfortunately one does not see what ought to be done to mend it. The public feeling in the city is very different from that
which was prevailing on the 4th of this month. The Parisians begin to realise the seriousness of the situation, but there is
no talk of a surrender, and the confidence that victory will return to France is very dominant among the lower classes,
whilst it is recognised among the higher ones that the deal has been irrevocably lost, and that peace ought to be
concluded, else serious disturbances may occur among the Garde Nationale and the numerous militia.
“The government does nothing, and when I have said this, I say everything. They say that they can do nothing and
that it is to the Tours delegation they must look for an attempt to stop the progress of the Prussian army. So long as
Gambetta was here there was some activity in ministerial offices; now he has gone there is absolute stagnation. All these
ministers, suddenly called upon to exercise functions for which they were totally unprepared, seem lost, and Jules Favre
looks at the political situation with the same eye he would look at some big criminal or civil law case—from the outlook
of an advocate, not from that of a statesman. They say he actually cried during his conversation with Bismarck. The
question arises whether these tears were genuine ones of grief, or simply a rhetorical incident. How much more dignity
there was in the conduct of General Wimpffen and his colleagues, when they discussed with the German Minister and the
German General Staff the conditions of the capitulation of Sedan! No one likes Jules Favre, whom even his partisans
consider to be a demagogue of talent, but nothing more. And certainly France does not need demagogues at the present
time.
“There are comical notes in the gravity of the situation. People talk about never surrendering, about dying for their
country, whilst running about buying hams and butter, and as many provisions as they can, in view of the siege.
Vegetables are at a premium, meat will soon become a luxury, bread is already looked upon in the same light that cakes
were formerly, and frivolous women are getting excited at the thought of the many privations which they expect they will
be called upon to endure. Yet comparatively few people have left the capital, where, after all, perhaps, one is safer than in
the provinces. News leaks out sometimes from the outside, mostly false; for instance, it was related the other day, that the
Prince Imperial had reached Metz, and put himself under the protection of Marshal Bazaine. All the partisans of the
Empire believed it, but serious people did not attach any faith to this rumour. The Legitimists are full of hope that out of
the present complications a monarchical restoration may ensue; the Radicals, on their part, are sure that, sooner or later,
the government will fall into their hands. The principal question that is agitating the public mind, is as to who would
eventually have the right to conclude peace with Prussia. No one, to begin with the members of the present administration
(for one can hardly call it a government), believes that the King of Prussia would consent to treat with them. Therefore
the calling together of a National Assembly is imperative, but would this Assembly be the expression of the will of the
nation, when the elections would have to be held under the muzzles of the enemy’s guns? In a word, we live in a state of
uncertainty such as France has never yet experienced, no one knows what the morrow holds in reserve, and though there
is a government of the National Defence, yet there is no one to defend the country.”
The Commune
M. Thiers
I had had many opportunities of meeting M. Thiers during the last years of
the Empire. I had known him even before I came to Paris in an official
capacity, had often seen him at the houses of some mutual friends, and we
came to know each other very well. He was one of the cleverest, nicest little
men in the world, and even among the many interesting people who
abounded in France at that time, he stood out conspicuously as one of the
pleasantest. He had many enemies, which is not to be wondered at if one
takes into consideration the vivacity which he always displayed in his likes
and dislikes, and the bitterness, or rather the caustic tendencies, of his
tongue. But friends and foes alike were loud in their praise of his
intelligence, and especially of his wit. I am not talking of his moral
character, which was discussed in many ways and which in part justified the
attacks that were levelled against it. The Legitimists could not forgive him
the part he had taken in the arrest of the Duchesse de Berry, nor the attitude
of the ministry of which he was a member with regard to that unfortunate
Princess whose frailties were so mercilessly displayed before the public
before the end of her captivity in the fortress of Blaye. The Orleanists also
did not care for him, in spite of the pledge which he had given to their
party; but Louis Philippe personally was fond of him, perhaps because their
tastes were very much alike, and because the sternness and austerity of
Guizot, his great opponent, had never appealed to the heart of the King,
who stood rather in awe of that imposing figure in modern French political
life. The bonhomie of Thiers, his easygoing manners, were more in
accordance with the homely attitude which at that time distinguished the
Orleans family circle. As Montalembert once said very wittily: “Thiers,
c’est le ministre bourgeois d’une dynastie bourgeoise.”
And the remark contained a great deal of truth, though it is much to be
doubted whether the brilliant Catholic leader appreciated at their real worth
the sterling qualities which M. Thiers was hiding under the sometimes
frivolous manner in which he treated serious subjects.
As a writer he was one of the greatest of his epoch, and his work on the
Consulate and the First Empire will always rank among the classics. Few
people have understood so well as he did the gigantic figure of the first
Napoleon, and certainly his knowledge of history, the wonderful way in
which he remembered its lessons, and knew how to apply them where it
became necessary, constituted a unique thing even in France, where at that
time there was a superabundance of clever writers and great thinkers, of
whom he was one of the foremost.
Some enemies of M. Thiers assured me that he would have done better
to confine himself to his historical studies, and that it was a mistake on his
part to throw himself into the struggles of a political career. I do not share
this opinion personally, because the very nature of Thiers would have
protested against a life spent only in thinking without the emulation of
doing. He was essentially a great patriot, far greater than the general public
supposed, and if he had personal ambitions, which cannot be denied, it must
also be admitted that in the great moments of crisis through which his
country passed during his lifetime, he never hesitated to put all his strength,
all his experience, and all his knowledge of public affairs, as well as his
influence at home and abroad, at her service, sparing neither time nor
trouble, nor energy, in his endeavours to help her.
During the whole reign of Louis Philippe, M. Thiers was a conspicuous
figure in Paris society, and, strange to relate, this petit bourgeois had
succeeded in entering the most exclusive circles of the Faubourg St.
Germain, and contrived to install himself in the favours of its leaders,
masculine as well as feminine. He was essentially the type of a middle-class
man, in spite of the high offices which he had held, and never could rid
himself of the habit of tying a napkin round his neck at meals, when he was
in his family circle, neither would he go out without the umbrella that
remained the distinctive sign of that epoch still known as the “époque de
Louis Philippe,” where the bourgeoisie reigned supreme, and where the
Sovereign tried by all means to win for himself the sympathies of the mob
by coming down to its level.
M. Thiers did not care for the mob. He was of an autocratic character,
and of an imperious disposition, admitting no sovereignty apart from his
own. But, nevertheless, he remained the child of his generation and of his
class. He rose, but neither by adapting himself to circumstances, nor to the
conditions of existence around him. Original he was in mind, in intelligence
and in manners, and he did not change; he always appeared to his friends as
a man of happy disposition tempered with affability, and tinged with
familiarity; his distinctive characteristic from the very first days he entered
public life.
Thiers was essentially “un homme d’opposition,” as one of his enemies
once remarked, but he was a statesman of a type such as is no longer found
nowadays; an active, busy, little individual, always on the look out for his
adversaries’ mistakes, and terrible in the merciless way in which he noticed
them—and, what is worse, made others notice them. He had but little pity
in his heart for the errors of mankind, but was wise enough not to show the
disdain in which he held it. He had been at a good school, had frequented
the salon of Talleyrand, and studied politics by contact with the politicians
who had ranked among the foremost in Europe. He used to relate a funny
little anecdote from his early days, when he had been introduced to Prince
Metternich, during one of his journeys to Vienna, whither he had repaired to
study certain episodes of the history of Napoleon, and examine certain
documents deposited in the Imperial Archives of the Burg. The statesman to
whose intrigues the great Emperor had in part been indebted for his fall
received Thiers in his study, and it seems received him very badly. But the
little Frenchman, far from appearing to notice it, began at once to talk with
the Austrian Chancellor as if he had known him for years, and did not
scruple to question him on the subjects about which he desired to learn, a
thing which Metternich, who liked above all things to hear himself speak,
particularly disliked. Surprised at first, then slightly bored, the Prince told
Thiers that he had better question the Director of the Archives about the
various points he desired to clear up, to which the historian of the Consulate
and the Empire replied quite brusquely that this personage could not tell
him anything worth listening to, and that he never took lessons in history
from those who had only read it. Metternich, more and more astonished,
asked him what he meant. “Oh, nothing very important,” was the answer;
“seulement je crois que personne ne pourrait mieux me renseigner sur
Napoleon que vous qui êtes parvenu à le tromper si complètement et si
souvent” (“I merely think no one should be better able to give me
information about Napoleon than yourself, who succeeded in deceiving him
so completely and so frequently”). When Thiers told this anecdote he never
failed to add that “Metternich ne trouva rien d’autre à me répondre que de
sourire avec la remarque: ‘Vous connaissez bien votre histoire, jeune
homme’ ” (“Metternich in reply could do nothing but smile, accompanying
it with the remark: ‘You are well up in your history, young man’ ”).
Impudence, as one can see from the above, was not wanting in the
character of the future President of the French Republic, and this
impudence never deserted him in later years. It has been said that his vanity
was intense, and that there was some truth in this accusation cannot be
denied; but beneath this vanity there lay the latent consciousness the man
had of his own moral and intellectual worth, and of the immense distance
that existed between him and the other men of his generation. He tried to
impose his ideas on others; he was despotic in his decisions, his judgments
and his opinions, but he was not devoid of impartiality, and he was very
well aware of his own faults. He loved France with a sincere affection,
which saw through her faults, and there was no chauvinism in his feelings.
He would have liked to see his fatherland prosperous and powerful, but he
never rushed into extremes as Frenchmen are so often inclined. Whilst he
was the responsible minister of the dynasty of July, he served it faithfully
and to the best of his ability, and though he has been often accused of
opportunism, yet he never would accept office under the Bonapartes,
though, and this is rather curious, he always was of opinion that their
dynasty was the most popular one among all those that aspired to the
government of France.
When, together with the other members of the Legislative Chamber, he
was imprisoned by the President on the day of the coup d’état of the 2nd of
December, he is said to have made the following typical remark: “Le
Président nous fait enfermer, c’est son droit; espérons pour lui, qu’il saura
en profiter, et ne donnera pas dans le travers de vouloir gouverner
constitutionnellement. Il ne peut pas avoir de Constitution pour les
Bonaparte, tout au plus peuvent ils prétendre à ce que leur règne soit celui
où on parle de Constitution comme les malades parlent des mêts que leurs
médecins leur interdisent de manger” (“The President is having us shut up,
it is his right; let us hope for his own sake that he will know how to profit
by it, and will not make the mistake of wanting to govern constitutionally.
There can be no constitutional government for the Bonapartes. The utmost
they can lay claim to is that during their reign the Constitution should be
spoken of in the tone in which invalids speak of dishes that their doctors
forbid them to eat”).
During the eighteen years that the Empire lasted, Thiers always refused
to take office, though he owned later on that he felt once or twice sorely
tempted to do so. But he realised that the regime could not last, and
reserved himself for the moment when it would be overturned, feeling
convinced in his mind that that day would be also that of his own personal
triumph, and that whether the country liked it or not it would be compelled
to turn to him for advice and for help.
When after the first defeats which characterised the war of 1870, the
Empress Eugénie felt inclined to appeal to him to help her, and had him
sounded by one of her friends who was on terms of close intimacy with
him, M. Thiers replied that it was either too late or too early for him to do
anything, and that as matters stood, the best thing to do was to allow events
to take their course. “But the dynasty,” said his visitor; “are you going to
allow the dynasty to fall like that?”
“If the dynasty were wise, I certainly would do my best to support it,”
was the unexpected reply; “but the dynasty will not be wise; it will never
have the common sense to bring itself to conclude peace just now, and to
enforce the conditions of that peace, even by measures of violence against
those who would undoubtedly oppose it. If I thought the Regent was strong
enough and firm enough to arrest half the members of the Corps Législatif,
and to send the other half back to their own firesides to meditate on the
wisdom of a useless opposition, if she would make up her mind to govern
for a time without the Chambers, then I would at once accept office; but she
will never have the courage to take such a responsibility before the country,
and therefore I cannot do anything for her. There are moments in the life of
nations when it is indispensable for their welfare that those who govern
them should feel no hesitation in resorting to violence, and France just now
has reached such a moment. It is a thousand pities that the Regent or the
Emperor fails to see it is the case. Under such circumstances my help would
be useless to them, and it might compromise my own future prospects.”
This conversation gives a very good insight into the character of M.
Thiers. It also accounts in part for the ruthlessness which he displayed in
the crushing of the Commune a few months later.
Apropos of this, a few weeks before his death, I had the opportunity of
talking to him about it at St. Germain, whither he had repaired to spend the
summer, and where he was preparing himself for the struggle of the coming
elections, which he fondly hoped would prove fatal to the government of
Marshal MacMahon, whom he still expected to replace as head of the State.
Thiers was in a communicative mood that afternoon, and he spoke with
great vivacity of that time when he had displayed such energy, as his friends
said—such brutality, as his foes maintained—in fighting the unruly and
disorderly elements that had so very nearly destroyed France. On that
occasion he used these memorable words: “I know that I have been severely
blamed for the orders which I had given to Galiffet, to show no mercy to the
insurgents, but, frankly, what else could I do? We had just gone through an
unfortunate war; the enemy was at our gates, we had to execute a most
onerous treaty, and above all to clear our territory from the invader, who
certainly would never have left it, had he thought that this rebellion was
going to take the upper hand. We had the whole country to reorganise, and
this under the most deplorable conditions that have ever existed in the life
of a nation. We were without an army, without any regular government, and
had to fight the many ambitions of those who thought to seek their own
advantage out of the general ruin. The first thing to do was to strike fear
into the hearts of those who already thought that they could bring their own
party to the head of affairs and thus add something to the general confusion.
Don’t forget that in order to oblige the Prussians to recognise that we were
strong enough to rule France, and to rule it well, we had not only to assert
ourselves, but also to drive out of the minds of all our opponents, and of
these there were legions, the idea that we had not got power enough on our
side.
“You tell me that the Commune might easily have been subdued on that
eventful and fatal 18th of March. This perhaps is true, because it did not
even exist at that time, and we were face to face with a simple insurrection,
not with a revolution. But would it have been wise? I don’t think so. Had
we not acted as if we were in presence of a real and earnest danger, had I
not retired to Versailles in a hurry as I did, the mutiny of the 18th of March
would have repeated itself a few months later, and this sort of thing would
have gone on continually. The government would have been weakened
quite uselessly, and the prestige of France fallen a little lower than was the
case already. A revolution is an incident, perhaps sad and bloody, but an
incident all the same; whereas continual rebellions mean the demoralisation
of a nation.
“I knew that France was demoralised in the sense I mean, but why need
the world come to the same conclusion? Surely, none at all. Therefore we
had to show the world that we were a strong government, that, what is even
more important, we were a government, a fact which many people doubted
still; and that as such we were determined to enforce order, to enforce it in
the most determined manner possible, even at the risk of spilling more
blood than we would have cared to do at other times. Of course I could not
foresee the excesses to which the Commune would resort, nor the murder of
the hostages, or the destroying of half Paris by fire, but I will be frank with
you, I much preferred this to the consequences which would have ensued
for the future of France, in an unsettled state of things such as would have
resulted had the government of which I was the head not had occasion to
show its energy and its decision to make itself respected. Of course, when
Bismarck saw that we could cope with the situation, that we did not require
his, or anyone else’s help, he gave up all idea of making difficulties in the
execution of the different clauses of the treaty of peace. The army also,
having just returned from its captivity in Germany, required something to
divert it from the many anxious and rebellious thoughts it had had time to
indulge in, during the long months of its imprisonment in German
fortresses. The Commune came opportunely to allow it to let its thoughts
drift into another channel.
“To resume the main point, I do not think that more indulgence towards
the rebels would have helped us to regain the position to which even as a
defeated nation we were entitled. For these reasons I do not regret that I
enjoined severity to the troops that entered Paris. This severity had the
result that out of the moral ruins left by the Empire, and those material ruins
which resulted from the fleeting victory of the Commune, rose a
government which won for itself the respect of Europe, and the esteem of
Germany, who, seeing what it was capable of, gave up every thought of
putting difficulties in its way. No, when I remember all that happened at
that time, I cannot say I am sorry for anything I did, or which was done
under my responsibility. I may deplore it, but I cannot regret it. One cannot
be sentimental in politics.”
I wrote down this conversation in my diary when I got home, and every
time I have the occasion to read it over again, I remember the vivacity with
which Thiers developed to me his ideas on this important subject, ideas
which I believe have never before been made known to the public.
It is strange how, with all his penetration, and his wonderful insight into
politics, Thiers did not foresee the circumstances that brought about his own
downfall. There were lacunes in that remarkable mind, lacunes which
proceeded from his inordinate vanity. For instance, when he had started on
that journey across Europe, in order to implore her help during the Franco-
German war, he never for one moment imagined that he would be
unsuccessful, or that his entreaties would be repulsed. The indifference with
which the fate of his country was viewed beyond its frontiers proved a
terrible blow to the old man, who sadly said, or, rather, repeated, the famous
words: “Il n’y a plus d’Europe,” when his last hope, his trust in Alexander
II. of Russia, also proved elusive. But with his usual ability he managed to
mask his defeat under the pretext that neither Italy, Austria, nor Russia
would have anything to do with the Imperial regime, and that as they hadn’t
been sure it was definitely to be classed among the past things of history,
they had thought it best and wisest to remain neutral, and not to interfere
with the course of events. Out of that circumstance Thiers made enough
capital to ensure his own election as head of the government, and once
established at Versailles in that capacity he felt sure that he would remain at
his post until his death.
He had no real adversaries worthy of that name. With consummate skill
he had succeeded in entirely discrediting the Orleans princes by the
willingness with which he had helped them to get back their confiscated
millions, and he knew that henceforward they had made themselves
impossible. There was still the Comte de Chambord, but in his case Thiers
had at his disposal sources of information that left him no doubt as to the
attitude that the latter would eventually take, if offered the crown of his
ancestors. The only adversaries he dreaded were, therefore, the Bonapartes;
and this danger seemed, for the present, to have drifted away by the death
of Napoleon III. and the extreme youth of his son.
Whether it was this last circumstance, or simply that his watchfulness
had relaxed, the fact remains that Thiers never noticed the storm that was
looming in the distance, and threatening him. And when an accidental
circumstance brought about his fall, in quite an unexpected manner, he was
more astonished than anyone else at the event.
Nevertheless, he took it quite good humouredly, and with far more
philosophy than could have been expected from him. I saw him a few days
after it had occurred and was struck with his indifference. I think that upon
the whole he was glad that his fall had taken place for a neutral cause, and
that it had been his person that had been objected to rather than his manner
of conducting the government. He hoped that the future would avenge him,
and though such an old man, yet he was making plans for the day when
France would call him back to the head of affairs. He knew that no matter
what his enemies might say, he had deserved and had earned the gratitude
of his country, and won for himself a glorious page in its annals. And if the
truth be told, he was rather glad to be once more in the ranks of the
opposition, and thus able to live over again the past days, when a word of
his could overturn a government. He devoted all his energies to the struggle
which he fully intended to initiate against President MacMahon, whom he
had never liked, even when he had employed him, and whom he never
forgave for having taken his place. Thiers had always been of opinion that
the Marshal’s intellectual capacities were of the smallest kind, and that
except honesty of purpose, he possessed none of the qualifications that are
required of the Head of a State. It was gall and wormwood to him, to find
his place had been taken by a man who would destroy some of his work,
and a great deal of his plans. So he devoted all his energies to prepare the
defeat of the Marshal after the latter’s coup d’état of the 16th of May.
Fate, however, interfered and carried off M. Thiers after an illness of a
few hours at St. Germain, where, as I have already related, he spent the last
summer of his life. In spite of his advanced age, he died in full possession
of his faculties, and with his intelligence as bright and clear as it had ever
been. The emotion provoked by his death was considerable. The old man
was, after all, more popular than one had thought, and the nation was very
well aware that in burying him, she was also burying a great patriot, who
had been true to her in the hour of her greatest adversity. I followed in his
funeral procession, and as we were marching towards distant Père la
Chaise, I heard the following remark which left a deep impression on my
mind: the more so that it was uttered by a common workman whom
certainly I wouldn’t have believed to be capable of it: “Il avait des défauts,
le petit homme, mais après tout c’est grâce à lui que Belfort est resté
français!” (“He had his faults, the little man; but, after all, it is thanks to
him that Belfort remained French”).
I think that Thiers would have thought, had he listened to these words,
that they constituted the best recognition that had ever been uttered of his
long life of service to the nation.