SList.insertFront for ranges
This commit is contained in:
parent
3c23aca6a6
commit
c6a99b114e
@ -3,414 +3,454 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Linked list.
|
||||||
|
*
|
||||||
* Copyright: Eugene Wissner 2016-2017.
|
* Copyright: Eugene Wissner 2016-2017.
|
||||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||||
* Mozilla Public License, v. 2.0).
|
* Mozilla Public License, v. 2.0).
|
||||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||||
*/
|
*/
|
||||||
module tanya.container.list;
|
module tanya.container.list;
|
||||||
|
|
||||||
import std.algorithm.comparison;
|
import std.algorithm.comparison;
|
||||||
|
import std.algorithm.mutation;
|
||||||
import std.algorithm.searching;
|
import std.algorithm.searching;
|
||||||
|
import std.range.primitives;
|
||||||
import std.traits;
|
import std.traits;
|
||||||
import tanya.container.entry;
|
import tanya.container.entry;
|
||||||
import tanya.memory;
|
import tanya.memory;
|
||||||
|
|
||||||
private struct Range(E)
|
private struct Range(E)
|
||||||
if (__traits(isSame, TemplateOf!E, SEntry))
|
if (__traits(isSame, TemplateOf!E, SEntry))
|
||||||
{
|
{
|
||||||
private alias T = typeof(E.content);
|
private alias T = typeof(E.content);
|
||||||
|
|
||||||
private E* head;
|
private E* head;
|
||||||
|
|
||||||
private this(E* head)
|
private this(E* head)
|
||||||
{
|
{
|
||||||
this.head = head;
|
this.head = head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property Range save()
|
@property Range save()
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property size_t length() const
|
@property size_t length() const
|
||||||
{
|
{
|
||||||
return count(opIndex());
|
return count(opIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
@property bool empty() const
|
@property bool empty() const
|
||||||
{
|
{
|
||||||
return head is null;
|
return head is null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property ref inout(T) front() inout
|
@property ref inout(T) front() inout
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(!empty);
|
assert(!empty);
|
||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
return head.content;
|
return head.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
void popFront()
|
void popFront()
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(!empty);
|
assert(!empty);
|
||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
head = head.next;
|
head = head.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
Range opIndex()
|
Range opIndex()
|
||||||
{
|
{
|
||||||
return typeof(return)(head);
|
return typeof(return)(head);
|
||||||
}
|
}
|
||||||
|
|
||||||
Range!(const E) opIndex() const
|
Range!(const E) opIndex() const
|
||||||
{
|
{
|
||||||
return typeof(return)(head);
|
return typeof(return)(head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singly-linked list.
|
* Singly-linked list.
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
* T = Content type.
|
* T = Content type.
|
||||||
*/
|
*/
|
||||||
struct SList(T)
|
struct SList(T)
|
||||||
{
|
{
|
||||||
private alias Entry = SEntry!T;
|
private alias Entry = SEntry!T;
|
||||||
|
|
||||||
// 0th element of the list.
|
// 0th element of the list.
|
||||||
private Entry* head;
|
private Entry* head;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all elements from the list.
|
* Removes all elements from the list.
|
||||||
*/
|
*/
|
||||||
~this()
|
~this()
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all contents from the list.
|
* Removes all contents from the list.
|
||||||
*/
|
*/
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
while (!empty)
|
while (!empty)
|
||||||
{
|
{
|
||||||
removeFront();
|
removeFront();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
SList!int l;
|
SList!int l;
|
||||||
|
|
||||||
l.insertFront(8);
|
l.insertFront(8);
|
||||||
l.insertFront(5);
|
l.insertFront(5);
|
||||||
l.clear();
|
l.clear();
|
||||||
assert(l.empty);
|
assert(l.empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns: First element.
|
* Returns: First element.
|
||||||
*/
|
*/
|
||||||
@property ref inout(T) front() inout
|
@property ref inout(T) front() inout
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(!empty);
|
assert(!empty);
|
||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
return head.content;
|
return head.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a new element at the beginning.
|
* Inserts a new element at the beginning.
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
* x = New element.
|
* R = Type of the inserted value(s).
|
||||||
*/
|
* x = New element.
|
||||||
void insertFront(ref T x)
|
*
|
||||||
{
|
* Returns: The number of elements inserted.
|
||||||
auto temp = allocator.make!Entry;
|
*/
|
||||||
|
size_t insertFront(R)(ref R x) @trusted
|
||||||
temp.content = x;
|
if (isImplicitlyConvertible!(R, T))
|
||||||
temp.next = head;
|
{
|
||||||
head = temp;
|
head = allocator.make!Entry(x, head);
|
||||||
}
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
void insertFront(T x)
|
size_t insertFront(R)(R x) @trusted
|
||||||
{
|
if (isImplicitlyConvertible!(R, T))
|
||||||
insertFront(x);
|
{
|
||||||
}
|
auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
|
||||||
|
|
||||||
/// Ditto.
|
x.moveEmplace(temp.content);
|
||||||
alias insert = insertFront;
|
temp.next = head;
|
||||||
|
|
||||||
///
|
head = temp;
|
||||||
unittest
|
return 1;
|
||||||
{
|
}
|
||||||
SList!int l;
|
|
||||||
|
|
||||||
l.insertFront(8);
|
/// Ditto.
|
||||||
assert(l.front == 8);
|
size_t insertFront(R)(R el)
|
||||||
l.insertFront(9);
|
if (!isInfinite!R
|
||||||
assert(l.front == 9);
|
&& isInputRange!R
|
||||||
}
|
&& isImplicitlyConvertible!(ElementType!R, T))
|
||||||
|
{
|
||||||
|
size_t retLength;
|
||||||
|
foreach (e; el)
|
||||||
|
{
|
||||||
|
retLength += insertFront(e);
|
||||||
|
}
|
||||||
|
return retLength;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/// Ditto.
|
||||||
* Returns: How many elements are in the list.
|
size_t insertFront(size_t R)(T[R] el)
|
||||||
*/
|
{
|
||||||
@property size_t length() const
|
return insertFront!(T[])(el[]);
|
||||||
{
|
}
|
||||||
return count(opIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
/// Ditto.
|
||||||
unittest
|
alias insert = insertFront;
|
||||||
{
|
|
||||||
SList!int l;
|
|
||||||
|
|
||||||
l.insertFront(8);
|
///
|
||||||
l.insertFront(9);
|
@nogc @safe unittest
|
||||||
assert(l.length == 2);
|
{
|
||||||
l.removeFront();
|
SList!int l1;
|
||||||
assert(l.length == 1);
|
|
||||||
l.removeFront();
|
|
||||||
assert(l.length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
assert(l1.insertFront(8) == 1);
|
||||||
* Comparison for equality.
|
assert(l1.front == 8);
|
||||||
*
|
assert(l1.insertFront(9) == 1);
|
||||||
* Params:
|
assert(l1.front == 9);
|
||||||
* that = The list to compare with.
|
|
||||||
*
|
|
||||||
* Returns: $(D_KEYWORD true) if the lists are equal, $(D_KEYWORD false)
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
bool opEquals()(auto ref typeof(this) that) @trusted
|
|
||||||
{
|
|
||||||
return equal(opIndex(), that[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ditto.
|
SList!int l2;
|
||||||
bool opEquals()(in auto ref typeof(this) that) const @trusted
|
assert(l2.insertFront([25, 30, 15]) == 3);
|
||||||
{
|
assert(l2.front == 15);
|
||||||
return equal(opIndex(), that[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
l2.insertFront(l1[]);
|
||||||
unittest
|
assert(l2.length == 5);
|
||||||
{
|
assert(l2.front == 8);
|
||||||
SList!int l1, l2;
|
}
|
||||||
|
|
||||||
l1.insertFront(8);
|
/**
|
||||||
l1.insertFront(9);
|
* Returns: How many elements are in the list.
|
||||||
l2.insertFront(8);
|
*/
|
||||||
l2.insertFront(10);
|
@property size_t length() const
|
||||||
assert(l1 != l2);
|
{
|
||||||
|
return count(opIndex());
|
||||||
|
}
|
||||||
|
|
||||||
l1.removeFront();
|
///
|
||||||
assert(l1 != l2);
|
unittest
|
||||||
|
{
|
||||||
|
SList!int l;
|
||||||
|
|
||||||
l2.removeFront();
|
l.insertFront(8);
|
||||||
assert(l1 == l2);
|
l.insertFront(9);
|
||||||
|
assert(l.length == 2);
|
||||||
|
l.removeFront();
|
||||||
|
assert(l.length == 1);
|
||||||
|
l.removeFront();
|
||||||
|
assert(l.length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
l1.removeFront();
|
/**
|
||||||
assert(l1 != l2);
|
* Comparison for equality.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* that = The list to compare with.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD true) if the lists are equal, $(D_KEYWORD false)
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
bool opEquals()(auto ref typeof(this) that) @trusted
|
||||||
|
{
|
||||||
|
return equal(opIndex(), that[]);
|
||||||
|
}
|
||||||
|
|
||||||
l2.removeFront();
|
/// Ditto.
|
||||||
assert(l1 == l2);
|
bool opEquals()(in auto ref typeof(this) that) const @trusted
|
||||||
}
|
{
|
||||||
|
return equal(opIndex(), that[]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
///
|
||||||
* Returns: $(D_KEYWORD true) if the list is empty.
|
unittest
|
||||||
*/
|
{
|
||||||
@property bool empty() const
|
SList!int l1, l2;
|
||||||
{
|
|
||||||
return head is null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
l1.insertFront(8);
|
||||||
* Returns the first element and moves to the next one.
|
l1.insertFront(9);
|
||||||
*
|
l2.insertFront(8);
|
||||||
* Returns: The first element.
|
l2.insertFront(10);
|
||||||
*
|
assert(l1 != l2);
|
||||||
* Precondition: $(D_INLINECODE !empty)
|
|
||||||
*/
|
|
||||||
void removeFront()
|
|
||||||
in
|
|
||||||
{
|
|
||||||
assert(!empty);
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
auto n = head.next;
|
|
||||||
|
|
||||||
allocator.dispose(head);
|
l1.removeFront();
|
||||||
head = n;
|
assert(l1 != l2);
|
||||||
}
|
|
||||||
|
|
||||||
///
|
l2.removeFront();
|
||||||
unittest
|
assert(l1 == l2);
|
||||||
{
|
|
||||||
SList!int l;
|
|
||||||
|
|
||||||
l.insertFront(8);
|
l1.removeFront();
|
||||||
l.insertFront(9);
|
assert(l1 != l2);
|
||||||
assert(l.front == 9);
|
|
||||||
l.removeFront();
|
|
||||||
assert(l.front == 8);
|
|
||||||
l.removeFront();
|
|
||||||
assert(l.empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
l2.removeFront();
|
||||||
* Removes $(D_PARAM howMany) elements from the list.
|
assert(l1 == l2);
|
||||||
*
|
}
|
||||||
* Unlike $(D_PSYMBOL removeFront()), this method doesn't fail, if it could not
|
|
||||||
* remove $(D_PARAM howMany) elements. Instead, if $(D_PARAM howMany) is
|
|
||||||
* greater than the list length, all elements are removed.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* howMany = How many elements should be removed.
|
|
||||||
*
|
|
||||||
* Returns: The number of elements removed.
|
|
||||||
*/
|
|
||||||
size_t removeFront(in size_t howMany)
|
|
||||||
out (removed)
|
|
||||||
{
|
|
||||||
assert(removed <= howMany);
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
for (; i < howMany && !empty; ++i)
|
|
||||||
{
|
|
||||||
removeFront();
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ditto.
|
/**
|
||||||
alias remove = removeFront;
|
* Returns: $(D_KEYWORD true) if the list is empty.
|
||||||
|
*/
|
||||||
|
@property bool empty() const
|
||||||
|
{
|
||||||
|
return head is null;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
/**
|
||||||
unittest
|
* Returns the first element and moves to the next one.
|
||||||
{
|
*
|
||||||
SList!int l;
|
* Returns: The first element.
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE !empty)
|
||||||
|
*/
|
||||||
|
void removeFront()
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(!empty);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
auto n = head.next;
|
||||||
|
|
||||||
l.insertFront(8);
|
allocator.dispose(head);
|
||||||
l.insertFront(5);
|
head = n;
|
||||||
l.insertFront(4);
|
}
|
||||||
assert(l.removeFront(0) == 0);
|
|
||||||
assert(l.removeFront(2) == 2);
|
|
||||||
assert(l.removeFront(3) == 1);
|
|
||||||
assert(l.removeFront(3) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
///
|
||||||
* $(D_KEYWORD foreach) iteration.
|
unittest
|
||||||
*
|
{
|
||||||
* Params:
|
SList!int l;
|
||||||
* dg = $(D_KEYWORD foreach) body.
|
|
||||||
*
|
|
||||||
* Returns: The value returned from $(D_PARAM dg).
|
|
||||||
*/
|
|
||||||
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (auto pos = head; pos; pos = pos.next, ++i)
|
l.insertFront(8);
|
||||||
{
|
l.insertFront(9);
|
||||||
result = dg(i, pos.content);
|
assert(l.front == 9);
|
||||||
|
l.removeFront();
|
||||||
|
assert(l.front == 8);
|
||||||
|
l.removeFront();
|
||||||
|
assert(l.empty);
|
||||||
|
}
|
||||||
|
|
||||||
if (result != 0)
|
/**
|
||||||
{
|
* Removes $(D_PARAM howMany) elements from the list.
|
||||||
return result;
|
*
|
||||||
}
|
* Unlike $(D_PSYMBOL removeFront()), this method doesn't fail, if it could not
|
||||||
}
|
* remove $(D_PARAM howMany) elements. Instead, if $(D_PARAM howMany) is
|
||||||
return result;
|
* greater than the list length, all elements are removed.
|
||||||
}
|
*
|
||||||
|
* Params:
|
||||||
|
* howMany = How many elements should be removed.
|
||||||
|
*
|
||||||
|
* Returns: The number of elements removed.
|
||||||
|
*/
|
||||||
|
size_t removeFront(in size_t howMany)
|
||||||
|
out (removed)
|
||||||
|
{
|
||||||
|
assert(removed <= howMany);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (; i < howMany && !empty; ++i)
|
||||||
|
{
|
||||||
|
removeFront();
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
int opApply(scope int delegate(ref T) @nogc dg)
|
alias remove = removeFront;
|
||||||
{
|
|
||||||
int result;
|
|
||||||
|
|
||||||
for (auto pos = head; pos; pos = pos.next)
|
///
|
||||||
{
|
unittest
|
||||||
result = dg(pos.content);
|
{
|
||||||
|
SList!int l;
|
||||||
|
|
||||||
if (result != 0)
|
l.insertFront(8);
|
||||||
{
|
l.insertFront(5);
|
||||||
return result;
|
l.insertFront(4);
|
||||||
}
|
assert(l.removeFront(0) == 0);
|
||||||
}
|
assert(l.removeFront(2) == 2);
|
||||||
return result;
|
assert(l.removeFront(3) == 1);
|
||||||
}
|
assert(l.removeFront(3) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
///
|
/**
|
||||||
unittest
|
* $(D_KEYWORD foreach) iteration.
|
||||||
{
|
*
|
||||||
SList!int l;
|
* Params:
|
||||||
|
* dg = $(D_KEYWORD foreach) body.
|
||||||
|
*
|
||||||
|
* Returns: The value returned from $(D_PARAM dg).
|
||||||
|
*/
|
||||||
|
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
l.insertFront(5);
|
for (auto pos = head; pos; pos = pos.next, ++i)
|
||||||
l.insertFront(4);
|
{
|
||||||
l.insertFront(9);
|
result = dg(i, pos.content);
|
||||||
foreach (i, e; l)
|
|
||||||
{
|
|
||||||
assert(i != 0 || e == 9);
|
|
||||||
assert(i != 1 || e == 4);
|
|
||||||
assert(i != 2 || e == 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Range!Entry opIndex()
|
if (result != 0)
|
||||||
{
|
{
|
||||||
return typeof(return)(head);
|
return result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Range!(const Entry) opIndex() const
|
/// Ditto.
|
||||||
{
|
int opApply(scope int delegate(ref T) @nogc dg)
|
||||||
return typeof(return)(head);
|
{
|
||||||
}
|
int result;
|
||||||
|
|
||||||
mixin DefaultAllocator;
|
for (auto pos = head; pos; pos = pos.next)
|
||||||
|
{
|
||||||
|
result = dg(pos.content);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
SList!int l;
|
||||||
|
|
||||||
|
l.insertFront(5);
|
||||||
|
l.insertFront(4);
|
||||||
|
l.insertFront(9);
|
||||||
|
foreach (i, e; l)
|
||||||
|
{
|
||||||
|
assert(i != 0 || e == 9);
|
||||||
|
assert(i != 1 || e == 4);
|
||||||
|
assert(i != 2 || e == 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Range!Entry opIndex()
|
||||||
|
{
|
||||||
|
return typeof(return)(head);
|
||||||
|
}
|
||||||
|
|
||||||
|
Range!(const Entry) opIndex() const
|
||||||
|
{
|
||||||
|
return typeof(return)(head);
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin DefaultAllocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
SList!int l;
|
SList!int l;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
l.insertFront(5);
|
l.insertFront(5);
|
||||||
l.insertFront(4);
|
l.insertFront(4);
|
||||||
l.insertFront(9);
|
l.insertFront(9);
|
||||||
foreach (e; l)
|
foreach (e; l)
|
||||||
{
|
{
|
||||||
assert(i != 0 || e == 9);
|
assert(i != 0 || e == 9);
|
||||||
assert(i != 1 || e == 4);
|
assert(i != 1 || e == 4);
|
||||||
assert(i != 2 || e == 5);
|
assert(i != 2 || e == 5);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
assert(i == 3);
|
assert(i == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
interface Stuff
|
interface Stuff
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
static assert(is(SList!Stuff));
|
static assert(is(SList!Stuff));
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user