To save content items to your account,
please confirm that you agree to abide by our usage policies.
If this is the first time you use this feature, you will be asked to authorise Cambridge Core to connect with your account.
Find out more about saving content to .
To save content items to your Kindle, first ensure no-reply@cambridge.org
is added to your Approved Personal Document E-mail List under your Personal Document Settings
on the Manage Your Content and Devices page of your Amazon account. Then enter the ‘name’ part
of your Kindle email address below.
Find out more about saving to your Kindle.
Note you can select to save to either the @free.kindle.com or @kindle.com variations.
‘@free.kindle.com’ emails are free but can only be saved to your device when it is connected to wi-fi.
‘@kindle.com’ emails can be delivered even when you are not connected to wi-fi, but note that service fees apply.
Learn to program more effectively, faster, with better results… and enjoy both the learning experience and the benefits it ultimately brings. While this undergraduate-level textbook is motivated by formal methods, so encouraging habits that lead to correct and concise computer programs, its informal presentation sidesteps any rigid reliance on formal logic which programmers are sometimes led to believe is required. Instead, a straightforward and intuitive use of simple 'What's true here?' comments encourages precision of thought without prescription of notation. Drawing on decades of the author's experience in teaching/industry, the text's careful presentation concentrates on key principles of structuring and reasoning about programs, applying them first to small, understandable algorithms. Then students can concentrate on turning those reliably into their corresponding – and correct – program source codes. The text includes over 200 exercises, for many of which full solutions are provided. A set of all solutions is available for instructors' use.
This chapter summarises what Part I has introduced, reinforcing things that the reader might already have known but whose importance needs a reminder. Additionally however it summarises the point of view (perhaps novel for the reader) that Part I has introduced and emphasises that we do not think about “what happens”, for example inside the boxes of a flowchart, but instead about “what is true” and should be written on the flowchart lines between its boxes.
Drill exercises correspond roughly to multiplication tables in primary school: practice and memorising that make the basic operations almost automatic. This selection does that for the ideas presented in this text: how to write down preconditions, given, say, an assignment statement and a postcondition etc.
Many of the exercises have answers given in Appendix H; those whose answers are not given are ideal for in-class tests.
Here the rules used for data refinement (Part II) are given in a condensed but also more explicit form. It’s for readers who are ready to understand the process at a more fundamental level, and who want to see it all together in one place. An extended example is given to demonstrate how all the rules interact.
Part II turns our attention to data structures and the way they should be coded, the basic principle being that related data should be grouped together. But why? It’s not only to save page turns when browsing your code: it’s that when grouped together in a certain way, data structures can be manipulated, made more efficient, better understood, by –again– focussing not on “what happens” but on “what is, and remains true about the components of a data structure and how they relate to each other”.
In this chapter precise instructions are given for transforming a data-type from one form (abstract) to another (concrete), using the techniques introduced so far and applying them to a running example: a simple “Set” class transformed systematically into one in which the set is represented by a sequence, or even by a fixed-size array. What is not done here however is the treatment of classes and instances more generally, since in terms of intricacy that is beyond our scope. We concentrate instead on the basics, the fundamental principles: not “Do this and all will be fine.” but rather “Here’s why doing something like this is the right approach.”
Although Python works very well for presenting systematic program development, at least for small programas, it is not the only candidate: C would be another, or Java. In Python’s case there are just a few issues that assertion-based reasoning stumbles over, and they are addressed here. If a different programming language were used, this would be a correspondingly different appendix.
Here we review how the formal methods approach was “lost” to the general programming population, in spite of its initial promise. But also we look at all the progress that has nevertheless been made, and by whom. Suggestions are made for teaching on the basis of this text, and pointers are given for those who –once they have “tasted” what informal methods can do– would like to get into the meat of what is possible if they are studied more intensively. Many programmers “out there” would have done that, if only they knew that formal methods existed and what they can do. This text tells them that, and we can hope to collect more “real” formal methods experts as a result. Finally, a brief history of the development of formal methods is given, with references to many of the principal texts published over the years since 1970.
Here a (long) list is given of useful logical equalities and implications, saying simply “what” but not “why”. It’s analogous to a table of integrals as an appendix to a text book on calculus, or to a list of trigonometric identities. In both cases, its utility is either to check a reasoning step you’ve taken (“Is this implication the right way around?” or to search for a simplification step you might be able to apply to make further progress.
These are fully worked answers (mostly) to a selection of the drills in Appendix A; they are marked there by a down arrow. Those without answers here can be used in class exercises or tests.
Although the text avoids (deliberately) the necessity of understanding logic, even only propositional logic, given here is material for explaining in more detail the role it plays and how it works. Again, the aim is to provide instant access for readers who want to dig a bit deeper: they do not have to seek a further text at this point; they can rather just look here in the book they already have. Again an example program is used to help with that. Also important is the intimate connection between program idioms and the idioms of logic, so often now mentioned: how Booleans in programs correspond to sets, how local variables in programs correspond to bound variables in logic etc.
This chapter summarises what Part II has introduced, showing by example how there are systematic rules that maintain correctness of a data encapsulation as it is transformed from one form to another. The key principle –and method– is that by encapsulating the data and presenting it in a simple form, the correctness of any surrounding program can be checked in those simple terms.
A large shared-variable concurrency case study is presented here: “Garbage collection on the fly”. It is a perfect target for concurrent reasoning, and has been attacked many times in many different versions by many prominent researchers. This chapter takes one of those (Dijkstra, Lamport et al. and later Ben-Ari and van der Snepscheut) and develops it step-by-step and in relentless detail using the techniques explained throughout the text up to this point: assertions, loop invariants, the Owicki–Gries method, auxiliary variables, specifications “refined later to code”, elimination of multiple assignments etc. Conspicuously it does not use await statements, because efficiency is also a prime concern: including await statements would make the algorithm potentially too slow. This case study brings everything together.
These are answers, almost all fully worked out for a selection of the exercises at the end of each chapter: the ones answered are marked in the chapter itself by a down arrow. Those without answers can be used in class group-exercises (i.e. with participation of the class, solving them together on the whiteboard), or tests and assignments. The end-of-chapter exercises were deliberately constructed so that they are not necessary for further progress while reading the chapter. (That is why they are at the ends of chapters.) The exercises are instead intended to make concrete any niggling doubt a reader might have at that point: by seeing the exercise there, they can see that they are not alone, that at some stage the author might have had the same doubt; and they can then think harder, can “muse” at their leisure about that issue without being held up in further reading of the text.