• No se han encontrado resultados

El marco estatal para la promoción de la cinematografía

A link rearrangement is an algorithm taking one or more linked ranges, returning one or more linked ranges, and satisfying the following properties.

 Input ranges (either counted or bounded) are pairwise disjoint.

 Output ranges (either counted or bounded) are pairwise disjoint.

 Every iterator in an input range appears in one of the output ranges.

 Every iterator in an output range appeared in one of the input ranges.

 Every iterator in each output range designates the same object as before the rearrangement, and this object has the same value.

Note that successor and predecessor relationships that held in the input ranges may not hold in the output ranges.

A link rearrangement is precedence preserving if, whenever two iterators i j in an output range came from the same input range, i j originally held in the input range.

Implementing a link rearrangement requires care to satisfy the properties of disjointness,

conservation, and ordering. We proceed by presenting three short procedures, or machines, each of which performs one step of traversal or linking, and then composing from these machines link

rearrangements for splitting, combining, and reversing linked ranges. The first two machines establish or maintain the relationship t = successor(f) between two iterator objects passed by reference:

template<typename I>

requires(ForwardIterator(I)) void advance_tail(I& t, I& f) {

// Precondition: successor(f) is defined t = f; f = successor(f); } template<typename S> requires(ForwardLinker(S)) struct linker_to_tail { typedef IteratorType(S) I; S set_link;

linker_to_tail(const S& set_link) : set_link(set_link) { } void operator()(I& t, I& f)

{

// Precondition: successor(f) is defined set_link(t, f);

advance_tail(t, f); }

};

We can use advance_tail to find the last iterator in a nonempty bounded range:[1]

[1] Observe that find_adjacent_mismatch_forward in Chapter 6 used advance_tail implicitly.

