Use std searching and iteration
This commit is contained in:
parent
2c21dc3429
commit
b62cbb0647
@ -11,7 +11,7 @@
|
|||||||
* All algorithms in this module are lazy, they request the next element of the
|
* All algorithms in this module are lazy, they request the next element of the
|
||||||
* original range on demand.
|
* original range on demand.
|
||||||
*
|
*
|
||||||
* Copyright: Eugene Wissner 2018-2020.
|
* Copyright: Eugene Wissner 2018-2021.
|
||||||
* 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)
|
||||||
@ -20,554 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
module tanya.algorithm.iteration;
|
module tanya.algorithm.iteration;
|
||||||
|
|
||||||
import std.algorithm.comparison;
|
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
import tanya.memory.lifetime;
|
import tanya.memory.lifetime;
|
||||||
import tanya.meta.trait;
|
import tanya.meta.trait;
|
||||||
import tanya.meta.transform;
|
import tanya.meta.transform;
|
||||||
import tanya.range;
|
import tanya.range;
|
||||||
|
|
||||||
// These predicates are used to help preserve `const` and `inout` for
|
|
||||||
// ranges built on other ranges.
|
|
||||||
|
|
||||||
private enum hasInoutFront(T) = is(typeof((inout ref T a) => a.front));
|
|
||||||
private enum hasInoutBack(T) = is(typeof((inout ref T a) => a.back));
|
|
||||||
private enum hasInoutIndex(T) = is(typeof((inout ref T a, size_t i) => a[i]));
|
|
||||||
|
|
||||||
private enum hasConstEmpty(T) = is(typeof(((const T* a) => (*a).empty)(null)) : bool);
|
|
||||||
private enum hasConstLength(T) = is(typeof(((const T* a) => (*a).length)(null)) : size_t);
|
|
||||||
private enum hasConstSave(T) = is(typeof(((const T* a) => (*a).save())(null)) : T);
|
|
||||||
private enum hasConstSlice(T) = is(typeof(((const T* a) => (*a)[0 .. $])(null)) : T);
|
|
||||||
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
// Test the definitions.
|
|
||||||
static assert(hasInoutFront!string);
|
|
||||||
static assert(hasInoutBack!string);
|
|
||||||
static assert(hasInoutIndex!string);
|
|
||||||
static assert(hasConstEmpty!string);
|
|
||||||
static assert(hasConstLength!string);
|
|
||||||
static assert(hasConstSave!string);
|
|
||||||
static assert(hasConstSlice!string);
|
|
||||||
|
|
||||||
// Test that Take propagates const/inout correctly.
|
|
||||||
alias TakeString = Take!(string, false);
|
|
||||||
static assert(hasInoutFront!TakeString);
|
|
||||||
static assert(hasInoutBack!TakeString);
|
|
||||||
static assert(hasInoutIndex!TakeString);
|
|
||||||
static assert(hasConstEmpty!TakeString);
|
|
||||||
static assert(hasConstLength!TakeString);
|
|
||||||
static assert(hasConstSave!TakeString);
|
|
||||||
static assert(hasConstSlice!TakeString);
|
|
||||||
|
|
||||||
// Test that Retro propagates const/inout correctly.
|
|
||||||
alias RetroString = Retro!string;
|
|
||||||
static assert(hasInoutFront!RetroString);
|
|
||||||
static assert(hasInoutBack!RetroString);
|
|
||||||
static assert(hasInoutIndex!RetroString);
|
|
||||||
static assert(hasConstEmpty!RetroString);
|
|
||||||
static assert(hasConstLength!RetroString);
|
|
||||||
static assert(hasConstSave!RetroString);
|
|
||||||
static assert(hasConstSlice!RetroString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct Take(R, bool exactly)
|
|
||||||
{
|
|
||||||
private R source;
|
|
||||||
size_t length_;
|
|
||||||
|
|
||||||
@disable this();
|
|
||||||
|
|
||||||
private this(R source, size_t length)
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
static if (!exactly && hasLength!R)
|
|
||||||
{
|
|
||||||
this.length_ = min(source.length, length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.length_ = length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin(`@property auto ref front() ` ~ (hasInoutFront!R ? `inout ` : ``) ~
|
|
||||||
`in (!empty)
|
|
||||||
{
|
|
||||||
return this.source.front;
|
|
||||||
}`);
|
|
||||||
|
|
||||||
void popFront()
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.popFront();
|
|
||||||
--this.length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin(`@property bool empty() ` ~ (exactly || isInfinite!R || hasConstEmpty!R ? `const ` : ``) ~
|
|
||||||
`{
|
|
||||||
static if (exactly || isInfinite!R)
|
|
||||||
{
|
|
||||||
return length == 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return this.length_ == 0 || this.source.empty;
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
static if (exactly || hasLength!R)
|
|
||||||
{
|
|
||||||
@property size_t length() const
|
|
||||||
{
|
|
||||||
return this.length_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (hasAssignableElements!R)
|
|
||||||
{
|
|
||||||
@property void front(ref ElementType!R value)
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.front = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property void front(ElementType!R value)
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.front = move(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (isForwardRange!R)
|
|
||||||
{
|
|
||||||
mixin(`typeof(this) save() ` ~ (hasConstSave!R ? `const ` : ``) ~
|
|
||||||
`{
|
|
||||||
return typeof(this)(this.source.save(), length);
|
|
||||||
}`);
|
|
||||||
}
|
|
||||||
static if (isRandomAccessRange!R)
|
|
||||||
{
|
|
||||||
mixin(`@property auto ref back() ` ~ (hasInoutBack!R ? `inout ` : ``) ~
|
|
||||||
`in (!empty)
|
|
||||||
{
|
|
||||||
return this.source[this.length - 1];
|
|
||||||
}`);
|
|
||||||
|
|
||||||
void popBack()
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
--this.length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin(`auto ref opIndex(size_t i) ` ~ (hasInoutIndex!R ? `inout ` : ``) ~
|
|
||||||
`in (i < length)
|
|
||||||
{
|
|
||||||
return this.source[i];
|
|
||||||
}`);
|
|
||||||
|
|
||||||
static if (hasAssignableElements!R)
|
|
||||||
{
|
|
||||||
@property void back(ref ElementType!R value)
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source[length - 1] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property void back(ElementType!R value)
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source[length - 1] = move(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void opIndexAssign(ref ElementType!R value, size_t i)
|
|
||||||
in (i < length)
|
|
||||||
{
|
|
||||||
this.source[i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opIndexAssign(ElementType!R value, size_t i)
|
|
||||||
in (i < length)
|
|
||||||
{
|
|
||||||
this.source[i] = move(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (!exactly && hasSlicing!R)
|
|
||||||
{
|
|
||||||
static if (is(typeof(length))) alias opDollar = length;
|
|
||||||
|
|
||||||
mixin(`auto opSlice(size_t i, size_t j) ` ~ (hasConstSlice!R ? `const ` : ``) ~
|
|
||||||
`in (i <= j)
|
|
||||||
in (j <= length)
|
|
||||||
{
|
|
||||||
return typeof(this)(this.source[i .. j], length);
|
|
||||||
}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
version (unittest) static assert(isInputRange!Take);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes $(D_PARAM n) elements from $(D_PARAM range).
|
|
||||||
*
|
|
||||||
* If $(D_PARAM range) doesn't have $(D_PARAM n) elements, the resulting range
|
|
||||||
* spans all elements of $(D_PARAM range).
|
|
||||||
*
|
|
||||||
* $(D_PSYMBOL take) is particulary useful with infinite ranges. You can take
|
|
||||||
` $(B n) elements from such range and pass the result to an algorithm which
|
|
||||||
* expects a finit range.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* R = Type of the adapted range.
|
|
||||||
* range = The range to take the elements from.
|
|
||||||
* n = The number of elements to take.
|
|
||||||
*
|
|
||||||
* Returns: A range containing maximum $(D_PARAM n) first elements of
|
|
||||||
* $(D_PARAM range).
|
|
||||||
*
|
|
||||||
* See_Also: $(D_PSYMBOL takeExactly).
|
|
||||||
*/
|
|
||||||
auto take(R)(R range, size_t n)
|
|
||||||
if (isInputRange!R)
|
|
||||||
{
|
|
||||||
static if (hasSlicing!R && hasLength!R)
|
|
||||||
{
|
|
||||||
if (range.length <= n)
|
|
||||||
return range;
|
|
||||||
else
|
|
||||||
return range[0 .. n];
|
|
||||||
}
|
|
||||||
// Special case: take(take(...), n)
|
|
||||||
else static if (is(Range == Take!(RRange, exact), RRange, bool exact))
|
|
||||||
{
|
|
||||||
if (n > range.length_)
|
|
||||||
n = range.length_;
|
|
||||||
static if (exact)
|
|
||||||
// `take(takeExactly(r, n0), n)` is rewritten `takeExactly(r, min(n0, n))`.
|
|
||||||
return Take!(RRange, true)(range.source, n);
|
|
||||||
else
|
|
||||||
// `take(take(r, n0), n)` is rewritten `take(r, min(n0, n))`.
|
|
||||||
return Take!(RRange, false)(range.source, n);
|
|
||||||
}
|
|
||||||
else static if (isInfinite!R)
|
|
||||||
{
|
|
||||||
// If the range is infinite then `take` is the same as `takeExactly`.
|
|
||||||
return Take!(R, true)(range, n);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Take!(R, false)(range, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
static struct InfiniteRange
|
|
||||||
{
|
|
||||||
private size_t front_ = 1;
|
|
||||||
|
|
||||||
enum bool empty = false;
|
|
||||||
|
|
||||||
@property size_t front() @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
return this.front_;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property void front(size_t i) @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
this.front_ = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void popFront() @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
++this.front_;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t opIndex(size_t i) @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
return this.front_ + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
this.front = i + value;
|
|
||||||
}
|
|
||||||
|
|
||||||
InfiniteRange save() @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto t = InfiniteRange().take(3);
|
|
||||||
assert(t.length == 3);
|
|
||||||
assert(t.front == 1);
|
|
||||||
assert(t.back == 3);
|
|
||||||
|
|
||||||
t.popFront();
|
|
||||||
assert(t.front == 2);
|
|
||||||
assert(t.back == 3);
|
|
||||||
|
|
||||||
t.popBack();
|
|
||||||
assert(t.front == 2);
|
|
||||||
assert(t.back == 2);
|
|
||||||
|
|
||||||
t.popFront();
|
|
||||||
assert(t.empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes exactly $(D_PARAM n) elements from $(D_PARAM range).
|
|
||||||
*
|
|
||||||
* $(D_PARAM range) must have at least $(D_PARAM n) elements.
|
|
||||||
*
|
|
||||||
* $(D_PSYMBOL takeExactly) is particulary useful with infinite ranges. You can
|
|
||||||
` take $(B n) elements from such range and pass the result to an algorithm
|
|
||||||
* which expects a finit range.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* R = Type of the adapted range.
|
|
||||||
* range = The range to take the elements from.
|
|
||||||
* n = The number of elements to take.
|
|
||||||
*
|
|
||||||
* Returns: A range containing $(D_PARAM n) first elements of $(D_PARAM range).
|
|
||||||
*
|
|
||||||
* See_Also: $(D_PSYMBOL take).
|
|
||||||
*/
|
|
||||||
auto takeExactly(R)(R range, size_t n)
|
|
||||||
if (isInputRange!R)
|
|
||||||
{
|
|
||||||
static if (hasSlicing!R)
|
|
||||||
{
|
|
||||||
return range[0 .. n];
|
|
||||||
}
|
|
||||||
// Special case: takeExactly(take(range, ...), n) is takeExactly(range, n)
|
|
||||||
else static if (is(Range == Take!(RRange, exact), RRange, bool exact))
|
|
||||||
{
|
|
||||||
assert(n <= range.length_);
|
|
||||||
return Take!(RRange, true)(range.source, n);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Take!(R, true)(range, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
static struct InfiniteRange
|
|
||||||
{
|
|
||||||
private size_t front_ = 1;
|
|
||||||
|
|
||||||
enum bool empty = false;
|
|
||||||
|
|
||||||
@property size_t front() @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
return this.front_;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property void front(size_t i) @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
this.front_ = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void popFront() @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
++this.front_;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t opIndex(size_t i) @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
return this.front_ + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
this.front = i + value;
|
|
||||||
}
|
|
||||||
|
|
||||||
InfiniteRange save() @nogc nothrow pure @safe
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto t = InfiniteRange().takeExactly(3);
|
|
||||||
assert(t.length == 3);
|
|
||||||
assert(t.front == 1);
|
|
||||||
assert(t.back == 3);
|
|
||||||
|
|
||||||
t.popFront();
|
|
||||||
assert(t.front == 2);
|
|
||||||
assert(t.back == 3);
|
|
||||||
|
|
||||||
t.popBack();
|
|
||||||
assert(t.front == 2);
|
|
||||||
assert(t.back == 2);
|
|
||||||
|
|
||||||
t.popFront();
|
|
||||||
assert(t.empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse-access-order range returned by `retro`.
|
|
||||||
private struct Retro(Range)
|
|
||||||
{
|
|
||||||
Range source;
|
|
||||||
|
|
||||||
@disable this();
|
|
||||||
|
|
||||||
private this(Range source) @safe
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin(`Retro save() ` ~ (hasConstSave!Range ? `const ` : ``) ~
|
|
||||||
`{
|
|
||||||
return Retro(source.save());
|
|
||||||
}`);
|
|
||||||
|
|
||||||
mixin(`@property auto ref front() ` ~ (hasInoutBack!Range ? `inout ` : ``) ~
|
|
||||||
`in (!empty)
|
|
||||||
{
|
|
||||||
return this.source.back;
|
|
||||||
}`);
|
|
||||||
|
|
||||||
void popFront()
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.popBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin(`@property auto ref back() ` ~ (hasInoutFront!Range ? `inout ` : ``) ~
|
|
||||||
`in (!empty)
|
|
||||||
{
|
|
||||||
return this.source.front;
|
|
||||||
}`);
|
|
||||||
|
|
||||||
void popBack()
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.popFront();
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin(`@property bool empty() ` ~ (hasConstEmpty!Range ? `const ` : ``) ~
|
|
||||||
`{
|
|
||||||
return this.source.empty;
|
|
||||||
}`);
|
|
||||||
|
|
||||||
static if (hasLength!Range)
|
|
||||||
{
|
|
||||||
mixin(`@property size_t length() ` ~ (hasConstLength!Range ? `const ` : ``) ~
|
|
||||||
`{
|
|
||||||
return this.source.length;
|
|
||||||
}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (isRandomAccessRange!Range && hasLength!Range)
|
|
||||||
{
|
|
||||||
mixin(`auto ref opIndex(size_t i) ` ~ (hasInoutIndex!Range ? `inout ` : ``) ~
|
|
||||||
`in (i < length)
|
|
||||||
{
|
|
||||||
return this.source[$ - ++i];
|
|
||||||
}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (hasLength!Range && hasSlicing!Range)
|
|
||||||
{
|
|
||||||
alias opDollar = length;
|
|
||||||
|
|
||||||
mixin(`auto opSlice(size_t i, size_t j) ` ~ (hasConstSlice!Range ? `const ` : ``) ~
|
|
||||||
`in (i <= j)
|
|
||||||
in (j <= length)
|
|
||||||
{
|
|
||||||
return typeof(this)(this.source[$-j .. $-i]);
|
|
||||||
}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (hasAssignableElements!Range)
|
|
||||||
{
|
|
||||||
@property void front(ref ElementType!Range value)
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.back = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property void front(ElementType!Range value)
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.back = move(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@property void back(ref ElementType!Range value)
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.front = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property void back(ElementType!Range value)
|
|
||||||
in (!empty)
|
|
||||||
{
|
|
||||||
this.source.front = move(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (isRandomAccessRange!Range && hasLength!Range)
|
|
||||||
{
|
|
||||||
void opIndexAssign(ref ElementType!Range value, size_t i)
|
|
||||||
in (i < length)
|
|
||||||
{
|
|
||||||
this.source[$ - ++i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opIndexAssign(ElementType!Range value, size_t i)
|
|
||||||
in (i < length)
|
|
||||||
{
|
|
||||||
this.source[$ - ++i] = move(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
version (unittest) static assert(isBidirectionalRange!Retro);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterates a bidirectional range backwards.
|
|
||||||
*
|
|
||||||
* If $(D_PARAM Range) is a random-access range as well, the resulting range
|
|
||||||
* is a random-access range too.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* Range = Bidirectional range type.
|
|
||||||
* range = Bidirectional range.
|
|
||||||
*
|
|
||||||
* Returns: Bidirectional range with the elements order reversed.
|
|
||||||
*/
|
|
||||||
auto retro(Range)(Range range)
|
|
||||||
if (isBidirectionalRange!Range)
|
|
||||||
{
|
|
||||||
// Special case: retro(retro(range)) is range
|
|
||||||
static if (is(Range == Retro!RRange, RRange))
|
|
||||||
return range.source;
|
|
||||||
else
|
|
||||||
return Retro!Range(range);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
const int[3] given = [1, 2, 3];
|
|
||||||
const int[3] expected = [3, 2, 1];
|
|
||||||
|
|
||||||
auto actual = retro(given[]);
|
|
||||||
|
|
||||||
assert(actual.length == expected.length);
|
|
||||||
assert(!actual.empty);
|
|
||||||
assert(equal(actual, expected[]));
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct SingletonByValue(E)
|
private struct SingletonByValue(E)
|
||||||
{
|
{
|
||||||
private Nullable!E element;
|
private Nullable!E element;
|
||||||
@ -719,61 +177,9 @@ auto singleton(E)(return ref E element)
|
|||||||
/**
|
/**
|
||||||
* Accumulates all elements of a range using a function.
|
* Accumulates all elements of a range using a function.
|
||||||
*
|
*
|
||||||
* $(D_PSYMBOL foldl) takes a function, an input range and the initial value.
|
* $(D_PSYMBOL foldr) takes a function, a bidirectional range and the initial
|
||||||
* The function takes this initial value and the first element of the range (in
|
* value. The function takes this initial value and the first element of the
|
||||||
* this order), puts them together and returns the result. The return
|
* range (in this order), puts them together and returns the result. The return
|
||||||
* type of the function should be the same as the type of the initial value.
|
|
||||||
* This is than repeated for all the remaining elements of the range, whereby
|
|
||||||
* the value returned by the passed function is used at the place of the
|
|
||||||
* initial value.
|
|
||||||
*
|
|
||||||
* $(D_PSYMBOL foldl) accumulates from left to right.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* F = Callable accepting the accumulator and a range element.
|
|
||||||
*/
|
|
||||||
template foldl(F...)
|
|
||||||
if (F.length == 1)
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Params:
|
|
||||||
* R = Input range type.
|
|
||||||
* T = Type of the accumulated value.
|
|
||||||
* range = Input range.
|
|
||||||
* init = Initial value.
|
|
||||||
*
|
|
||||||
* Returns: Accumulated value.
|
|
||||||
*/
|
|
||||||
auto foldl(R, T)(R range, auto ref T init)
|
|
||||||
if (isInputRange!R && !isInfinite!R)
|
|
||||||
{
|
|
||||||
if (range.empty)
|
|
||||||
{
|
|
||||||
return init;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto acc = F[0](init, getAndPopFront(range));
|
|
||||||
return foldl(range, acc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
int[3] range = [1, 2, 3];
|
|
||||||
const actual = foldl!((acc, x) => acc + x)(range[], 0);
|
|
||||||
|
|
||||||
assert(actual == 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accumulates all elements of a range using a function.
|
|
||||||
*
|
|
||||||
* $(D_PSYMBOL foldr) takes a function, an input range and the initial value.
|
|
||||||
* The function takes this initial value and the first element of the range (in
|
|
||||||
* this order), puts them together and returns the result. The return
|
|
||||||
* type of the function should be the same as the type of the initial value.
|
* type of the function should be the same as the type of the initial value.
|
||||||
* This is than repeated for all the remaining elements of the range, whereby
|
* This is than repeated for all the remaining elements of the range, whereby
|
||||||
* the value returned by the passed function is used at the place of the
|
* the value returned by the passed function is used at the place of the
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
/**
|
/**
|
||||||
* Collection of generic algorithms.
|
* Collection of generic algorithms.
|
||||||
*
|
*
|
||||||
* Copyright: Eugene Wissner 2017-2020.
|
* Copyright: Eugene Wissner 2017-2021.
|
||||||
* 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)
|
||||||
@ -16,4 +16,3 @@ module tanya.algorithm;
|
|||||||
|
|
||||||
public import tanya.algorithm.iteration;
|
public import tanya.algorithm.iteration;
|
||||||
public import tanya.algorithm.mutation;
|
public import tanya.algorithm.mutation;
|
||||||
public import tanya.algorithm.searching;
|
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searching algorithms.
|
|
||||||
*
|
|
||||||
* Copyright: Eugene Wissner 2018-2020.
|
|
||||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
|
||||||
* Mozilla Public License, v. 2.0).
|
|
||||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
|
||||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/searching.d,
|
|
||||||
* tanya/algorithm/searching.d)
|
|
||||||
*/
|
|
||||||
module tanya.algorithm.searching;
|
|
||||||
|
|
||||||
import tanya.range;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Counts the elements in an input range.
|
|
||||||
*
|
|
||||||
* If $(D_PARAM R) has length, $(D_PSYMBOL count) returns it, otherwise it
|
|
||||||
* iterates over the range and counts the elements.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* R = Input range type.
|
|
||||||
* range = Input range.
|
|
||||||
*
|
|
||||||
* Returns: $(D_PARAM range) length.
|
|
||||||
*/
|
|
||||||
size_t count(R)(R range)
|
|
||||||
if (isInputRange!R)
|
|
||||||
{
|
|
||||||
static if (hasLength!R)
|
|
||||||
{
|
|
||||||
return range.length;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t counter;
|
|
||||||
for (; !range.empty; range.popFront(), ++counter)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
int[3] array;
|
|
||||||
assert(count(array) == 3);
|
|
||||||
}
|
|
@ -5,7 +5,7 @@
|
|||||||
/**
|
/**
|
||||||
* Single-dimensioned array.
|
* Single-dimensioned array.
|
||||||
*
|
*
|
||||||
* Copyright: Eugene Wissner 2016-2020.
|
* Copyright: Eugene Wissner 2016-2021.
|
||||||
* 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)
|
||||||
@ -16,7 +16,7 @@ module tanya.container.array;
|
|||||||
|
|
||||||
import core.checkedint;
|
import core.checkedint;
|
||||||
import std.algorithm.comparison;
|
import std.algorithm.comparison;
|
||||||
import tanya.algorithm.iteration;
|
import std.algorithm.iteration;
|
||||||
import tanya.algorithm.mutation;
|
import tanya.algorithm.mutation;
|
||||||
import tanya.memory.allocator;
|
import tanya.memory.allocator;
|
||||||
import tanya.memory.lifetime;
|
import tanya.memory.lifetime;
|
||||||
@ -676,7 +676,7 @@ struct Array(T)
|
|||||||
{
|
{
|
||||||
reserve(length + el.length);
|
reserve(length + el.length);
|
||||||
}
|
}
|
||||||
return foldl!((acc, e) => acc + insertBack(e))(el, 0U);
|
return fold!((acc, e) => acc + insertBack(e))(el, size_t.init);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
/**
|
/**
|
||||||
* Hash table.
|
* Hash table.
|
||||||
*
|
*
|
||||||
* Copyright: Eugene Wissner 2018-2020.
|
* Copyright: Eugene Wissner 2018-2021.
|
||||||
* 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)
|
||||||
@ -14,7 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
module tanya.container.hashtable;
|
module tanya.container.hashtable;
|
||||||
|
|
||||||
import tanya.algorithm.iteration;
|
import std.algorithm.iteration;
|
||||||
import tanya.algorithm.mutation;
|
import tanya.algorithm.mutation;
|
||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.entry;
|
import tanya.container.entry;
|
||||||
@ -719,7 +719,7 @@ if (isHashFunction!(hasher, Key))
|
|||||||
size_t insert(R)(scope R range)
|
size_t insert(R)(scope R range)
|
||||||
if (isForwardRange!R && is(ElementType!R == KeyValue) && !isInfinite!R)
|
if (isForwardRange!R && is(ElementType!R == KeyValue) && !isInfinite!R)
|
||||||
{
|
{
|
||||||
return foldl!((acc, x) => acc + insert(x))(range, 0U);
|
return fold!((acc, x) => acc + insert(x))(range, size_t.init);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* This module contains singly-linked ($(D_PSYMBOL SList)) and doubly-linked
|
* This module contains singly-linked ($(D_PSYMBOL SList)) and doubly-linked
|
||||||
* ($(D_PSYMBOL DList)) lists.
|
* ($(D_PSYMBOL DList)) lists.
|
||||||
*
|
*
|
||||||
* Copyright: Eugene Wissner 2016-2020.
|
* Copyright: Eugene Wissner 2016-2021.
|
||||||
* 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)
|
||||||
@ -16,7 +16,7 @@
|
|||||||
module tanya.container.list;
|
module tanya.container.list;
|
||||||
|
|
||||||
import std.algorithm.comparison;
|
import std.algorithm.comparison;
|
||||||
import tanya.algorithm.iteration;
|
import std.algorithm.iteration;
|
||||||
import tanya.container.entry;
|
import tanya.container.entry;
|
||||||
import tanya.memory.allocator;
|
import tanya.memory.allocator;
|
||||||
import tanya.memory.lifetime;
|
import tanya.memory.lifetime;
|
||||||
@ -1693,7 +1693,7 @@ struct DList(T)
|
|||||||
&& isImplicitlyConvertible!(ElementType!R, T))
|
&& isImplicitlyConvertible!(ElementType!R, T))
|
||||||
in (checkRangeBelonging(r))
|
in (checkRangeBelonging(r))
|
||||||
{
|
{
|
||||||
return foldl!((acc, x) => acc + insertAfter(r, x))(el, 0U);
|
return fold!((acc, x) => acc + insertAfter(r, x))(el, size_t.init);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -1342,7 +1342,7 @@ struct String
|
|||||||
///
|
///
|
||||||
@nogc pure @safe unittest
|
@nogc pure @safe unittest
|
||||||
{
|
{
|
||||||
import tanya.algorithm.searching : count;
|
import std.algorithm.searching : count;
|
||||||
|
|
||||||
auto s = String("Из пословицы слова не выкинешь.");
|
auto s = String("Из пословицы слова не выкинешь.");
|
||||||
|
|
||||||
|
@ -1,85 +1,13 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
module tanya.algorithm.tests.iteration;
|
module tanya.algorithm.tests.iteration;
|
||||||
|
|
||||||
import tanya.algorithm.iteration;
|
import tanya.algorithm.iteration;
|
||||||
import tanya.range;
|
import tanya.range;
|
||||||
import tanya.test.stub;
|
import tanya.test.stub;
|
||||||
|
|
||||||
// length is unknown when taking from a range without length
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
static struct R
|
|
||||||
{
|
|
||||||
mixin InputRangeStub;
|
|
||||||
}
|
|
||||||
auto actual = take(R(), 100);
|
|
||||||
|
|
||||||
static assert(!hasLength!(typeof(actual)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes minimum length if the range length > n
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
auto range = take(cast(int[]) null, 8);
|
|
||||||
assert(range.length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
const int[9] range = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
||||||
{
|
|
||||||
auto slice = take(range[], 8)[1 .. 3];
|
|
||||||
|
|
||||||
assert(slice.length == 2);
|
|
||||||
assert(slice.front == 2);
|
|
||||||
assert(slice.back == 3);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto slice = takeExactly(range[], 8)[1 .. 3];
|
|
||||||
|
|
||||||
assert(slice.length == 2);
|
|
||||||
assert(slice.front == 2);
|
|
||||||
assert(slice.back == 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elements are accessible in reverse order
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
const int[3] given = [1, 2, 3];
|
|
||||||
auto actual = retro(given[]);
|
|
||||||
|
|
||||||
assert(actual.back == given[].front);
|
|
||||||
assert(actual[0] == 3);
|
|
||||||
assert(actual[2] == 1);
|
|
||||||
|
|
||||||
actual.popBack();
|
|
||||||
assert(actual.back == 2);
|
|
||||||
assert(actual[1] == 2);
|
|
||||||
|
|
||||||
// Check slicing.
|
|
||||||
auto slice = retro(given[])[1 .. $];
|
|
||||||
assert(slice.length == 2 && slice.front == 2 && slice.back == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elements can be assigned
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
int[4] given = [1, 2, 3, 4];
|
|
||||||
auto actual = retro(given[]);
|
|
||||||
|
|
||||||
actual.front = 5;
|
|
||||||
assert(given[].back == 5);
|
|
||||||
|
|
||||||
actual.back = 8;
|
|
||||||
assert(given[].front == 8);
|
|
||||||
|
|
||||||
actual[2] = 10;
|
|
||||||
assert(given[1] == 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Singleton range is bidirectional and random-access
|
// Singleton range is bidirectional and random-access
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
module tanya.algorithm.tests.searching;
|
|
||||||
|
|
||||||
import tanya.algorithm.searching;
|
|
||||||
import tanya.test.stub;
|
|
||||||
|
|
||||||
@nogc nothrow pure @safe unittest
|
|
||||||
{
|
|
||||||
@Count(3)
|
|
||||||
static struct Range
|
|
||||||
{
|
|
||||||
mixin InputRangeStub!int;
|
|
||||||
}
|
|
||||||
assert(count(Range()) == 3);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user