2 This module implements a singly-linked list container.
3 It can be used as a stack.
5 This module is a submodule of $(MREF std, container).
7 Source: $(PHOBOSSRC std/container/_slist.d)
9 Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
11 License: Distributed under the Boost Software License, Version 1.0.
12 (See accompanying file LICENSE_1_0.txt or copy at $(HTTP
13 boost.org/LICENSE_1_0.txt)).
15 Authors: $(HTTP erdani.com, Andrei Alexandrescu)
17 $(SCRIPT inhibitQuickIndex = 1;)
19 module std.container.slist;
24 import std.algorithm.comparison : equal;
25 import std.container : SList;
27 auto s = SList!int(1, 2, 3);
28 assert(equal(s[], [1, 2, 3]));
31 assert(equal(s[], [2, 3]));
33 s.insertFront([5, 6]);
34 assert(equal(s[], [5, 6, 2, 3]));
36 // If you want to apply range operations, simply slice it.
37 import std.algorithm.searching : countUntil;
38 import std.range : popFrontN, walkLength;
40 auto sl = SList!int(1, 2, 3, 4, 5);
41 assert(countUntil(sl[], 2) == 1);
45 assert(walkLength(r) == 3);
48 public import std.container.util;
51 Implements a simple and fast singly-linked list.
52 It can be used as a stack.
54 $(D SList) uses reference semantics.
58 import std.exception : enforce;
59 import std.range : Take;
60 import std.range.primitives : isInputRange, isForwardRange, ElementType;
61 import std.traits : isImplicitlyConvertible;
68 private struct NodeWithoutPayload
72 static assert(NodeWithoutPayload._next.offsetof == Node._next.offsetof);
76 private void initialize() @trusted nothrow pure
79 _root = cast (Node*) new NodeWithoutPayload();
82 private ref inout(Node*) _first() @property @safe nothrow pure inout
88 private static Node * findLastNode(Node * n)
100 private static Node * findLastNode(Node * n, size_t limit)
103 auto ahead = n._next;
113 private static Node * findNode(Node * n, Node * findMe)
116 auto ahead = n._next;
117 while (ahead != findMe)
127 Constructor taking a number of nodes
129 this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
135 Constructor taking an input range
137 this(Stuff)(Stuff stuff)
138 if (isInputRange!Stuff
139 && isImplicitlyConvertible!(ElementType!Stuff, T)
140 && !is(Stuff == T[]))
146 Comparison for equality.
148 Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of
149 elements in $(D rhs).
151 bool opEquals(const SList rhs) const
153 return opEquals(rhs);
157 bool opEquals(ref const SList rhs) const
159 if (_root is rhs._root) return true;
160 if (_root is null) return rhs._root is null || rhs._first is null;
161 if (rhs._root is null) return _root is null || _first is null;
163 const(Node) * n1 = _first, n2 = rhs._first;
165 for (;; n1 = n1._next, n2 = n2._next)
168 if (!n2 || n1._payload != n2._payload) return false;
173 Defines the container's primary range, which embodies a forward range.
177 private Node * _head;
178 private this(Node * p) { _head = p; }
180 /// Input range primitives.
181 @property bool empty() const { return !_head; }
184 @property ref T front()
186 assert(!empty, "SList.Range.front: Range is empty");
187 return _head._payload;
193 assert(!empty, "SList.Range.popFront: Range is empty");
197 /// Forward range primitive.
198 @property Range save() { return this; }
202 import std.algorithm.mutation : move;
204 assert(!empty, "SList.Range.moveFront: Range is empty");
205 return move(_head._payload);
208 bool sameHead(Range rhs)
210 return _head && _head == rhs._head;
216 static assert(isForwardRange!Range);
220 Property returning $(D true) if and only if the container has no
223 Complexity: $(BIGOH 1)
225 @property bool empty() const
227 return _root is null || _first is null;
231 Duplicates the container. The elements themselves are not transitively
234 Complexity: $(BIGOH n).
236 @property SList dup()
238 return SList(this[]);
242 Returns a range that iterates over all elements of the container, in
245 Complexity: $(BIGOH 1)
252 return Range(_first);
256 Forward to $(D opSlice().front).
258 Complexity: $(BIGOH 1)
260 @property ref T front()
262 assert(!empty, "SList.front: List is empty");
263 return _first._payload;
268 auto s = SList!int(1, 2, 3);
270 assert(s == SList!int(42, 2, 3));
274 Returns a new $(D SList) that's the concatenation of $(D this) and its
275 argument. $(D opBinaryRight) is only defined if $(D Stuff) does not
276 define $(D opBinary).
278 SList opBinary(string op, Stuff)(Stuff rhs)
279 if (op == "~" && is(typeof(SList(rhs))))
281 auto toAdd = SList(rhs);
282 if (empty) return toAdd;
285 auto n = findLastNode(result._first);
286 n._next = toAdd._first;
291 SList opBinaryRight(string op, Stuff)(Stuff lhs)
292 if (op == "~" && !is(typeof(lhs.opBinary!"~"(this))) && is(typeof(SList(lhs))))
294 auto toAdd = SList(lhs);
295 if (empty) return toAdd;
297 result.insertFront(toAdd[]);
302 Removes all contents from the $(D SList).
304 Postcondition: $(D empty)
306 Complexity: $(BIGOH 1)
315 Reverses SList in-place. Performs no memory allocation.
317 Complexity: $(BIGOH n)
326 auto next = _first._next;
336 Inserts $(D stuff) to the front of the container. $(D stuff) can be a
337 value convertible to $(D T) or a range of objects convertible to $(D
338 T). The stable version behaves the same, but guarantees that ranges
339 iterating over the container are never invalidated.
341 Returns: The number of elements inserted
343 Complexity: $(BIGOH m), where $(D m) is the length of $(D stuff)
345 size_t insertFront(Stuff)(Stuff stuff)
346 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
351 foreach (item; stuff)
353 auto newNode = new Node(null, item);
354 (newRoot ? n._next : newRoot) = newNode;
359 // Last node points to the old root
366 size_t insertFront(Stuff)(Stuff stuff)
367 if (isImplicitlyConvertible!(Stuff, T))
370 auto newRoot = new Node(_first, stuff);
376 alias insert = insertFront;
379 alias stableInsert = insert;
382 alias stableInsertFront = insertFront;
385 Picks one value in an unspecified position in the container, removes
386 it from the container, and returns it. The stable version behaves the same,
387 but guarantees that ranges iterating over the container are never invalidated.
389 Precondition: $(D !empty)
391 Returns: The element removed.
393 Complexity: $(BIGOH 1).
397 import std.algorithm.mutation : move;
399 assert(!empty, "SList.removeAny: List is empty");
400 auto result = move(_first._payload);
401 _first = _first._next;
405 alias stableRemoveAny = removeAny;
408 Removes the value at the front of the container. The stable version
409 behaves the same, but guarantees that ranges iterating over the
410 container are never invalidated.
412 Precondition: $(D !empty)
414 Complexity: $(BIGOH 1).
418 assert(!empty, "SList.removeFront: List is empty");
419 _first = _first._next;
423 alias stableRemoveFront = removeFront;
426 Removes $(D howMany) values at the front or back of the
427 container. Unlike the unparameterized versions above, these functions
428 do not throw if they could not remove $(D howMany) elements. Instead,
429 if $(D howMany > n), all elements are removed. The returned value is
430 the effective number of elements removed. The stable version behaves
431 the same, but guarantees that ranges iterating over the container are
434 Returns: The number of elements removed
436 Complexity: $(BIGOH howMany * log(n)).
438 size_t removeFront(size_t howMany)
441 while (_first && result < howMany)
443 _first = _first._next;
450 alias stableRemoveFront = removeFront;
453 Inserts $(D stuff) after range $(D r), which must be a range
454 previously extracted from this container. Given that all ranges for a
455 list end at the end of the list, this function essentially appends to
456 the list and uses $(D r) as a potentially fast way to reach the last
457 node in the list. Ideally $(D r) is positioned near or at the last
460 $(D stuff) can be a value convertible to $(D T) or a range of objects
461 convertible to $(D T). The stable version behaves the same, but
462 guarantees that ranges iterating over the container are never
465 Returns: The number of values inserted.
467 Complexity: $(BIGOH k + m), where $(D k) is the number of elements in
468 $(D r) and $(D m) is the length of $(D stuff).
472 auto sl = SList!string(["a", "b", "d"]);
473 sl.insertAfter(sl[], "e"); // insert at the end (slowest)
474 assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"]));
475 sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b"
476 assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"]));
480 size_t insertAfter(Stuff)(Range r, Stuff stuff)
486 return insertFront(stuff);
489 auto n = findLastNode(r._head);
491 auto result = tmp.insertFront(stuff);
492 n._next = tmp._first;
497 Similar to $(D insertAfter) above, but accepts a range bounded in
498 count. This is important for ensuring fast insertions in the middle of
499 the list. For fast insertions after a specified position $(D r), use
500 $(D insertAfter(take(r, 1), stuff)). The complexity of that operation
501 only depends on the number of elements in $(D stuff).
503 Precondition: $(D r.original.empty || r.maxLength > 0)
505 Returns: The number of values inserted.
507 Complexity: $(BIGOH k + m), where $(D k) is the number of elements in
508 $(D r) and $(D m) is the length of $(D stuff).
510 size_t insertAfter(Stuff)(Take!Range r, Stuff stuff)
512 auto orig = r.source;
515 // Inserting after a null range counts as insertion to the
517 return insertFront(stuff);
520 // Find the last valid element in the range
521 foreach (i; 1 .. r.maxLength)
523 if (!orig._head._next) break;
529 tmp._first = orig._head._next;
530 auto result = tmp.insertFront(stuff);
531 orig._head._next = tmp._first;
536 alias stableInsertAfter = insertAfter;
539 Removes a range from the list in linear time.
541 Returns: An empty range.
543 Complexity: $(BIGOH n)
545 Range linearRemove(Range r)
552 auto n = findNode(_root, r._head);
558 Removes a $(D Take!Range) from the list in linear time.
560 Returns: A range comprehending the elements after the removed range.
562 Complexity: $(BIGOH n)
564 Range linearRemove(Take!Range r)
566 auto orig = r.source;
567 // We have something to remove here
568 if (orig._head == _first)
570 // remove straight from the head of the list
571 for (; !r.empty; r.popFront())
579 // Nothing to remove, return the range itself
582 // Remove from somewhere in the middle of the list
584 auto n1 = findNode(_root, orig._head);
585 auto n2 = findLastNode(orig._head, r.maxLength);
587 return Range(n1._next);
591 alias stableLinearRemove = linearRemove;
596 import std.algorithm.comparison : equal;
598 auto a = SList!int(5);
603 assert(equal(a[], [2, 1, 5]));
604 assert(equal(b[], [2, 1, 5]));
606 assert(equal(a[], [2, 1, 9]));
607 assert(equal(b[], [2, 1, 9]));
612 auto s = SList!int(1, 2, 3);
613 auto n = s.findLastNode(s._root);
614 assert(n && n._payload == 3);
619 import std.range.primitives;
620 auto s = SList!int(1, 2, 5, 10);
621 assert(walkLength(s[]) == 4);
626 import std.range : take;
627 auto src = take([0, 1, 2, 3], 3);
628 auto s = SList!int(src);
629 assert(s == SList!int(0, 1, 2));
634 auto a = SList!int();
635 auto b = SList!int();
642 auto a = SList!int(1, 2, 3);
643 auto b = SList!int(4, 5, 6);
645 assert(c == SList!int(1, 2, 3, 4, 5, 6));
650 auto a = SList!int(1, 2, 3);
653 assert(c == SList!int(1, 2, 3, 4, 5, 6));
658 auto a = SList!int(1, 2, 3);
660 assert(c == SList!int(1, 2, 3, 4));
665 auto a = SList!int(2, 3, 4);
667 assert(b == SList!int(1, 2, 3, 4));
673 auto b = SList!int(4, 5, 6);
675 assert(c == SList!int(1, 2, 3, 4, 5, 6));
680 auto s = SList!int(1, 2, 3, 4);
681 s.insertFront([ 42, 43 ]);
682 assert(s == SList!int(42, 43, 1, 2, 3, 4));
687 auto s = SList!int(1, 2, 3);
688 assert(s.removeAny() == 1);
689 assert(s == SList!int(2, 3));
690 assert(s.stableRemoveAny() == 2);
691 assert(s == SList!int(3));
696 import std.algorithm.comparison : equal;
698 auto s = SList!int(1, 2, 3);
700 assert(equal(s[], [2, 3]));
701 s.stableRemoveFront();
702 assert(equal(s[], [3]));
707 auto s = SList!int(1, 2, 3, 4, 5, 6, 7);
708 assert(s.removeFront(3) == 3);
709 assert(s == SList!int(4, 5, 6, 7));
714 auto a = SList!int(1, 2, 3);
715 auto b = SList!int(1, 2, 3);
716 assert(a.insertAfter(a[], b[]) == 3);
721 import std.range : take;
722 auto s = SList!int(1, 2, 3, 4);
723 auto r = take(s[], 2);
724 assert(s.insertAfter(r, 5) == 1);
725 assert(s == SList!int(1, 2, 5, 3, 4));
730 import std.algorithm.comparison : equal;
731 import std.range : take;
733 // insertAfter documentation example
734 auto sl = SList!string(["a", "b", "d"]);
735 sl.insertAfter(sl[], "e"); // insert at the end (slowest)
736 assert(equal(sl[], ["a", "b", "d", "e"]));
737 sl.insertAfter(take(sl[], 2), "c"); // insert after "b"
738 assert(equal(sl[], ["a", "b", "c", "d", "e"]));
743 import std.range.primitives;
744 auto s = SList!int(1, 2, 3, 4, 5);
747 auto r1 = s.linearRemove(r);
748 assert(s == SList!int(1, 2, 3));
754 auto s = SList!int(1, 2, 3, 4, 5);
756 auto r1 = s.linearRemove(r);
757 assert(s == SList!int());
763 import std.algorithm.comparison : equal;
766 auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
769 auto r1 = take(r, 4);
770 assert(equal(r1, [4, 5, 6, 7]));
771 auto r2 = s.linearRemove(r1);
772 assert(s == SList!int(1, 2, 3, 8, 9, 10));
773 assert(equal(r2, [8, 9, 10]));
778 import std.range.primitives;
779 auto lst = SList!int(1, 5, 42, 9);
781 assert(lst.front == 1);
782 assert(walkLength(lst[]) == 4);
784 auto lst2 = lst ~ [ 1, 2, 3 ];
785 assert(walkLength(lst2[]) == 7);
787 auto lst3 = lst ~ [ 7 ];
788 assert(walkLength(lst3[]) == 5);
793 auto s = make!(SList!int)(1, 2, 3);
808 auto s = SList!int([1, 2, 3]);
809 s.front = 5; //test frontAssign
810 assert(s.front == 5);
812 r.front = 1; //test frontAssign
813 assert(r.front == 1);
820 s.insertAfter(s[], 1);
821 assert(s.front == 1);
839 import std.algorithm.comparison : equal;
841 auto s = SList!int([1, 2, 3]);
842 assert(s[].equal([1, 2, 3]));
845 assert(s[].equal([3, 2, 1]));