template<typename I> requires(ForwardIterator(I)) I find last(I f, I l) { // Precondition: bounded_range(f, l) f l I t; do advance tail(t, f); while (f != l); return t; }

We can use advance_tail and linker_to_tail together to split a range into two ranges based on the value of a pseudopredicate applied to each iterator. A pseudopredicate is not necessarily regular, and its result may depend on its own state as well as its inputs. For example, a pseudopredicate might ignore its arguments and return alternating false and true values. The algorithm takes a bounded range of linked iterators, a pseudopredicate on the linked iterator type, and a linker object. The algorithm returns a pair of ranges: iterators not satisfying the pseudopredicate and iterators satisfying it. It is useful to represent these returned ranges as closed bounded ranges [h, t], where h is the first, or head, iterator, and t is the last, or tail, iterator. Returning the tail of each range allows the caller to relink that iterator without having to traverse to it (using find_last, for example). However, either of the returned ranges could be empty, which we represent by returning h = t = l, where l is the limit of the input range. The successor links of the tails of the two returned ranges are not

modified by the algorithm. Here is the algorithm:

template<typename I, typename S, typename Pred>

requires(ForwardLinker(S) && I == IteratorType(S) && UnaryPseudoPredicate(Pred) && I == Domain(Pred)) pair< pair<I, I>, pair<I, I> >

split_linked(I f, I l, Pred p, S set_link) {

// Precondition: bounded range(f, l) typedef pair<I, I> P;

linker_to_tail<S> link_to_tail(set_link); I h0 = l; I t0 = l;

I h1 = l; I t1 = l;

if (f == l) goto s4; if (p(f)) { h1 = f; advance_tail(t1, f); goto s1; } else { h0 = f; advance_tail(t0, f); goto s0; } s0: if (f == l) goto s4; if (p(f)) { h1 = f; advance_tail(t1, f); goto s3; } else { advance_tail(t0, f); goto s0; } s1: if (f == l) goto s4; if (p(f)) { advance_tail(t1, f); goto s1; } else { h0 = f; advance_tail(t0, f); goto s2; } s2: if (f == l) goto s4; if (p(f)) { link to tail(t1, f); goto s3; } else { advance_tail(t0, f); goto s2; } s3: if (f == l) goto s4; if (p(f)) { advance_tail(t1, f); goto s3; } else { link_to_tail(t0, f); goto s2; } s4: return pair<P, P>(P(h0, t0), P(h1, t1));

}

The procedure is a state machine. The variables t0 and t1 point to the tails of the two output ranges, respectively. The states correspond to the following conditions:

s0: successor(t0) = f ¬p(t0)

s1: successor(t1) = f p(t1)

s2: successor(t0) = f ¬p(t0) p(t1)

s3: successor(t1) = f ¬p(t0) p(t1)

Relinking is necessary only when moving between states s2 and s3. goto statements from a state to the immediately following state are included for symmetry.

Lemma 8.1.

Exercise 8.1.

Lemma 8.2.

We can also use advance_tail and linker_to_tail to implement an algorithm to combine two For each of the ranges [h, t] returned by split_linked, h = l t = l.

Assuming that one of the ranges (h, t) returned by split_linked is not empty, explain what iterator t points to and what the value of successor(t) is.

ranges into a single range based on a pseudorelation applied to the heads of the remaining portions of the input ranges. A pseudorelation is a binary homogeneous pseudopredicate and thus not necessarily regular. The algorithm takes two bounded ranges of linked iterators, a pseudorelation on the linked iterator type, and a linker object. The algorithm returns a triple (f, t, l), where [f, l) is the half-open range of combined iterators, and t [f, l) is the last-visited iterator. A subsequent call to find_last(t,

l) would return the last iterator in the range, allowing it to be linked to another range. Here is the

algorithm:

template<typename I, typename S, typename R>

requires(ForwardLinker(S) && I == IteratorType(S) && PseudoRelation(R) && I == Domain(R))

triple<I, I, I>

combine_linked_nonempty(I f0, I l0, I f1, I l1, R r, S set_link) {

// Precondition: bounded range(f0, l0) bounded range(f1, l1) // Precondition: f0 l0 f1 l1 disjoint(f0, l0, f1, l1) typedef triple<I, I, I> T;

linker_to_tail<S> link_to_tail(set_link); I h; I t;

if (r(f1, f0)) { h = f1; advance_tail(t, f1); goto s1; } else { h = f0; advance_tail(t, f0); goto s0; } s0: if (f0 == l0) goto s2; if (r(f1, f0)) { link to tail(t, f1); goto s1; } else { advance_tail(t, f0); goto s0; } s1: if (f1 == l1) goto s3; if (r(f1, f0)) { advance_tail(t, f1); goto s1; } else { link to tail(t, f0); goto s0; } s2: set_link(t, f1); return T(h, t, l1);

s3: set_link(t, f0); return T(h, t, l0); }

Exercise 8.2.

The procedure is also a state machine. The variable t points to the tail of the output range. The states correspond to the following conditions:

s0: successor(t) = f0 ¬r(f1, t) s1: successor(t) = f1 r(t, f0)

Relinking is necessary only when moving between states s0 and s1.

Lemma 8.3.

Lemma 8.4.

Implement combine_linked, allowing for empty inputs. What value should be returned as the last-visited iterator?

If a call combine_linked_nonempty(f0, l0, f1, l1, r, s) returns (h, t, l), h equals f0 or f1 and, independently, l equals l0 or l1.

When state s2 is reached, t is from the original range [f0, l0), successor(t) = l0, and f1 l1; when state s3 is reached, t is from the original range [f1, l1), successor(t) = l1, and f0 l0.

Lemma 8.5.

The third machine links to the head of a list rather than to its tail:

template<typename I, typename S>

requires(ForwardLinker(S) && I == IteratorType(S)) struct linker_to_head

{

S set_link;

linker_to_head(const S& set_link) : set_link(set_link) {} void operator()(I& h, I& f)

{

// Precondition: successor(f) is defined IteratorType(S) tmp = successor(f); set_link(f, h); h = f; f = tmp; } };

With this machine, we can reverse a range of iterators:

template<typename I, typename S>

requires(ForwardLinker(S) && I == IteratorType(S)) I reverse_append(I f, I l, I h, S set_link) { // Precondition: bounded_range(f, l) h [f, l) linker_to_head<I, S> link_to_head(set_link); while (f != l) link_to_head(h, f); return h; }

To avoid sharing of proper tails, h should be the beginning of a disjoint linked list (for a singly linked list, nil is acceptable) or l. While we could have used l as the initial value for h (thus giving us reverse_linked), it is useful to pass a separate accumulation parameter.

combine_linked_nonempty is a precedence-preserving link rearrangement.

Algorithms C++ Software Engineering Programming Alexander Stepanov Paul McJones Addison-Wesley Professional Elements of Programming

Documento similar