SList.insertFront for ranges

This commit is contained in:
Eugen Wissner 2017-03-01 19:23:54 +01:00
parent 3c23aca6a6
commit c6a99b114e
2 changed files with 1885 additions and 1845 deletions

View File

@ -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