Elements of Programming. Alexander Stepanov and Paul McJones. Addison- Wesley Professional Sample pages (Preface, Chapter 2, Index) PDF. Contents. Elements of Programming. 3 reviews. by Paul McJones, Alexander Stepanov. Publisher: Addison-Wesley Professional. Release Date: June Elements of Programming - ondieslinfuncton.ga - Ebook download as PDF File .pdf), Text File .txt) or read book online.
|Language:||English, Spanish, Indonesian|
|Distribution:||Free* [*Sign up for free]|
Data structures and program design in C++ / Robert L. Kruse,. Alexander J. Ryba. p. cm. Includes Data str Programming Embedded Systems, Second Edition. Many are the ways of getting into programming. One route is to start tinkering with the html source code behind web pages. This can lead to. This talk is an introduction to the book. Elements of Programming published. Addison Wesley in The book presents practical.
With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more. Start Free Trial No credit card required. Elements of Programming 3 reviews. View table of contents. Start reading. Foundations 2. Transformations and Their Orbits 3. Associative Operations 4. Linear Orderings 5. Ordered Algebraic Structures 6.
Iterators 7. Coordinate Structures 8. Coordinates with Mutable Successors 9. Copying Finally, the book would not have been possible without Sean Parent's enlightened management and continuous scrutiny of the code and the text. The ideas in the book stem from our close collaboration, spanning almost three decades, with Dave Musser. Both Dave and Bjarne were kind enough to come to San Jose and carefully review the preliminary draft.
Jon Brandt reviewed multiple drafts of the book. John Wilkinson carefully read the final manuscript, providing innumerable valuable suggestions. The book has benefited significantly from the contributions of our editor, Peter Gordon, our project editor, Elizabeth Ryan, our copy editor, Evelyn Pyle, and the editorial reviewers: We thank all the students who took the course at Adobe and an earlier course at SGI for their suggestions.
We hope we succeeded in weaving the material from these courses into a coherent whole. Finally, we are grateful to all the people who taught us through their writings or in person, and to the institutions that allowed us to deepen our understanding of programming. He has been programming since He has programmed operating systems, programming tools, compilers, and libraries. In he received the Dr.
Paul McJones studied engineering mathematics at the University of California, Berkeley, from to He has been programming since in the areas of operating systems, programming environments, transaction processing systems, and enterprise and consumer applications. Foundations Starting with a brief taxonomy of ideas, we introduce notions of value, object, type, procedure, and concept that represent different categories of ideas in the computer.
A central notion of the book, regularity, is introduced and elaborated. When applied to procedures, regularity means that procedures return equal results for equal arguments. When applied to types, regularity means that types possess the equality operator and equality-preserving copy construction and assignment.
Regularity enables us to apply equational reasoning substituting equals for equals to transform and optimize programs. Entity, Species, Genus In order to explain what objects, types, and other foundational computer notions are, it is useful to give an overview of some categories of ideas that correspond to these notions. An abstract entity is an individual thing that is eternal and unchangeable, while a concrete entity is an individual thing that comes into and out of existence in space and time.
An attribute—a correspondence between a concrete entity and an abstract entity—describes some property, measurement, or quality of the concrete entity. Identity, a primitive notion of our perception of reality, determines the sameness of a thing changing over time. Attributes of a concrete entity can change without affecting its identity.
A snapshot of a concrete entity is a complete collection of its attributes at a particular point in time. Concrete entities are not only physical entities but also legal, financial, or political entities. Blue and 13 are examples of abstract entities.
Socrates and the United States of America are examples of concrete entities. The color of Socrates' eyes and the number of U. An abstract species describes common properties of essentially equivalent abstract entities. Examples of abstract species are natural number and color. A concrete species describes the set of attributes of essentially equivalent concrete entities. Examples of concrete species are man and U.
A function is a rule that associates one or more abstract entities, called arguments, from corresponding species with an abstract entity, called the result, from another species.
Examples of functions are the successor function, which associates each natural number with the one that immediately follows it, and the function that associates with two colors the result of blending them. An abstract genus describes different abstract species that are similar in some respect. Examples of abstract genera are number and binary operator.
A concrete genus describes different concrete species similar in some respect. Examples of concrete genera are mammal and biped.
An entity belongs to a single species, which provides the rules for its construction or existence. An entity can belong to several genera, each of which describes certain properties. We show later in the chapter that objects and values represent entities, types represent species, and concepts represent genera.
Values Unless we know the interpretation, the only things we see in a computer are 0s and 1s. A datum is a finite sequence of 0s and 1s. A datum corresponding to a particular entity is called a representation of the entity; the entity is called the interpretation of the datum. We refer to a datum together with its interpretation as a value. Examples of values are integers represented in bit two's complement big-endian format and rational numbers represented as a concatenation of two bit sequences, interpreted as integer numerator and denominator, represented as two's complement big-endian values.
A datum is well formed with respect to a value type if and only if that datum represents an abstract entity.
For example, every sequence of 32 bits is well formed when interpreted as a two's- complement integer; an IEEE floating-point NaN Not a Number is not well formed when interpreted as a real number. A value type is properly partial if its values represent a proper subset of the abstract entities in the corresponding species; otherwise it is total.
For example, the type int is properly partial, while the type bool is total. A value type is uniquely represented if and only if at most one value corresponds to each abstract entity. For example, a type representing a truth value as a byte that interprets zero as false and nonzero as true is not uniquely represented.
A type representing an integer as a sign bit and an unsigned magnitude does not provide a unique representation of zero. A type representing an integer in two's complement is uniquely represented. A value type is ambiguous if and only if a value of the type has more than one interpretation. The negation of ambiguous is unambiguous.
For example, a type representing a calendar year over a period longer than a single century as two decimal digits is ambiguous. Two values of a value type are equal if and only if they represent the same abstract entity. They are representationally equal if and only if their datums are identical sequences of 0s and 1s. Lemma 1. If a value type is uniquely represented, we implement equality by testing that both sequences of 0s and 1s are the same. Otherwise we must implement equality in such a way that preserves its consistency with the interpretations of its arguments.
Nonunique representations are chosen when testing equality is done less frequently than operations generating new values and when it is possible to make generating new values faster at the cost of making equality slower.
For example, two rational numbers represented as pairs of integers are equal if they reduce to the same lowest terms. Two finite sets represented as unsorted sequences are equal if, after sorting and eliminating duplicates, their corresponding elements are equal. Sometimes, implementing true behavioral equality is too expensive or even impossible, as in the case for a type of encodings of computable functions.
In these cases we must settle for the weaker representational equality: Computers implement functions on abstract entities as functions on values. While values reside in memory, a properly implemented function on values does not depend on particular memory addresses: It implements a mapping from values to values. A function defined on a value type is regular if and only if it respects equality: Substituting an equal value for an argument gives an equal result.
Most numeric functions are regular. An example of a numeric function that is not regular is the function that returns the numerator of a rational number If a value type is uniquely represented, equality implies representational equality.
If a value type is not ambiguous, representational equality implies equality. Regular functions allow equational reasoning: A nonregular function depends on the representation, not just the interpretation, of its argument. When designing the representation for a value type, two tasks go hand in hand: Objects A memory is a set of words, each with an address and a content.
The addresses are values of a fixed size, called the address length. The contents are values of another fixed size, called the word length.
The content of an address is obtained by a load operation. The association of a content with an address is changed by a store operation. Examples of memories are bytes in main memory and blocks on a disk drive. An object is a representation of a concrete entity as a value in memory. An object has a state that is a value of some value type. The state of an object is changeable. Given an object corresponding to a concrete entity, its state corresponds to a snapshot of that entity.
An object owns a set of resources, such as memory words or records in a file, to hold its state. While the value of an object is a contiguous sequence of 0s and 1s, the resources in which these 0s and 1s are stored are not necessarily contiguous. It is the interpretation that gives unity to an object. For example, two doubles may be interpreted as a single complex number even if they are not adjacent. The resources of an object might even be in different memories. This book, however, deals only with objects residing in a single memory with one address space.
Every object has a unique starting address, from which all its resources can be reached. An object type is a pattern for storing and modifying values in memory. Corresponding to every object type is a value type describing states of objects of that type. Every object belongs to an object type. An example of an object type is integers represented in bit two's complement little-endian format aligned to a 4-byte address boundary. Values and objects play complementary roles.
Values are unchanging and are independent of any particular implementation in the computer. Objects are changeable and have computer-specific implementations. The state of an object at any point in time can be described by a value; this value could in principle be written down on paper making a snapshot or serialized and sent over a communication link. Describing the states of objects in terms of values allows us to abstract from the particular implementations of the objects when discussing equality.
Functional programming deals with values; imperative programming deals with objects. We use values to represent entities. Since values are unchanging, they can represent abstract entities.
Sequences of values can also represent sequences of snapshots of concrete entities. Objects hold values representing entities. Since objects are changeable, they can represent concrete entities by taking on a new value to represent a change in the entity. Objects can also represent abstract entities: We use objects in the computer for the following three reasons.
Objects model changeable concrete entities, such as employee records in a payroll application. Objects provide a powerful way to implement functions on values, such as a procedure implementing the square root of a floating-point number using an iterative algorithm.
Computers with memory constitute the only available realization of a universal computational device. Some properties of value types carry through to object types. An object is well formed if and only if its state is well formed.
An object type is properly partial if and only if its value type is properly partial; otherwise it is total. An object type is uniquely represented if and only if its value type is uniquely represented. Since concrete entities have identities, objects representing them need a corresponding notion of identity.
An identity token is a unique value expressing the identity of an object and is computed from the value of the object and the address of its resources.
Examples of identity tokens are the address of the object, an index into an array where the object is stored, and an employee number in a personnel record. Testing equality of identity tokens corresponds to testing identity. During the lifetime of an application, a particular object could use different identity tokens as it moves either within a data structure or from one data structure to another. Two objects of the same type are equal if and only if their states are equal.
If two objects are equal, we say that one is a copy of the other. Making a change to an object does not affect any copy of it. This book uses a programming language that has no way to describe values and value types as separate from objects and object types. So from this point on, when we refer to types without qualification, we mean object types.
Procedures A procedure is a sequence of instructions that modifies the state of some objects; it may also construct or destroy objects. The objects with which a procedure interacts can be divided into four kinds, corresponding to the intentions of the programmer. Local state consists of objects created, destroyed, and usually modified during a single invocation of the procedure.
Global state consists of objects accessible to this and other procedures across multiple invocations. Own state consists of objects accessible only to this procedure and its affiliated procedures but shared across multiple invocations. An object is an input to a procedure if it is read, but not modified, by the procedure. An object is an output from a procedure if it is written, created, or destroyed by the procedure, but its initial state is not read by the procedure. A computational basis for a type is a finite set of procedures that enable the construction of any other procedure on the type.
A basis is efficient if and only if any procedure implemented using it is as efficient as an equivalent procedure written in terms of an alternative basis. For example, a basis for unsigned k -bit integers providing only zero, equality, and the successor function is not efficient, since the complexity of addition in terms of successor is exponential in k.
A basis is expressive if and only if it allows compact and convenient definitions of procedures on the type. In particular, all the common mathematical operations need to be provided when they are appropriate. For example, subtraction could be implemented using negation and addition but should be included in an expressive basis.
Similarly, negation could be implemented using subtraction and zero but should be included in an expressive basis. Regular Types There is a set of procedures whose inclusion in the computational basis of a type lets us place objects in data structures and use algorithms to copy objects from one data structure to another.
We call types having such a basis regular, since their use guarantees regularity of behavior and, therefore, interoperability. A type is regular if and only if its basis includes equality, assignment, destructor, default constructor, copy constructor, total ordering,  and underlying type.
Equality is a procedure that takes two objects of the same type and returns true if and only if the object states are equal. Inequality is always defined and returns the negation of equality. We use the following notation: Assignment is a procedure that takes two objects of the same type and makes the first object equal to the second without modifying the second.
The meaning of assignment does not depend on the initial value of the first object. After a destructor has been called on an object, no procedure can be applied to it, and its former memory locations and resources may be reused for other purposes. The destructor is normally invoked implicitly. Global objects are destroyed when the application terminates, local objects are destroyed when the block in which they are declared is exited, and elements of a data structure are destroyed when the data structure is destroyed.
A constructor is a procedure transforming memory locations into an object. The possible behaviors range from doing nothing to establishing a complex object state. An object is in a partially formed state if it can be assigned to or destroyed. For an object that is partially formed but not well formed, the effect of any procedure other than assignment only on the left side and destruction is not defined.
A default constructor takes no arguments and leaves the object in a partially formed state. A copy constructor takes an additional argument of the same type and constructs a new object equal to it.
Regular Procedures A procedure is regular if and only if replacing its inputs with equal objects results in equal output objects. As with value types, when defining an object type we must make consistent choices in how to implement equality and which procedures on the type will be regular. While regularity is the default, there are reasons for nonregular behavior of procedures. A functional procedure is a regular procedure defined on regular types, with one or more direct inputs and a single output that is returned as the result of the procedure.
The regularity of functional procedures allows two techniques for passing inputs. When the size of the parameter is small or if the procedure needs a copy it can mutate, we pass it by value, making a local copy. Otherwise we pass it by constant reference.
This is a functional procedure: The notion of a functional procedure is a syntactic rather than semantic property: The definition space for a functional procedure is that subset of values for its inputs to which it is intended to be applied. A procedure returns the address of an object; for example, the built-in function addressof. A procedure returns a value determined by the state of the real world, such as the value of a clock or other device.
A procedure returns a value depending on own state; for example, a pseudorandom number generator. A procedure returns a representation-dependent attribute of an object, such as the amount of reserved memory for a data structure.
A homogeneous functional procedure is one whose input objects are all the same type. The domain of a homogeneous functional procedure is the type of its inputs. Rather than defining the domain of a nonhomogeneous functional procedure as the direct product of its input types, we refer individually to the input types of a procedure.
The codomain for a functional procedure is the type of its output. The result space for a functional procedure is the set of all values from its codomain returned by the procedure for inputs from its definition space. Exercise 1. Assuming that int is a bit two's complement type, determine the exact definition and result space.
Concepts A procedure using a type depends on syntactic, semantic, and complexity properties of the computational basis of the type. Syntactically it depends on the presence of certain literals and procedures with particular names and signatures. Its semantics depend on properties of these procedures. Its complexity depends on the time and space complexity of these procedures. A program remains correct if a type is replaced by a different type with the same properties.
The utility of a software component, such as a library procedure or data structure, is increased by designing it not in terms of concrete types but in terms of requirements on types expressed as syntactic and semantic properties.
We call a collection of requirements a concept. Types represent species; concepts represent genera. In order to describe concepts, we need several mechanisms dealing with types: A type attribute is a mapping from a type to a value describing some characteristic of the type. If F is a functional procedure type, Arity F returns its number of inputs. A type function is a mapping from a type to an affiliated type.
An example of a type function is: In some cases it is useful to define an indexed type function with an additional constant integer parameter; for example, a type function returning the type of the ith member of a structure type counting from 0. If F is a functional procedure type, the type function Codomain F returns the type of the result.
If is an n-ary type constructor, we usually denote its application to types T 0 , An important example is pair, which, when applied to regular types T 0 and T 1 , returns a struct type pair T0 , T 1 with a member m0 of type T 0 and a member m1 of type T 1.
To ensure that the type pair T0 , T1 is itself regular, equality, assignment, destructor, and constructors are defined through memberwise extensions of the corresponding operations on the types T 0 and T 1. The same technique is used for any tuple type, such as triple. In Chapter 12 we show the implementation of pair T 0 , T 1 and describe how regularity is preserved by more complicated type constructors.
Somewhat more formally, a concept is a description of requirements on one or more types stated in terms of the existence and properties of procedures, type attributes, and type functions defined on the types. We say that a concept is modeled by specific types, or that the types model the concept, if the requirements are satisfied for these types. To assert that a concept is modeled by types T 0 , Concept refines concept if whenever is satisfied for a set of types, is also satisfied for those types.
We say that weakens if refines. A type concept is a concept defined on one type. We use the primitive type concepts Regular and FunctionalProcedure, corresponding to the informal definitions we gave earlier. We define concepts formally by using standard mathematical notation. Application of a previously defined concept, indicating a subset of the type parameters modeling it. Signature of a type attribute, type function, or procedure that must exist for any types modeling the concept.
A procedure signature takes the form f: T T', where T is the domain and T' is the codomain. A type function signature takes the form , where the domain and codomain are concepts.
Axiom expressed in terms of these type attributes, type functions, and procedures. We sometimes include the definition of a type attribute, type function, or procedure following its T 0 , It takes the form for some expression. In a particular model, such a definition could be overridden with a different but consistent implementation. For example, this concept describes a unary functional procedure: This concept describes a homogeneous functional procedure: The parameters follow the template keyword and are introduced by typename for types and int or another integral type for constant values.
Requirements are specified via the requires clause, whose argument is an expression built up from constant values, concrete types, formal parameters, applications of type attributes and type functions, equality on values and types, concepts, and logical connectives. George Collins and David Musser used them in the context of computer algebra in the late s and early s.
See, for example, Musser . Here is an example of an abstract procedure: Operations tend to be small e.
For example, a procedure might require a parameter to be a prime number. The requirement for an integer type is specified by a concept, while primality is specified by a precondition. The type of a function pointer expresses only its signature, not its semantic properties. For example, a procedure might require a parameter to be a pointer to a function implementing an associative binary operation on integers.
The requirement for a binary operation on integers is specified by a concept; associativity of a particular function is specified by a precondition. To define a precondition for a family of types, we need to use mathematical notation, such as universal and existential quantifiers, implication, and so on. For example, to specify the primality of an integer, we define where the first line introduces formal type parameters and the concepts they model, the second line names the property and gives its signature, and the third line gives the predicate establishing whether the property holds for a given argument.
To define regularity of a unary functional procedure, we write The definition easily extends to n-ary functions: Application of equal functions to equal arguments gives equal results. By extension, we call an abstract function regular if all its instantiations are regular. In this book every procedural argument is a regular function unless otherwise stated; we omit the precondition stating this explicitly.
Project 1. Integer prime: Think about the interpretations of the two types and axioms that connect cross- type procedures. By grounding the meanings of values and objects in their interpretations, we obtain a simple, coherent view. Design decisions, such as how to define equality, become straightforward when the correspondence to entities is taken into account.
Transformations and Their Orbits This chapter defines a transformation as a unary regular function from a type to itself. Successive applications of a transformation starting from an initial value determine an orbit of this value.
Depending only on the regularity of the transformation and the finiteness of the orbit, we implement an algorithm for determining orbit structures that can be used in different domains. For example, it could be used to detect a cycle in a linked list or to analyze a pseudorandom number generator. We derive an interface to the algorithm as a set of related procedures and definitions for their arguments and results.
This analysis of an orbit-structure algorithm allows us to introduce our approach to programming in the simplest possible setting. Transformations While there are functions from any sequence of types to any type, particular classes of signatures commonly occur. In this book we frequently use two such classes: Homogeneous predicates are of the form T x While there are n-ary predicates and n-ary operations, we encounter mostly unary and binary homogeneous predicates and unary and binary operations.
A predicate is a functional procedure returning a truth value: A homogeneous predicate is one that is also a homogeneous function: An operation is a homogeneous function whose codomain is equal to its domain: Examples of operations: This lemma shows that the ternary version can be obtained from the binary version.
For reasons of efficiency, expressiveness, and, possibly, accuracy, the ternary version is part of the computational basis for programs dealing with three-dimensional space. A procedure is partial if its definition space is a subset of the direct product of the types of its inputs; it is total if its definition space is equal to the direct product.
We follow standard mathematical usage, where partial function includes total function. We call partial procedures that are not total nontotal. Implementations of some total functions are nontotal on the computer because of the finiteness of the representation. For example, addition on signed bit integers is nontotal. A nontotal procedure is accompanied by a precondition specifying its definition space.
To verify the correctness of a call of that procedure, we must determine that the arguments satisfy the precondition. Sometimes, a partial procedure is passed as a parameter to an algorithm that needs to determine at runtime the definition space of the procedural parameter. To deal with such cases, we define a definition-space predicate with the same inputs as the procedure; the predicate returns true if and only if the inputs are within the definition space of the procedure.
Exercise 2. This chapter deals with unary operations, which we call transformations: We discuss DistanceType in the next section. Transformations are self-composable: The definition space of f f x is the intersection of the definition space and result space of f. This ability to self-compose, together with the ability to test for equality, allows us to define interesting algorithms. When f is a transformation, we define its powers as follows: To implement an algorithm to compute f n x , we need to specify the requirement for an integer type.
We study various concepts describing integers in Chapter 5. For now we rely on the intuitive understanding of integers. Their models include signed and unsigned integral types, as well as arbitrary-precision integers, with these operations and literals: That leads to the following algorithm: Orbits To understand the global behavior of a transformation, we examine the structure of its orbits: The orbit of x under a transformation f is the set of all elements reachable from x under f.
Lemma 2. If y is reachable from x under f, the distance from x to y is the least number of transformation steps from x to y. Obviously, distance is not always defined. If type T occupies k bits, there can be as many as 2 k values but only 2 k — 1 steps between distinct values. Thus if T is a fixed-size type, an integral type of the same size is a valid distance type for any transformation on T.
It is often the case that all transformation types over a domain have the same distance type. In this case the type function DistanceType is defined for the domain type and defines the corresponding type function for the transformation types. The existence of DistanceType leads to the following procedure: An orbit contains at most one terminal element.
An orbit of x under a transformation is An orbit of x is finite if it is not infinite. Figure 2. Orbit Shapes. The orbit cycle is the set of cyclic elements in the orbit and is empty for infinite and terminating orbits. The orbit handle, the complement of the orbit cycle with respect to the orbit, is empty for a circular orbit. The orbit size o of an orbit is the number of distinct elements in it. The handle size h of an orbit is the number of elements in the orbit handle.
The cycle size c of an orbit is the number of elements in the orbit cycle. Collision Point If we observe the behavior of a transformation, without access to its definition, we cannot determine whether a particular orbit is infinite: It might terminate or cycle back at any point.
If we know that an orbit is finite, we can use an algorithm to determine the shape of the orbit. Therefore there is an implicit precondition of orbit finiteness for all the algorithms in this chapter. There is, of course, a naive algorithm that stores every element visited and checks at every step whether the new element has been previously encountered.
Even if we could use hashing to speed up the search, such an algorithm still would require linear storage and would not be practical in many applications. However, there is an algorithm that requires only a constant amount of storage. The following analogy helps to understand the algorithm. If a fast car and a slow one start along a path, the fast one will catch up with the slow one if and only if there is a cycle. If there is no cycle, the fast one will reach the end of the path before the slow one.
If there is a cycle, by the time the slow one enters the cycle, the fast one will already be there and will catch up eventually. Carrying our intuition from the continuous domain to the discrete domain requires care to avoid the fast one skipping past the slow one.
The discrete version of the algorithm is based on looking for a point where fast meets slow. To handle partial transformations, we pass a definition-space predicate to the algorithm: While f is a partial function, its use by the procedure is well defined, since the movement of fast is guarded by a call of p.
The movement of slow is unguarded, because by the regularity of f, slow traverses the same orbit as fast, so f is always defined when applied to slow. If there is no cycle, p will eventually return false because of finiteness. If there is a cycle, slow will eventually reach the connection point the first element in the cycle.
Consider the distance d from fast to slow at the top of the loop when slow first enters the cycle: Otherwise the distance from fast to slow decreases by 1 on each iteration. The following procedure determines whether an orbit is terminating: Determining the latter is simple once the collision point is known: Traverse the cycle and count the steps.
To see how to determine h, let us look at the position of the collision point: If the orbits of two elements intersect, they have the same cyclic elements. Design an algorithm that determines, given a transformation and its definition-space predicate, whether the orbits of two elements intersect.
Measuring Orbit Sizes The natural type to use for the sizes o, h, and c of an orbit on type T would be an integer count type large enough to count all the distinct values of type T. If a type T occupies k bits, there can be as many as 2 k values, so a count type occupying k bits could not represent all the counts from 0 to 2 k.
There is a way to represent these sizes by using distance type. An orbit could potentially contain all values of a type, in which case o might not fit in the distance type. Depending on the shape of such an orbit, h and c would not fit either. In all cases each of these fits: That allows us to implement procedures returning a triple representing the complete structure of an orbit, where the members of the triple are as follows: There is a duality between transformations and the corresponding actions: An action is definable in terms of a transformation, and vice versa: For example, if a transformation is defined on a large object and modifies only part of its overall state, the action could be considerably faster.
Project 2. Rewrite all the algorithms in this chapter in terms of actions. Another way to detect a cycle is to repeatedly test a single advancing element for equality with a stored element while replacing the stored element at ever-increasing intervals. This and other ideas are described in Sedgewick, et al.
Implement other algorithms for orbit analysis, compare their performance for different applications, and develop a set of recommendations for selecting the appropriate algorithm. Conclusions Abstraction allowed us to define abstract procedures that can be used in different domains. Regularity of types and functions is essential to make the algorithms work: Developing nomenclature is essential e.
Affiliated types, such as distance type, need to be precisely defined. Associative Operations This chapter discusses associative binary operations. Associativity allows regrouping the adjacent operations. This ability to regroup leads to an efficient algorithm for computing powers of the binary operation.
Regularity enables a variety of program transformations to optimize the algorithm. We then use the algorithm to compute linear recurrences, such as Fibonacci numbers, in logarithmic time. Associativity A binary operation is an operation with two arguments: The binary operations of addition and multiplication are central to mathematics. Many more are used, such as min, max, conjunction, disjunction, set union, set intersection, and so on.
All these operations are associative: BinaryOperation associative: When a particular associative binary operation op is clear from the context, we often use implied multiplicative notation by writing ab instead of op a, b. Because of associativity, we do not need to parenthesize an expression involving two or more applications of op, because all the groupings are equivalent: Lemma 3. This condition holds only when the operation is commutative. If we choose some element a of the domain of an associative operation op and consider the expression op a, x as a unary operation with formal parameter x, we can think of a as the transformation "multiplication by a.
This duality allows us to use an algorithm from the previous chapter to prove an interesting theorem about powers of an associative operation. Theorem 3. An element of finite order has an idempotent power Frobenius . Assume that x is an element of finite order under an associative operation op.
Since x is an element of finite order, its orbit under g has a cycle. By its postcondition, for some n 0. Computing Powers An algorithm to compute a n for an associative operation op will take a, n, and op as parameters. The type of a is the domain of op; n must be of an integer type. Without the assumption of associativity, two algorithms compute power from left to right and right to left, respectively: They return different results for a nonassociative operation.
Consider, for example, raising 1 to the 3rd power with the operation of subtraction. When both a and n are integers, and if the operation is multiplication, both algorithms give us exponentiation; if the operation is addition, both give us multiplication.
The ancient Egyptians discovered a faster multiplication algorithm that can be generalized to computing powers of any associative operation. The number of recursive calls is log 2 n. Each recursive call performs an operation to square a. There are also faster groupings for other exponents, such as 23, 27, 39, and This is not always the case.
The concept of regularity is an assertion by the creator of a type that programmers and compilers can safely perform such transformations. While only one is executed in a given invocation, it is possible to reduce the code size via common-subexpression elimination: A first step is to transform the procedure to tail-recursive form, where the procedure's execution ends with the recursive call.
One of the techniques that allows this transformation is accumulation-variable introduction, where the accumulation variable carries the accumulated result between recursive calls: As an additional benefit, this version computes not just power but also power multiplied by a coefficient.
It also handles zero as the value of the exponent. That can be eliminated by adding an extra case: We take advantage of this by eliminating the test for 0 and strengthening the precondition: While developing a component, we often discover new interfaces.
Now we relax the precondition again: The transformation is accumulation-variable elimination: For example, when n is 16, it performs seven operations where only four are needed. When n is odd, this algorithm is fine.
Therefore we can avoid the problem by repeated squaring of a and halving the exponent until it becomes odd: Special-Case Procedures In the final versions we used these operations: We can use shifts and masks on non-negative values of both signed and unsigned integers.
It is frequently useful to identify commonly occuring expressions involving procedures and constants of a type by defining special-case procedures.
Often these special cases can be implemented more efficiently than the general case and, therefore, belong to the computational basis of the type. For built-in types, there may exist machine instructions for the special cases. For user-defined types, there are often even more significant opportunities for optimizing special cases. For example, division of two arbitrary polynomials is more difficult than division of a polynomial by x.
Any integer type must provide the following special-case procedures: Integer I successor: Now we can give the final implementations of the power procedures by using the special-case procedures: We can extend power to zero exponents by passing the identity element as another parameter: Parameterizing Algorithms In power we use two different techniques for providing operations for the abstract algorithm.
The associative operation is passed as a parameter. This allows power to be used with different operations on the same type, such as multiplication modulo n. The operations on the exponent are provided as part of the computational basis for the exponent type.
When a procedure is defined with an operation as a parameter, a suitable default should be specified whenever possible. For example, the natural default for the operation passed to power is multiplication. Using an operator symbol or a procedure name with the same semantics on different types is called overloading, and we say that the operator symbol or procedure name is overloaded on the type.
When actual type parameters satisfy the requirements, the instances of the abstract procedure have the same semantics. Linear Recurrences A linear recurrence function of order k is a function f such that where coefficients a 0 , a k—1 0. Given k initial values x 0 , As we will see, we can compute x n in O log 2 n steps, using power. They discuss linear recurrences starting on page A straightforward matrix multiplication algorithm requires k 3 multiplications and k 3 — k 2 additions of coefficients.
Recall that k is the order of the linear recurrence and is a constant. We never defined the domain of the elements of a linear recurrence sequence. It could be integers, rationals, reals, or complex numbers: The only requirements are the existence of associative and commutative addition, associative multiplication, and distributivity of multiplication over addition.
We use the Fibonacci sequence to illustrate how the k 3 multiplications can be reduced for this particular case. Let  Leonardo Pisano, Liber Abaci, first edition, For an English translation, see Sigler . We can show by induction that Indeed: Since the first element of the bottom row of F n is f n , the following procedure computes f n: Accumulation Procedures The previous chapter defined an action as a dual to a transformation. An accumulation procedure is definable in terms of a binary operation, and vice versa: Exercise 3.
Project 3. Rewrite all the algorithms in this chapter in terms of accumulation procedures. Create a library for the generation of linear recurrence sequences based on the results of Miller and Brown  and Fiduccia .
Conclusions Algorithms are abstract when they can be used with different models satisfying the same requirements, such as associativity. Code optimization depends on equational reasoning; unless types are known to be regular, few optimizations can be performed. Special-case procedures can make code more efficient and even more abstract. The combination of mathematics and abstract algorithms leads to surprising algorithms, such as logarithmic time generation of the nth element of a linear recurrence.
Linear Orderings This chapter describes properties of binary relations, such as transitivity and symmetry. In particular, we introduce total and weak linear orderings. We introduce the concept of stability of functions based on linear ordering: We generalize min and max to order-selection functions, such as the median of three elements, and introduce a technique for managing their implementation complexity through reduction to constrained subproblems.
Classification of Relations A relation is a predicate taking two parameters of the same type: A relation is transitive if, whenever it holds between a and b, and between b and c, it holds between a and c: A relation is strict if it never holds between an element and itself; a relation is reflexive if it always holds between an element and itself: All the previous examples of transitive relations are reflexive; proper factor is strict.
Exercise 4. A relation is symmetric if, whenever it holds in one direction, it holds in the other; a relation is asymmetric if it never holds in both directions: An example of a symmetric transitive relation is "sibling"; an example of an asymmetric transitive relation is "ancestor.
Relation transitive: Relation strict: Relation reflexive: R r a Domain R r a, a Give an example of a relation that is neither strict nor reflexive. Relation symmetric: Relation asymmetric: Given a relation r a, b , there are derived relations with the same domain: Given a symmetric relation, the only interesting derivable relation is the complement, because the converse is equivalent to the original relation.
A relation is an equivalence if it is transitive, reflexive, and symmetric: Examples of equivalence relations are equality, geometric congruence, and integer congruence modulo n. Lemma 4.
An equivalence relation partitions its domain into a set of equivalence classes: We can often implement an equivalence relation by defining a key function, a function that returns a unique value for all the elements in each equivalence class. Applying equality to the results of the key function determines equivalence: Give an example of a symmetric relation that is not transitive. Give an example of a symmetric relation that is not reflexive. Relation equivalence: UnaryFunction, R: Total and Weak Orderings A relation is a total ordering if it is transitive and obeys the trichotomy law, whereby for every pair of elements, exactly one of the following holds: A relation is a weak ordering if it is transitive and there is an equivalence relation on the same domain such that the original relation obeys the weak-trichotomy law, whereby for every pair of elements, exactly one of the following holds: Examples of a weak ordering are pairs ordered by their first members and employees ordered by salary.
R r transitive r a, b Domain R exactly one of the following holds: A total ordering is a weak ordering. A key function f on a set T, together with a total ordering r on the codomain of f, define a weak ordering x, y r f x , f y. We refer to total and weak orderings as linear orderings because of their respective trichotomy laws.
A weak ordering is asymmetric. A weak ordering is strict. Order Selection Given a weak ordering r and two objects a and b from r's domain, it makes sense to ask which is the minimum. It is obvious how to define the minimum when r or its converse holds between a and b but is not so when they are equivalent.
A similar problem arises if we ask which is the maximum. A property for dealing with this problem is known as stability. Informally, an algorithm is stable if it respects the original order of equivalent objects. So if we think of minimum and maximum as selecting, respectively, the smallest and second smallest from a list of two arguments, stability requires that when called with equivalent elements, minimum should return the first and maximum the second.
To formalize our notion of stability, assume that each of the k arguments is associated with a unique natural number called its stability index. Given the original weak ordering r, we define the strengthened relation on object, stability index pairs: The natural default for the stability index of an argument is its ordinal position in the argument list.
While the strengthened relation gives us a powerful tool for reasoning about stability, it is straightforward to define simple order-selection procedures without making the stability indices explicit.
This implementation of minimum returns a when a and b are equivalent, satisfying our definition of stability: While it is useful to have other order-selection procedures for k arguments, the difficulty of writing such an order-selection procedure grows quickly with k, and there are many different procedures we might have a need for.
A technique we call reduction to constrained subproblems addresses both issues. We develop a family of procedures that assume a certain amount of information about the relative ordering of their arguments.
Naming these procedures systematically is essential. We append a sequence of letters to indicate a precondition on the ordering of parameters, expressed as increasing chains. More than one such suffix appears when there are preconditions on different chains of parameters. For example, it is straightforward to implement minimum and maximum for three elements: Because the parameters are passed by constant reference, no data movement takes place: The function does two comparisons only when c is the maximum of a, b, c, and since it happens in one-third of the cases, the average number of comparisons is , assuming a uniform distribution of inputs.
It is easy to select the second of four if we know that the first pair of arguments and the second pair of arguments are each in increasing order: Maintaining stability of order-selection networks up through order 4 has not been too difficult. But with order 5, situations arise in which the procedure corresponding to a constrained subproblem is called with arguments out of order from the original caller, which violates stability.
A systematic way to deal with such situations is to pass the stability indices along with the actual parameters and to use the strengthened relation. We avoid extra runtime cost by using integer template parameters. We name the stability indices ia, ib, We can wrap an order-selection procedure with an outer procedure that supplies, as the stability indices, any strictly increasing series of integer constants; by convention, we use successive integers starting with 0: Find an algorithm for median of 5 that does slightly fewer comparisons on average.