summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2019-02-25 09:27:03 +0100
committerEugen Wissner <belka@caraus.de>2019-02-25 09:27:03 +0100
commit8fd0452cd021219d809627146dc6651c8bc36901 (patch)
tree43f7384977cd30e848232078622c52bdadc74014 /source
parentdf99ea45f286430205dbc68d4437f123a9efb3ea (diff)
downloadtanya-8fd0452cd021219d809627146dc6651c8bc36901.tar.gz
algorithm.iteration: Add singleton()
... iterating over a single value.
Diffstat (limited to 'source')
-rw-r--r--source/tanya/algorithm/iteration.d207
1 files changed, 207 insertions, 0 deletions
diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d
index 8281ae1..7eae9cd 100644
--- a/source/tanya/algorithm/iteration.d
+++ b/source/tanya/algorithm/iteration.d
@@ -22,7 +22,10 @@ module tanya.algorithm.iteration;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
+import tanya.meta.trait;
+import tanya.meta.transform;
import tanya.range;
+import tanya.typecons;
version (unittest) import tanya.test.stub;
private struct Take(R, bool exactly)
@@ -646,3 +649,207 @@ if (isBidirectionalRange!Range)
actual[2] = 10;
assert(given[1] == 10);
}
+
+private struct SingletonByValue(E)
+{
+ private Option!E element;
+
+ @disable this();
+
+ private this(U)(ref U element)
+ if (is(U == E))
+ {
+ this.element = move(element);
+ }
+
+ private this(U)(ref U element)
+ if (is(Unqual!U == Option!(Unqual!E)) || is(Unqual!U == Option!(const E)))
+ {
+ if (!element.isNothing)
+ {
+ this.element = element.get;
+ }
+ }
+
+ @property ref inout(E) front() inout
+ in (!empty)
+ {
+ return this.element.get;
+ }
+
+ alias back = front;
+
+ void popFront()
+ in (!empty)
+ {
+ this.element.reset();
+ }
+
+ alias popBack = popFront;
+
+ @property bool empty() const
+ {
+ return this.element.isNothing;
+ }
+
+ @property size_t length() const
+ {
+ return !this.element.isNothing;
+ }
+
+ auto save()
+ {
+ return SingletonByValue!E(this.element);
+ }
+
+ auto save() const
+ {
+ return SingletonByValue!(const E)(this.element);
+ }
+
+ ref inout(E) opIndex(size_t i) inout
+ in (!empty)
+ in (i == 0)
+ {
+ return this.element.get;
+ }
+}
+
+private struct SingletonByRef(E)
+{
+ private E* element;
+
+ @disable this();
+
+ private this(return ref E element) @trusted
+ {
+ this.element = &element;
+ }
+
+ @property ref inout(E) front() inout return
+ in (!empty)
+ {
+ return *this.element;
+ }
+
+ alias back = front;
+
+ void popFront()
+ in (!empty)
+ {
+ this.element = null;
+ }
+
+ alias popBack = popFront;
+
+ @property bool empty() const
+ {
+ return this.element is null;
+ }
+
+ @property size_t length() const
+ {
+ return this.element !is null;
+ }
+
+ auto save() return
+ {
+ return typeof(this)(*this.element);
+ }
+
+ auto save() const return
+ {
+ return SingletonByRef!(const E)(*this.element);
+ }
+
+ ref inout(E) opIndex(size_t i) inout return
+ in (!empty)
+ in (i == 0)
+ {
+ return *this.element;
+ }
+}
+
+/**
+ * Creates a bidirectional and random-access range with the single element
+ * $(D_PARAM element).
+ *
+ * If $(D_PARAM element) is passed by value the resulting range stores it
+ * internally. If $(D_PARAM element) is passed by reference, the resulting
+ * range keeps only a pointer to the element.
+ *
+ * Params:
+ * E = Element type.
+ * element = Element.
+ *
+ * Returns: A range with one element.
+ */
+auto singleton(E)(return E element)
+if (isMutable!E)
+{
+ return SingletonByValue!E(element);
+}
+
+/// ditto
+auto singleton(E)(return ref E element)
+{
+ return SingletonByRef!E(element);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ auto singleChar = singleton('a');
+
+ assert(singleChar.length == 1);
+ assert(singleChar.front == 'a');
+
+ singleChar.popFront();
+ assert(singleChar.empty);
+}
+
+// Singleton range is bidirectional and random-access
+@nogc nothrow pure @safe unittest
+{
+ static assert(isBidirectionalRange!(typeof(singleton('a'))));
+ static assert(isRandomAccessRange!(typeof(singleton('a'))));
+
+ assert({ char a; return isBidirectionalRange!(typeof(singleton(a))); });
+ assert({ char a; return isRandomAccessRange!(typeof(singleton(a))); });
+}
+
+@nogc nothrow pure @safe unittest
+{
+ char a = 'a';
+ auto single = singleton(a);
+
+ assert(single.front == 'a');
+ assert(single.back == 'a');
+ assert(single[0] == 'a');
+ assert(single.length == 1);
+ assert(!single.empty);
+}
+
+// popFront makes SingletonByRef empty
+@nogc nothrow pure @safe unittest
+{
+ char a = 'a';
+ auto single = singleton(a);
+
+ single.popFront();
+ assert(single.empty);
+ assert(single.length == 0);
+ assert(single.empty);
+}
+
+// popBack makes SingletonByRef empty
+@nogc nothrow pure @safe unittest
+{
+ char a = 'b';
+ auto single = singleton(a);
+
+ single.popBack();
+ assert(single.empty);
+ assert(single.length == 0);
+ assert(single.empty);
+}