[5717495] | 1 | \chapter{Linked List}
|
---|
| 2 |
|
---|
[4740281] | 3 | This chapter presents my work on designing and building a linked-list library for \CFA.
|
---|
| 4 | Due to time limitations and the needs expressed by the \CFA runtime developers, I focussed on providing a doubly-linked list, and its bidirectionally iterators for traversal.
|
---|
| 5 | Simpler data-structures, like stack and queue, can be built from the doubly-linked mechanism with only a slight storage/performance cost because of the unused link field.
|
---|
| 6 | Reducing to data-structures with a single link follows directly from the more complex doubly-links and its iterators.
|
---|
[5717495] | 7 |
|
---|
| 8 |
|
---|
[4740281] | 9 | \section{Features}
|
---|
[5717495] | 10 |
|
---|
[4740281] | 11 | The following features directed this project, where the goal is high-performance list operations required by \CFA runtime components, like the threading library.
|
---|
[5717495] | 12 |
|
---|
| 13 |
|
---|
| 14 | \subsection{Core Design Issues}
|
---|
| 15 |
|
---|
[4740281] | 16 | The doubly-linked list attaches links intrusively, supports multiple link directions, integrates with user code via the type system, treats its ends uniformly, and identifies a list using an explicit head.
|
---|
[b195498] | 17 | This design covers system and data management issues stated in \VRef{toc:lst:issue}.
|
---|
[4740281] | 18 |
|
---|
[b195498] | 19 | \VRef[Figure]{fig:lst-features-intro} continues the running @req@ example from \VRef[Figure]{fig:lst-issues-attach} using the \CFA list.
|
---|
| 20 | The \CFA link attachment is intrusive so the resulting memory layout is per user node, as for the LQ version of \VRef[Figure]{f:Intrusive}.
|
---|
[4740281] | 21 | The \CFA framework provides generic type @dlink( T, T )@ for the two link fields (front and back).
|
---|
| 22 | A user inserts the links into the @req@ structure via \CFA inline-inheritance from the Plan-9 C dialect~\cite[\S~3.3]{Thompson90new}.
|
---|
| 23 | Inline inheritance is containment, where the inlined field is unnamed but the type's internal fields are hoisted into the containing structure.
|
---|
| 24 | Hence, the field names must be unique, unlike \CC nested types, but the type names are at a nested scope level, unlike aggregate nesting in C.
|
---|
| 25 | Note, the position of the containment is normally unimportant, unless there is some form of memory or @union@ overlay.
|
---|
| 26 | The key feature of inlined inheritance is that a pointer to the containing structure is automatically converted to a pointer to any anonymous inline field for assignments and function calls, providing containment inheritance with implicit subtyping.
|
---|
| 27 | Therefore, a reference to a @req@ is implicitly convertible to @dlink@ in assignments and function calls.
|
---|
| 28 | % These links have a nontrivial, user-specified location within the @req@ structure;
|
---|
| 29 | % this convention encapsulates the implied pointer arithmetic safely.
|
---|
| 30 | The links in @dlist@ point at (links) in the containing node, know the offsets of all links (data is abstract), and any field-offset arithmetic or link-value changes are safe and abstract.
|
---|
[5717495] | 31 |
|
---|
| 32 | \begin{figure}
|
---|
[4740281] | 33 | \lstinput{20-30}{lst-features-intro.run.cfa}
|
---|
[5717495] | 34 | \caption[Multiple link directions in \CFA list library]{
|
---|
[5d9c4bb] | 35 | Demonstration of the running \lstinline{req} example, done using the \CFA list library.
|
---|
[b195498] | 36 | This example is equivalent to the three approaches in \VRef[Figure]{fig:lst-issues-attach}.
|
---|
[5717495] | 37 | }
|
---|
[5d9c4bb] | 38 | \label{fig:lst-features-intro}
|
---|
[5717495] | 39 | \end{figure}
|
---|
| 40 |
|
---|
[b195498] | 41 | \VRef[Figure]{fig:lst-features-multidir} shows how the \CFA library supports multi-inline links, so a node can be on one or more lists simultaneously.
|
---|
[4740281] | 42 | The declaration of @req@ has two inline-inheriting @dlink@ occurrences.
|
---|
| 43 | The first of these gives a type named @req.by_pri@, @req@ inherits from it, and it inherits from @dlink@.
|
---|
| 44 | The second line @req.by_rqr@ is similar to @req.by_pri@.
|
---|
| 45 | Thus, there is a diamond, non-virtual, inheritance from @req@ to @dlink@, with @by_pri@ and @by_rqr@ being the mid-level types.
|
---|
| 46 |
|
---|
| 47 | Disambiguation occurs in the declarations of the list-head objects: @reqs_pri_global@, @reqs_rqr_42@, @reqs_rqr_17@, and @reqs_rqr_99@.
|
---|
| 48 | The type of the variable @reqs_pri_global@ is @dlist(req, req.by_pri)@, meaning operations called on @reqs_pri_global@ are implicitly disambiguated.
|
---|
| 49 | In the example, the calls @insert_first(reqs_pri_global, ...)@ imply, ``here, we are working by priority.''
|
---|
[b195498] | 50 | As in \VRef[Figure]{fig:lst-issues-multi-static}, three lists are constructed, a priority list containing all nodes, a list with only nodes containing the value 42, and a list with only nodes containing the value 17.
|
---|
[4740281] | 51 |
|
---|
[5717495] | 52 | \begin{figure}
|
---|
[5d9c4bb] | 53 | \centering
|
---|
| 54 | \begin{tabular}{@{}ll@{}}
|
---|
| 55 | \begin{tabular}{@{}l@{}}
|
---|
[4740281] | 56 | \lstinput{20-31}{lst-features-multidir.run.cfa} \\
|
---|
| 57 | \lstinput{43-71}{lst-features-multidir.run.cfa}
|
---|
[5d9c4bb] | 58 | \end{tabular}
|
---|
| 59 | &
|
---|
[b64d0f4] | 60 | \lstinput[language=C++]{20-60}{lst-issues-multi-static.run.c}
|
---|
[5d9c4bb] | 61 | \end{tabular}
|
---|
| 62 |
|
---|
| 63 | \caption{
|
---|
[5717495] | 64 | Demonstration of multiple static link directions done in the \CFA list library.
|
---|
[b195498] | 65 | The right example is from \VRef[Figure]{fig:lst-issues-multi-static}.
|
---|
[4740281] | 66 | The left \CFA example does the same job.
|
---|
[5717495] | 67 | }
|
---|
[5d9c4bb] | 68 | \label{fig:lst-features-multidir}
|
---|
[5717495] | 69 | \end{figure}
|
---|
| 70 |
|
---|
| 71 | The \CFA library also supports the common case, of single directionality, more naturally than LQ.
|
---|
[b195498] | 72 | \VRef[Figure]{fig:lst-features-intro} shows a single-direction list done with no contrived name for the link direction,
|
---|
| 73 | where \VRef[Figure]{f:Intrusive} adds the unnecessary field name, @d@.
|
---|
| 74 | In \CFA, a user doing a single direction (\VRef[Figure]{fig:lst-features-intro}) sets up a simple inheritance with @dlink@, and declares a list head to have the simpler type @dlist( T )@.
|
---|
| 75 | In contrast, (\VRef[Figure]{fig:lst-features-multidir}) sets up a diamond inheritance with @dlink@, and declares a list head to have the more-informed type @dlist( T, DIR )@.
|
---|
[5717495] | 76 |
|
---|
[0d41e600] | 77 | The directionality issue also has an advanced corner-case that needs treatment.
|
---|
[4740281] | 78 | When working with multiple directions, calls like @insert_first@ benefit from implicit direction disambiguation;
|
---|
| 79 | however, other calls like @insert_after@ still require explicit disambiguation, \eg the call
|
---|
[0d41e600] | 80 | \begin{cfa}
|
---|
| 81 | insert_after(r1, r2);
|
---|
| 82 | \end{cfa}
|
---|
| 83 | does not have enough information to clarify which of a request's simultaneous list directions is intended.
|
---|
[4740281] | 84 | Is @r2@ supposed to be the next-priority request after @r1@, or is @r2@ supposed to join the same-requester list of @r1@?
|
---|
[0d41e600] | 85 | As such, the \CFA compiler gives an ambiguity error for this call.
|
---|
[4740281] | 86 | To resolve the ambiguity, the list library provides a hook for applying the \CFA language's scoping and priority rules.
|
---|
[0d41e600] | 87 | It applies as:
|
---|
| 88 | \begin{cfa}
|
---|
| 89 | with ( DLINK_VIA(req, req.pri) ) insert_after(r1, r2);
|
---|
| 90 | \end{cfa}
|
---|
[4740281] | 91 | Here, the @with@ statement opens the scope of the object type for the expression;
|
---|
| 92 | hence, the @DLINK_VIA@ result causes one of the list directions to become a more attractive candidate to \CFA's overload resolution.
|
---|
| 93 | This boost applies within the scope of the following statement, but could also be a custom block or an entire function body.
|
---|
| 94 | \begin{cquote}
|
---|
| 95 | \setlength{\tabcolsep}{15pt}
|
---|
| 96 | \begin{tabular}{@{}ll@{}}
|
---|
[0d41e600] | 97 | \begin{cfa}
|
---|
[4740281] | 98 | void f() @with( DLINK_VIA(req, req.pri) )@ {
|
---|
| 99 | ...
|
---|
[0d41e600] | 100 |
|
---|
[4740281] | 101 | insert_after(r1, r2);
|
---|
[0d41e600] | 102 |
|
---|
[4740281] | 103 | ...
|
---|
[0d41e600] | 104 | }
|
---|
| 105 | \end{cfa}
|
---|
| 106 | &
|
---|
| 107 | \begin{cfa}
|
---|
| 108 | void f() {
|
---|
[4740281] | 109 | ...
|
---|
| 110 | @with( DLINK_VIA(req, req.pri) )@ {
|
---|
| 111 | ... insert_after(r1, r2); ...
|
---|
| 112 | }
|
---|
| 113 | ...
|
---|
[0d41e600] | 114 | }
|
---|
| 115 | \end{cfa}
|
---|
| 116 | \end{tabular}
|
---|
[4740281] | 117 | \end{cquote}
|
---|
| 118 | By using a larger scope, a user can put code within that acts as if there is only one list direction.
|
---|
| 119 | This boost is needed only when operating on a list with several directions, using operations that do not take the list head.
|
---|
[b195498] | 120 | Otherwise, the sole applicable list direction \emph{just works}.
|
---|
[0d41e600] | 121 |
|
---|
[4740281] | 122 | Unlike \CC templates container-types, the \CFA library works completely within the type system;
|
---|
| 123 | both @dlink@ and @dlist@ are ordinary types.
|
---|
| 124 | There is no textual expansion other than header-included static-inline function for performance.
|
---|
[5717495] | 125 | Errors in user code are reported only with mention of the library's declarations.
|
---|
[4740281] | 126 | Finally, the library is separately compiled from the usage code.
|
---|
[5717495] | 127 |
|
---|
| 128 | The \CFA library works in headed and headless modes. TODO: elaborate.
|
---|
| 129 |
|
---|
| 130 |
|
---|
[0e4e43d] | 131 | \section{List API}
|
---|
| 132 |
|
---|
[9d9fd81] | 133 | \VRef[Figure]{f:ListAPI} shows the API for the doubly-link list operations, where each is explained.
|
---|
| 134 | \begin{itemize}[leftmargin=*]
|
---|
[0e4e43d] | 135 | \item
|
---|
[9d9fd81] | 136 | @isListed@ returns true if the node is an element of a list and false otherwise.
|
---|
[0e4e43d] | 137 | \item
|
---|
[9d9fd81] | 138 | @isEmpty@ returns true if the list has no nodes and false otherwise.
|
---|
[0e4e43d] | 139 | \item
|
---|
[b195498] | 140 | @first@ returns a reference to the first node of the list without removing it or @0p@ if the list is empty.
|
---|
[0e4e43d] | 141 | \item
|
---|
[b195498] | 142 | @last@ returns a reference to the last node of the list without removing it or @0p@ if the list is empty.
|
---|
[0e4e43d] | 143 | \item
|
---|
[9d9fd81] | 144 | @insert_before@ adds a node before the specified node and returns the added node for cascading.
|
---|
[0e4e43d] | 145 | \item
|
---|
[9d9fd81] | 146 | @insert_after@ adds a node after the specified node and returns the added node for cascading.
|
---|
[0e4e43d] | 147 | \item
|
---|
[b195498] | 148 | @remove@ removes the specified node from the list (any location) and returns a reference to the node.
|
---|
| 149 | \item
|
---|
| 150 | @iter@ create an iterator for the list.
|
---|
| 151 | \item
|
---|
| 152 | @recede@ returns true if the iterator cursor is advanced to the previous (predecessor, towards first) node before the prior cursor node and false otherwise.
|
---|
| 153 | \item
|
---|
| 154 | @advance@ returns true if the iterator cursor is advanced to the next (successor, towards last) node after the prior cursor node and false otherwise.
|
---|
| 155 | \item
|
---|
| 156 | @isFirst@ returns true if the node is the first node in the list and false otherwise.
|
---|
| 157 | \item
|
---|
| 158 | @isLast@ returns true if the node is the last node in the list and false otherwise.
|
---|
[0e4e43d] | 159 | \item
|
---|
[b195498] | 160 | @pred@ returns a reference to the previous (predecessor, towards first) node before the specified node or @0p@ if the specified node is the first node in the list.
|
---|
[0e4e43d] | 161 | \item
|
---|
[b195498] | 162 | @next@ returns a reference to the next (successor, towards last) node after the specified node or @0p@ if the specified node is the last node in the list.
|
---|
[0e4e43d] | 163 | \item
|
---|
[b195498] | 164 | @insert_first@ adds a node to the start of the list so it becomes the first node and returns a reference to the node for cascading.
|
---|
[0e4e43d] | 165 | \item
|
---|
[b195498] | 166 | @insert_last@ adds a node to the end of the list so it becomes the last node and returns returns a reference to the node for cascading.
|
---|
[0e4e43d] | 167 | \item
|
---|
[b195498] | 168 | @remove_first@ removes the first node and returns a reference to it or @0p@ if the list is empty.
|
---|
| 169 | \item
|
---|
| 170 | @remove_last@ removes the last node and returns a reference to it or @0p@ if the list is empty.
|
---|
[0e4e43d] | 171 | \item
|
---|
[9d9fd81] | 172 | @transfer@ transfers all nodes from the @from@ list to the end of the @to@ list; the @from@ list is empty after the transfer.
|
---|
[0e4e43d] | 173 | \item
|
---|
[9d9fd81] | 174 | @split@ transfers the @from@ list up to node to the end of the @to@ list; the @from@ list becomes the list after the node.
|
---|
| 175 | The node must be in the @from@ list.
|
---|
[0e4e43d] | 176 | \end{itemize}
|
---|
| 177 |
|
---|
[9d9fd81] | 178 | \begin{figure}
|
---|
| 179 | \begin{cfa}
|
---|
[b195498] | 180 | E & isListed( E & node );
|
---|
| 181 | E & isEmpty( dlist( E ) & list );
|
---|
| 182 | E & first( dlist( E ) & list );
|
---|
| 183 | E & last( dlist( E ) & list );
|
---|
[9d9fd81] | 184 | E & insert_before( E & before, E & node );
|
---|
| 185 | E & insert_after( E & after, E & node );
|
---|
| 186 | E & remove( E & node );
|
---|
[b195498] | 187 | E & iter( dlist( E ) & list );
|
---|
| 188 | bool advance( E && refx );
|
---|
| 189 | bool recede( E && refx );
|
---|
| 190 | bool isFirst( E & node );
|
---|
| 191 | bool isLast( E & node );
|
---|
| 192 | E & prev( E & node );
|
---|
| 193 | E & next( E & node );
|
---|
[9d9fd81] | 194 | E & insert_first( dlist( E ) & list, E & node );
|
---|
| 195 | E & insert_last( dlist( E ) & list, E & node );
|
---|
| 196 | E & remove_first( dlist( E ) & list );
|
---|
| 197 | E & remove_last( dlist( E ) & list );
|
---|
| 198 | void transfer( dlist( E ) & to, dlist( E ) & from ) {
|
---|
| 199 | void split( dlist( E ) & to, dlist( E ) & from, E & node ) {
|
---|
| 200 | \end{cfa}
|
---|
| 201 | \caption{\CFA List API}
|
---|
| 202 | \label{f:ListAPI}
|
---|
| 203 | \end{figure}
|
---|
| 204 |
|
---|
[5717495] | 205 |
|
---|
| 206 | \subsection{Iteration}
|
---|
| 207 |
|
---|
[0e4e43d] | 208 | It is possible to iterate through a list manually or using a set of standard macros.
|
---|
[b195498] | 209 | \VRef[Figure]{f:IteratorTemple} shows the iterator template, managing a list of nodes, used throughout the following iterator examples.
|
---|
[0e4e43d] | 210 | Each example assumes its loop body prints the value in the current node.
|
---|
| 211 |
|
---|
[9d9fd81] | 212 | \begin{figure}
|
---|
| 213 | \begin{cfa}
|
---|
| 214 | #include <fstream.hfa>
|
---|
| 215 | #include <list.hfa>
|
---|
| 216 |
|
---|
| 217 | struct node {
|
---|
| 218 | int v;
|
---|
| 219 | inline dlink(node);
|
---|
| 220 | };
|
---|
| 221 | int main() {
|
---|
| 222 | dlist(node) list;
|
---|
| 223 | node n1 = { 1 }, n2 = { 2 }, n3 = { 3 }, n4 = { 4 };
|
---|
| 224 | insert( list, n1 ); insert( list, n2 ); insert( list, n3 ); insert( list, n4 );
|
---|
| 225 | sout | nlOff;
|
---|
[b195498] | 226 | for ( ... ) @sout | it.v | ","; sout | nl;@ // iterator examples in text
|
---|
[9d9fd81] | 227 | remove( n1 ); remove( n2 ); remove( n3 ); remove( n4 );
|
---|
| 228 | }
|
---|
| 229 | \end{cfa}
|
---|
[b195498] | 230 | \caption{Iterator Temple}
|
---|
| 231 | \label{f:IteratorTemple}
|
---|
[9d9fd81] | 232 | \end{figure}
|
---|
| 233 |
|
---|
[0e4e43d] | 234 | The manual method is low level but allows complete control of the iteration.
|
---|
| 235 | The list cursor (index) can be either a pointer or a reference to a node in the list.
|
---|
| 236 | The choice depends on how the programmer wants to access the fields: @it->f@ or @it.f@.
|
---|
| 237 | The following examples use a reference because the loop body manipulates the node values rather than the list pointers.
|
---|
| 238 | The end of iteration is denoted by the loop cursor having the null pointer, denoted @0p@ in \CFA.
|
---|
| 239 |
|
---|
| 240 | \noindent
|
---|
| 241 | Iterating forward and reverse through the entire list.
|
---|
| 242 | \begin{cquote}
|
---|
| 243 | \setlength{\tabcolsep}{15pt}
|
---|
| 244 | \begin{tabular}{@{}l|l@{}}
|
---|
| 245 | \begin{cfa}
|
---|
[b195498] | 246 | for ( node & it = @first@( list ); &it /* != 0p */ ; &it = &@next@( it ) ...
|
---|
| 247 | for ( node & it = @last@( list ); ⁢ &it = &@prev@( it ) ) ...
|
---|
[0e4e43d] | 248 | \end{cfa}
|
---|
| 249 | &
|
---|
| 250 | \begin{cfa}
|
---|
| 251 | 1, 2, 3, 4,
|
---|
| 252 | 4, 3, 2, 1,
|
---|
| 253 | \end{cfa}
|
---|
| 254 | \end{tabular}
|
---|
| 255 | \end{cquote}
|
---|
| 256 | Iterating forward and reverse from a starting node through the remaining list.
|
---|
| 257 | \begin{cquote}
|
---|
| 258 | \setlength{\tabcolsep}{15pt}
|
---|
| 259 | \begin{tabular}{@{}l|l@{}}
|
---|
| 260 | \begin{cfa}
|
---|
[b195498] | 261 | for ( node & it = @n2@; ⁢ &it = &@next@( it ) ) ...
|
---|
| 262 | for ( node & it = @n3@; ⁢ &it = &@prev@( it ) ) ...
|
---|
[0e4e43d] | 263 | \end{cfa}
|
---|
| 264 | &
|
---|
| 265 | \begin{cfa}
|
---|
| 266 | 2, 3, 4,
|
---|
| 267 | 3, 2, 1,
|
---|
| 268 | \end{cfa}
|
---|
| 269 | \end{tabular}
|
---|
| 270 | \end{cquote}
|
---|
[b195498] | 271 | Iterating forward and reverse from a starting node to an ending node through the contained list.
|
---|
[0e4e43d] | 272 | \begin{cquote}
|
---|
| 273 | \setlength{\tabcolsep}{15pt}
|
---|
| 274 | \begin{tabular}{@{}l|l@{}}
|
---|
| 275 | \begin{cfa}
|
---|
[b195498] | 276 | for ( node & it = @n2@; &it @!= &n4@; &it = &@next@( it ) ) ...
|
---|
| 277 | for ( node & it = @n4@; &it @!= &n2@; &it = &@prev@( it ) ) ...
|
---|
[0e4e43d] | 278 | \end{cfa}
|
---|
| 279 | &
|
---|
| 280 | \begin{cfa}
|
---|
| 281 | 2, 3,
|
---|
| 282 | 4, 3,
|
---|
| 283 | \end{cfa}
|
---|
| 284 | \end{tabular}
|
---|
| 285 | \end{cquote}
|
---|
| 286 | Iterating forward and reverse through the entire list using the shorthand start at the list head and pick a direction.
|
---|
[b195498] | 287 | In this case, @advance@ and @recede@ return a boolean, like \CC @while ( cin >> i )@.
|
---|
[0e4e43d] | 288 | \begin{cquote}
|
---|
| 289 | \setlength{\tabcolsep}{15pt}
|
---|
| 290 | \begin{tabular}{@{}l|l@{}}
|
---|
| 291 | \begin{cfa}
|
---|
[b195498] | 292 | for ( node & it = @iter@( list ); @advance@( it ); ) ...
|
---|
| 293 | for ( node & it = @iter@( list ); @recede@( it ); ) ...
|
---|
[0e4e43d] | 294 | \end{cfa}
|
---|
| 295 | &
|
---|
| 296 | \begin{cfa}
|
---|
| 297 | 1, 2, 3, 4,
|
---|
| 298 | 4, 3, 2, 1,
|
---|
| 299 | \end{cfa}
|
---|
| 300 | \end{tabular}
|
---|
| 301 | \end{cquote}
|
---|
| 302 | Finally, there are convenience macros that look like @foreach@ in other programming languages.
|
---|
| 303 | Iterating forward and reverse through the entire list.
|
---|
| 304 | \begin{cquote}
|
---|
| 305 | \setlength{\tabcolsep}{15pt}
|
---|
| 306 | \begin{tabular}{@{}l|l@{}}
|
---|
| 307 | \begin{cfa}
|
---|
| 308 | FOREACH( list, it ) ...
|
---|
| 309 | FOREACH_REV( list, it ) ...
|
---|
| 310 | \end{cfa}
|
---|
| 311 | &
|
---|
| 312 | \begin{cfa}
|
---|
| 313 | 1, 2, 3, 4,
|
---|
| 314 | 4, 3, 2, 1,
|
---|
| 315 | \end{cfa}
|
---|
| 316 | \end{tabular}
|
---|
| 317 | \end{cquote}
|
---|
| 318 | Iterating forward and reverse through the entire list or until a predicate is triggered.
|
---|
| 319 | \begin{cquote}
|
---|
| 320 | \setlength{\tabcolsep}{15pt}
|
---|
| 321 | \begin{tabular}{@{}l|l@{}}
|
---|
| 322 | \begin{cfa}
|
---|
| 323 | FOREACH_COND( list, it, @it.v == 3@ ) ...
|
---|
| 324 | FOREACH_REV_COND( list, it, @it.v == 1@ ) ...
|
---|
| 325 | \end{cfa}
|
---|
| 326 | &
|
---|
| 327 | \begin{cfa}
|
---|
| 328 | 1, 2,
|
---|
| 329 | 4, 3, 2,
|
---|
| 330 | \end{cfa}
|
---|
| 331 | \end{tabular}
|
---|
| 332 | \end{cquote}
|
---|
[b195498] | 333 | Macros are not ideal, so future work is to provide a language-level @foreach@ statement, like \CC.
|
---|
[9d9fd81] | 334 | Finally, a predicate can be added to any of the manual iteration loops.
|
---|
| 335 | \begin{cquote}
|
---|
| 336 | \setlength{\tabcolsep}{15pt}
|
---|
| 337 | \begin{tabular}{@{}l|l@{}}
|
---|
[0e4e43d] | 338 | \begin{cfa}
|
---|
[b195498] | 339 | for ( node & it = first( list ); &it @&& !(it.v == 3)@; &it = &next( it ) ) ...
|
---|
| 340 | for ( node & it = last( list ); &it @&& !(it.v == 1)@; &it = &prev( it ) ) ...
|
---|
| 341 | for ( node & it = iter( list ); advance( it ) @&& !(it.v == 3)@; ) ...
|
---|
| 342 | for ( node & it = iter( list ); recede( it ) @&& !(it.v == 1)@; ) ...
|
---|
[0e4e43d] | 343 | \end{cfa}
|
---|
[9d9fd81] | 344 | &
|
---|
| 345 | \begin{cfa}
|
---|
| 346 | 1, 2,
|
---|
| 347 | 4, 3, 2,
|
---|
| 348 | 1, 2,
|
---|
| 349 | 4, 3, 2,
|
---|
| 350 | \end{cfa}
|
---|
| 351 | \end{tabular}
|
---|
| 352 | \end{cquote}
|
---|
[0e4e43d] | 353 |
|
---|
| 354 | \begin{comment}
|
---|
[4740281] | 355 | Many languages offer an iterator interface for collections, and a corresponding for-each loop syntax for consuming the items through implicit interface calls.
|
---|
[0d41e600] | 356 | \CFA does not yet have a general-purpose form of such a feature, though it has a form that addresses some use cases.
|
---|
[4740281] | 357 | This section shows why the incumbent \CFA pattern does not work for linked lists and gives the alternative now offered by the linked-list library.
|
---|
[0d41e600] | 358 | Chapter 5 [TODO: deal with optimism here] presents a design that satisfies both uses and accommodates even more complex collections.
|
---|
| 359 |
|
---|
[4740281] | 360 | The current \CFA extensible loop syntax is:
|
---|
[0d41e600] | 361 | \begin{cfa}
|
---|
| 362 | for( elem; end )
|
---|
[4740281] | 363 | for( elem; begin ~ end )
|
---|
| 364 | for( elem; begin ~ end ~ step )
|
---|
[0d41e600] | 365 | \end{cfa}
|
---|
[4740281] | 366 | Many derived forms of @begin ~ end@ exist, but are used for defining numeric ranges, so they are excluded from the linked-list discussion.
|
---|
| 367 | These three forms are rely on the iterative trait:
|
---|
[0d41e600] | 368 | \begin{cfa}
|
---|
[4740281] | 369 | forall( T ) trait Iterate {
|
---|
| 370 | void ?{}( T & t, zero_t );
|
---|
| 371 | int ?<?( T t1, T t2 );
|
---|
| 372 | int ?<=?( T t1, T t2 );
|
---|
| 373 | int ?>?( T t1, T t2 );
|
---|
| 374 | int ?>=?( T t1, T t2 );
|
---|
| 375 | T ?+=?( T & t1, T t2 );
|
---|
| 376 | T ?+=?( T & t, one_t );
|
---|
| 377 | T ?-=?( T & t1, T t2 );
|
---|
| 378 | T ?-=?( T & t, one_t );
|
---|
| 379 | }
|
---|
[0d41e600] | 380 | \end{cfa}
|
---|
[4740281] | 381 | where @zero_t@ and @one_t@ are constructors for the constants 0 and 1.
|
---|
| 382 | The simple loops above are abbreviates for:
|
---|
[0d41e600] | 383 | \begin{cfa}
|
---|
[4740281] | 384 | for( typeof(end) elem = @0@; elem @<@ end; elem @+=@ @1@ )
|
---|
| 385 | for( typeof(begin) elem = begin; elem @<@ end; elem @+=@ @1@ )
|
---|
| 386 | for( typeof(begin) elem = @0@; elem @<@ end; elem @+=@ @step@ )
|
---|
[0d41e600] | 387 | \end{cfa}
|
---|
[4740281] | 388 | which use a subset of the trait operations.
|
---|
| 389 | The shortened loop works well for iterating a number of times or through an array.
|
---|
| 390 | \begin{cfa}
|
---|
| 391 | for ( 20 ) // 20 iterations
|
---|
| 392 | for ( i: 1 ~= 21 ~ 2 ) // odd numbers
|
---|
| 393 | for ( i; n ) total += a[i]; // subscripts
|
---|
| 394 | \end{cfa}
|
---|
| 395 | which is similar to other languages, like JavaScript.
|
---|
[0d41e600] | 396 | \begin{cfa}
|
---|
| 397 | for ( i in a ) total += a[i];
|
---|
| 398 | \end{cfa}
|
---|
[4740281] | 399 | Albeit with different mechanisms for expressing the array's length.
|
---|
| 400 | It might be possible to take the \CC iterator:
|
---|
| 401 | \begin{c++}
|
---|
| 402 | for ( list<int>::iterator it=mylist.begin(); it != mylist.end(); ++it )
|
---|
| 403 | \end{c++}
|
---|
| 404 | and convert it to the \CFA form
|
---|
| 405 | \begin{cfa}
|
---|
| 406 | for ( it; begin() ~= end() )
|
---|
| 407 | \end{cfa}
|
---|
| 408 | by having a list operator @<=@ that just looks for equality, and @+=@ that moves to the next node, \etc.
|
---|
| 409 |
|
---|
[0e4e43d] | 410 | However, the list usage is contrived, because a list does use its data values for relational comparison, only links for equality comparison.
|
---|
[4740281] | 411 | Hence, the focus of a list iterator's stopping condition is fundamentally different.
|
---|
| 412 | So, iteration of a linked list via the existing loop syntax is to ask whether this syntax can also do double-duty for iterating values.
|
---|
[0d41e600] | 413 | That is, to be an analog of JavaScript's @for..of@ syntax:
|
---|
| 414 | \begin{cfa}
|
---|
| 415 | for ( e of a ) total += e;
|
---|
| 416 | \end{cfa}
|
---|
| 417 |
|
---|
| 418 | The \CFA team will likely implement an extension of this functionality that moves the @~@ syntax from being part of the loop, to being a first-class operator (with associated multi-pace operators for the elided derived forms).
|
---|
| 419 | With this change, both @begin ~ end@ and @end@ (in context of the latter ``two-place for'' expression) parse as \emph{ranges}, and the loop syntax becomes, simply:
|
---|
| 420 | \begin{cfa}
|
---|
| 421 | for( elem; rangeExpr )
|
---|
| 422 | \end{cfa}
|
---|
| 423 | The expansion and underlying API are under discussion.
|
---|
| 424 | TODO: explain pivot from ``is at done?'' to ``has more?''
|
---|
| 425 | Advantages of this change include being able to pass ranges to functions, for example, projecting a numerically regular subsequence of array entries, and being able to use the loop syntax to cover more collection types, such as looping over the keys of a hashtable.
|
---|
| 426 |
|
---|
[4740281] | 427 | When iterating an empty list, the question, ``Is there a further element?'' needs to be posed once, receiving the answer, ``no.''
|
---|
[0d41e600] | 428 | When iterating an $n$-item list, the same question gets $n$ ``yes'' answers (one for each element), plus one ``no'' answer, once there are no more elements; the question is posed $n+1$ times.
|
---|
| 429 |
|
---|
[4740281] | 430 | When iterating an empty list, the question, ``What is the value of the current element?'' is never posed, nor is the command, ``Move to the next element,'' issued. When iterating an $n$-item list, each happens $n$ times.
|
---|
[0d41e600] | 431 |
|
---|
| 432 | So, asking about the existence of an element happens once more than retrieving an element's value and advancing the position.
|
---|
| 433 |
|
---|
| 434 | Many iteration APIs deal with this fact by splitting these steps across different functions, and relying on the user's knowledge of iterator state to know when to call each. In Java, the function @hasNext@ should be called $n+1$ times and @next@ should be called $n$ times (doing the double duty of advancing the iteration and returning a value). In \CC, the jobs are split among the three actions, @it != end@, @it++@ and @*it@, the latter two being called one time more than the first.
|
---|
| 435 |
|
---|
| 436 | TODO: deal with simultaneous axes: @DLINK_VIA@ just works
|
---|
| 437 |
|
---|
| 438 | TODO: deal with spontaneous simultaneity, like a single-axis req, put into an array: which ``axis'' is @&req++@ navigating: array-adjacency vs link dereference. It should sick according to how you got it in the first place: navigating dlist(req, req.pri) vs navigating array(req, 42). (prob. future work)
|
---|
[0e4e43d] | 439 | \end{comment}
|
---|
| 440 |
|
---|
[0d41e600] | 441 |
|
---|
| 442 | \section{Implementation}
|
---|
| 443 |
|
---|
| 444 | \VRef[Figure]{fig:lst-impl-links} continues the running @req@ example, now showing the \CFA list library's internal representation.
|
---|
[4740281] | 445 | The @dlink@ structure contains exactly two pointers: @next@ and @prev@, which are opaque to a user.
|
---|
[b195498] | 446 | Even though the user-facing list model is linear, the CFA library implements all listing as circular.
|
---|
[4740281] | 447 | This choice helps achieve uniform end treatment and TODO finish summarizing benefit.
|
---|
| 448 | A link pointer targets a neighbouring @dlink@ structure, rather than a neighbouring @req@.
|
---|
| 449 | (Recall, the running example has the user putting a @dlink@ within a @req@.)
|
---|
[0d41e600] | 450 |
|
---|
| 451 | \begin{figure}
|
---|
| 452 | \centering
|
---|
| 453 | \includegraphics{lst-impl-links.pdf}
|
---|
| 454 | \caption{
|
---|
| 455 | \CFA list library representations for the cases under discussion.
|
---|
| 456 | }
|
---|
| 457 | \label{fig:lst-impl-links}
|
---|
| 458 | \end{figure}
|
---|
| 459 |
|
---|
[b195498] | 460 | System-added link pointers (dashed lines) are internally tagged to indicate linear endpoints that are the circular pointers.
|
---|
| 461 | Links among neighbour nodes are not tagged.
|
---|
[0d41e600] | 462 | Iteration reports ``has more elements'' when crossing natural links, and ``no more elements'' upon reaching a tagged link.
|
---|
| 463 |
|
---|
| 464 | In a headed list, the list head (@dlist(req)@) acts as an extra element in the implementation-level circularly-linked list.
|
---|
[b195498] | 465 | The content of a @dlist@ is a (private) @dlink@, with the @next@ pointer to the first element, and the @prev@ pointer to the last element.
|
---|
| 466 | Since the head wraps a @dlink@, as does a @req@ does too, and since a link-pointer targets a @dlink@, the resulting cycle is among @dlink@ structures, situated inside of header/node.
|
---|
| 467 | An untagged pointer points within a @req@, while a tagged pointer points within a list head.
|
---|
| 468 | In a headless list, the circular backing list is only among @dlink@s within @req@s.
|
---|
| 469 | The tags are set on the links that a user cannot navigate.
|
---|
| 470 |
|
---|
| 471 | No distinction is made between an unlisted item under a headed model and a singleton list under a headless model.
|
---|
| 472 | Both are represented as an item referring to itself, with both tags set.
|
---|
[0d41e600] | 473 |
|
---|
[5717495] | 474 |
|
---|
| 475 | \section{Future Work}
|
---|
| 476 | \label{toc:lst:futwork}
|
---|
| 477 |
|
---|
[4740281] | 478 | The \CFA list examples elide the \lstinline{P9_EMBEDDED} annotations that (TODO: xref P9E future work) proposes to obviate.
|
---|
| 479 | Thus, these examples illustrate a to-be state, free of what is to be historic clutter.
|
---|
| 480 | The elided portions are immaterial to the discussion and the examples work with the annotations provided.
|
---|
| 481 | The \CFA test suite (TODO:cite?) includes equivalent demonstrations, with the annotations included.
|
---|
| 482 | \begin{cfa}
|
---|
| 483 | struct mary {
|
---|
| 484 | float anotherdatum;
|
---|
| 485 | inline dlink(mary);
|
---|
| 486 | };
|
---|
| 487 | struct fred {
|
---|
| 488 | float adatum;
|
---|
| 489 | inline struct mine { inline dlink(fred); };
|
---|
| 490 | inline struct yours { inline dlink(fred); };
|
---|
| 491 | };
|
---|
| 492 | \end{cfa}
|
---|
| 493 | like in the thesis examples. You have to say
|
---|
| 494 | \begin{cfa}
|
---|
| 495 | struct mary {
|
---|
| 496 | float anotherdatum;
|
---|
| 497 | inline dlink(mary);
|
---|
| 498 | };
|
---|
| 499 | P9_EMBEDDED(mary, dlink(mary))
|
---|
| 500 | struct fred {
|
---|
| 501 | float adatum;
|
---|
| 502 | inline struct mine { inline dlink(fred); };
|
---|
| 503 | inline struct yours { inline dlink(fred); };
|
---|
| 504 | };
|
---|
| 505 | P9_EMBEDDED(fred, fred.mine)
|
---|
| 506 | P9_EMBEDDED(fred, fred.yours)
|
---|
| 507 | P9_EMBEDDED(fred.mine, dlink(fred))
|
---|
| 508 | P9_EMBEDDED(fred.yours, dlink(fred))
|
---|
| 509 | \end{cfa}
|
---|
| 510 | like in tests/list/dlist-insert-remove.
|
---|
| 511 | Future work should autogen those @P9_EMBEDDED@ declarations whenever it sees a plan-9 declaration.
|
---|
| 512 | The exact scheme chosen should harmonize with general user-defined conversions.
|
---|
| 513 |
|
---|
| 514 | Today's P9 scheme is: mary gets a function `inner returning this as dlink(mary).
|
---|
| 515 | Fred gets four of them in a diamond.
|
---|
| 516 | They're defined so that `inner is transitive; i.e. fred has two further ambiguous overloads mapping fred to dlink(fred).
|
---|
| 517 | The scheme allows the dlist functions to give the assertion, "we work on any T that gives a `inner to dlink(T)."
|
---|
| 518 |
|
---|
[5717495] | 519 |
|
---|
| 520 | TODO: deal with: A doubly linked list is being designed.
|
---|
| 521 |
|
---|
| 522 | TODO: deal with: Link fields are system-managed.
|
---|
| 523 | Links in GDB.
|
---|
| 524 |
|
---|