cppannotations/annotations/yo/containers/list.yo
2014-12-24 17:28:59 +01:00

242 lines
12 KiB
Text

The ti(list) container hi(list container) implements a list data structure.
Before using a tt(list) container the header file tthi(list) must be included.
The organization of a tt(list) is shown in figure ref(listFig).
figure(containers/list)(A list data-structure)(listFig)
Figure ref(listFig) shows that a list consists of separate
list-elements, connected by pointers. The list can be
hi(list: traversal) traversed in two directions: starting at em(Front) the
list may be traversed from left to right, until the 0-pointer is reached at
the end of the rightmost list-element. The list can also be traversed from
right to left: starting at em(Back), the list is traversed from right to left,
until eventually the 0-pointer emanating from the leftmost list-element is
reached.
As a subtlety note that the representation given in figure ref(listFig) is not
necessarily used in actual implementations of the list. For example, consider
the following little program:
verb(
int main()
{
list<int> l;
cout << "size: " << l.size() << ", first element: " <<
l.front() << '\n';
}
)
When this program is run it might actually produce the output:
verb(
size: 0, first element: 0
)
Its front element can even be assigned a value. In this case the
implementor has chosen to provide the list with a hidden element. The list
actually is a em(circular)
hi(list: circular) list, where the hidden element serves as terminating
element, replacing the 0-pointers in figure ref(listFig). As noted, this is a
subtlety, which doesn't affect the conceptual notion of a list as a data
structure ending in 0-pointers. Note also that it is well known that various
implementations of list-structures are possible (cf.
i(Aho, A.V.), i(Hopcroft J.E.) and i(Ullman, J.D.), (1983)
emi(Data Structures and Algorithms) (Addison-Wesley)).
Both lists and vectors are often appropriate data structures in situations
where an hi(storing data) unknown number of data elements must be
stored. However, there are some hi(rule of thumb) rules of thumb to follow
when selecting the appropriate data structure.
itemization(
it() When most accesses are i(random), a tt(vector) is
the preferred data structure. Example: in a program counting
character frequencies in a textfile, a tt(vector<int> frequencies(256)) is the
datastructure of choice, as the values of the received characters can be
used as indices into the tt(frequencies) vector.
it() The previous example illustrates a second rule of thumb, also
favoring the tt(vector): if the number of elements is known in advance (and
does not notably change during the lifetime of the program), the vector
is also preferred over the list.
it() In cases where i(insertions) or i(deletions) prevail and the data
structure is large the list is generally preferred.
)
At present lists aren't as useful anymore as they used to be (when
computers were much slower and more memory-constrained). Except maybe for
some rare cases, a tt(vector) should be the preferred container; even when
implementing algorithms traditionally using lists.
Other considerations related to the choice between lists and vectors
should also be given some thought. Although it is true that the vector is able
to grow dynamically, the i(dynamic growth) requires data-copying.
Clearly, copying a million large data structures takes a considerable amount
of time, even on fast computers. On the other hand, inserting a large number
of elements in a list doesn't require us to
i(copy non-involved data). Inserting a new element in a list merely
requires us to juggle some pointers. In figure ref(listAdd) this is shown: a
new element is inserted between the second and third element, creating a new
list of four elements.
figure(containers/insertlist)(Adding a new element to a list)(listAdd)
Removing an element from a list is also fairly easy. Starting again
from the situation shown in figure ref(listFig), figure ref(listDel) shows
what happens if element two is removed from our list. Again: only pointers
need to be juggled. In this case it's even simpler than adding an element:
only two pointers need to be rerouted.
figure(containers/dellist)(Removing an element from a list)(listDel)
To summarize the comparison between lists and vectors: it's probably best
to conclude that there is no clear-cut answer to the question what data
structure to prefer. There are rules of thumb, which may be adhered to. But if
worse comes to worst, a i(profiler) may be required to find out what's best.
The tt(list) container offers the following constructors, operators, and
member functions:
itemization(
it() hi(list constructors) Constructors:
itemization(
it() The copy and move constructors are available;
it() A tt(list) may be constructed empty:
verb(
list<string> object;
)
As with the tt(vector), it is an error to refer to an element of an
empty list.
it() A list may be initialized to a certain number of
elements. By default, if the initialization value is not explicitly mentioned,
the default value or default constructor for the actual data type is used. For
example:
verb(
list<string> object(5, string("Hello")); // initialize to 5 Hello's
list<string> container(10); // and to 10 empty strings
)
it() A list may be initialized using a two iterators. To
initialize a list with elements 5 until 10 (including the last one) of a
tt(vector<string>) the following construction may be used:
verb(
extern vector<string> container;
list<string> object(&container[5], &container[11]);
)
)
it() The tt(list) does not offer specialized operators, apart from the
standard operators for containers.
it() The following hi(member function)member functions are available:
itemization(
itht(assign)(void assign(...)):
quote(assigns new contents to the list:)
itemization(
itt(assign(iterator begin, iterator end)) assigns the values at
the iterator range rangett(begin, end) to the list;
itt(assign(size_type n, value_type const &val)) assigns tt(n)
copies of tt(val) to the list;
)
ithtq(back)(Type &back())(returns a
reference to the last element in the list. It is the
i(responsibility of the programmer) to use this member only if the list is
not empty.)
ithtq(begin)(list::iterator begin())(returns an i(iterator) pointing
to the first element in the list, returning tt(end) if the list is empty.)
ithtq(clear)(void clear())(erases all elements from the
list.)
ithtq(empty)(bool empty())(returns tt(true)
if the list contains no elements.)
ithtq(end)(list::iterator end())(returns an iterator pointing beyond
the last element in the list.)
ithtq(erase)(list::iterator erase())(erases a specific range of
elements in the list:)
itemization(
itt(erase(pos)) erases the element pointed to by tt(pos). The
iterator tt(++pos) is returned.
itt(erase(first, beyond)) erases elements indicated by the iterator
range rangett(first, beyond). tt(Beyond) is returned.
)
ithtq(front)(Type &front())(returns a
reference to the first element in the list. It is the responsibility of the
programmer to use this member only if the list is not empty.)
ithtq(get_allocator)(allocator_type get_allocator() const)(returns a
copy of the allocator object used by the tt(list) object.)
ithtq(insert)(... insert())(inserts elements into the list. The return
value depends on the version of tt(insert) that is called:)
itemization(
itt(list::iterator insert(pos)) inserts a default value of type
tt(Type) at tt(pos), tt(pos) is returned.
itt(list::iterator insert(pos, value)) inserts tt(value) at
tt(pos), tt(pos) is returned.
itt(void insert(pos, first, beyond)) inserts the elements in the
i(iterator range) rangeti(first, beyond).
itt(void insert(pos, n, value)) inserts tt(n) elements having value
tt(value) at position tt(pos).
)
ithtq(max_size)(size_t max_size())(returns the maximum number of
elements this tt(list) may contain.)
ithtq(merge)(void merge(list<Type> other))(this
member function assumes that the current and other lists are sorted (see
below, the member tt(sort)). Based on that assumption, it inserts the
elements of tt(other) into the current list in such a way that the modified
list remains sorted. If both list are not sorted, the resulting list will be
ordered `as much as possible', given the initial ordering of the elements in
the two lists. tt(list<Type>::merge) uses tt(Type::operator<) to sort the
data in the list, which operator must therefore be available. The next
example illustrates the use of the tt(merge) member: the list `tt(object)'
is not sorted, so the resulting list is ordered 'as much as possible'.
verbinclude(-a examples/listmerge.cc)
A subtlety is that tt(merge) doesn't alter the list if the list itself
is used as argument: tt(object.merge(object)) won't change the list
`tt(object)'.)
ithtq(pop_back)(void pop_back())(removes
the last element from the list. With an i(empty list) nothing happens.)
ithtq(pop_front)(void pop_front())(removes
the first element from the list. With an i(empty list) nothing happens.)
ithtq(push_back)(void push_back(value))(adds tt(value) to the end of
the list.)
ithtq(push_front)(void push_front(value))(adds tt(value) before the
first element of the list.)
ithtq(rbegin)(list::reverse_iterator rbegin())(
hi(reverse_iterator) returns an iterator pointing to the last element in
the list.)
ithtq(remove)(void remove(value))(removes
all occurrences of tt(value) from the list. In the following example, the two
strings `tt(Hello)' are removed from the list tt(object):
verbinclude(-a examples/listremove.cc))
ithtq(remove_if)(void remove_if(Predicate pred))(removes
all occurrences from the list for which the predicate function or function
object tt(pred) returns tt(true). For each of the objects stored in the list
the predicate is called as tt(pred(*iter)), where tt(iter) represents the
iterator used internally by tt(remove_if). If a function tt(pred) is used, its
prototype should be tt(bool pred(value_type const &object)).
ithtq(rend)(list::reverse_iterator rend())(this
member returns an iterator pointing before the first element in the list.)
ithtq(resize)(void resize())(alters the number of elements that are
currently stored in the list:)
itemization(
itt(resize(n, value)) may be used to resize the list to a size of
tt(n). tt(Value) is optional. If the list is expanded and tt(value) is not
provided, the extra elements are initialized to the i(default value) of the
used data type, otherwise tt(value) is used to initialize extra elements.
)
ithtq(reverse)(void reverse())(reverses the order of the
elements in the list. The element tt(back) becomes tt(front) and em(vice
versa).)
ithtq(size)(size_t size())(returns the number of elements in the
list.)
ithtq(sort)(void sort())(sorts the
list. Once the list has been sorted, An example of its use is given at the
description of the tt(unique) member function below. tt(list<Type>::sort)
uses tt(Type::operator<) to sort the data in the list, which operator must
therefore be available.)
ithtq(splice)(void splice(pos, object))(transfers the contents of
tt(object) to the current list, starting the insertion at the iterator
position tt(pos) of the object using the tt(splice) member. Following
tt(splice), tt(object) is empty. For example:
verbinclude(-a examples/listsplice.cc)
Alternatively, tt(argument) may be followed by an iterator of tt(argument),
indicating the first element of tt(argument) that should be spliced, or by two
iterators tt(begin) and tt(end) defining the iterator-range
rangett(begin, end) on tt(argument) that should be spliced into tt(object).)
ithtq(swap)(void swap())(swaps two lists using identical data types.)
ithtq(unique)(void unique())(operating on a sorted list,
this member function removes all consecutively identical elements from the
list. tt(list<Type>::unique) uses tt(Type::operator==) to identify
identical data elements, which operator must therefore be available. Here's
an example removing all multiply occurring words from the list:
verbinclude(-a examples/listunique.cc))
)
)
)