Moved split from string to array, made one more pass through array

This commit is contained in:
Andrei Alexandrescu 2011-01-19 06:44:46 +00:00
parent 907a34e7f2
commit 331dd3a489
5 changed files with 538 additions and 488 deletions

View file

@ -269,19 +269,20 @@ unittest
fibsSquares.popFront; fibsSquares.popFront;
assert(fibsSquares.front == 9); assert(fibsSquares.front == 9);
auto repeatMap = map!"a"(repeat(1)); auto repeatMap = map!"a"(repeat(1));
static assert(isInfinite!(typeof(repeatMap))); static assert(isInfinite!(typeof(repeatMap)));
auto intRange = map!"a"([1,2,3]);
static assert(isRandomAccessRange!(typeof(intRange)));
foreach(DummyType; AllDummyRanges)
{
DummyType d;
auto m = map!"a * a"(d);
auto intRange = map!"a"([1,2,3]); static assert(propagatesRangeType!(typeof(m), DummyType));
static assert(isRandomAccessRange!(typeof(intRange))); assert(equal(m, [1,4,9,16,25,36,49,64,81,100]));
}
foreach(DummyType; AllDummyRanges) {
DummyType d;
auto m = map!"a * a"(d);
static assert(propagatesRangeType!(typeof(m), DummyType));
assert(equal(m, [1,4,9,16,25,36,49,64,81,100]));
}
} }
// reduce // reduce
@ -1453,9 +1454,9 @@ public:
/// Ditto /// Ditto
Splitter!(Range, Separator) Splitter!(Range, Separator)
splitter(Range, Separator)(Range r, Separator s) splitter(Range, Separator)(Range r, Separator s)
if (is(typeof(ElementType!(Range).init == ElementType!(Separator).init)) if (is(typeof(ElementType!Range.init == ElementType!Separator.init))
|| ||
is(typeof(ElementType!(Range).init == Separator.init)) is(typeof(ElementType!Range.init == Separator.init))
) )
{ {
return typeof(return)(r, s); return typeof(return)(r, s);
@ -1528,7 +1529,7 @@ Splits a range using another range as a separator. This can be used
with any range type, but is most popular with string types. with any range type, but is most popular with string types.
*/ */
struct Splitter(Range, Separator) struct Splitter(Range, Separator)
if (is(typeof(Range.init.front == Separator.init.front))) if (is(typeof(Range.init.front == Separator.init.front) : bool))
{ {
private: private:
Range _input; Range _input;
@ -1721,7 +1722,8 @@ struct Splitter(alias isTerminator, Range,
Slice = Select!(is(typeof(Range.init[0 .. 1])), Slice = Select!(is(typeof(Range.init[0 .. 1])),
Range, Range,
ElementType!(Range)[])) ElementType!(Range)[]))
if(!is(isTerminator)) { if(!is(isTerminator))
{
private Range _input; private Range _input;
private size_t _end; private size_t _end;
private alias unaryFun!isTerminator _isTerminator; private alias unaryFun!isTerminator _isTerminator;
@ -1867,25 +1869,87 @@ assert(equal(joiner(["Mary", "has", "a", "little", "lamb"], "..."),
"Mary...has...a...little...lamb")); "Mary...has...a...little...lamb"));
---- ----
*/ */
auto joiner(Range, Separator)(Range r, Separator sep) auto joiner(RoR, Separator)(RoR r, Separator sep)
if (isForwardRange!RoR && isInputRange!(ElementType!RoR)
&& isForwardRange!Separator
&& is(ElementType!Separator : ElementType!(ElementType!RoR)))
{ {
struct Result static struct Result
{ {
private: private RoR _items;
Range _items; private ElementType!RoR _current;
Separator _sep, _currentSep; private Separator _sep, _currentSep;
public:
@property bool empty() private void useSeparator()
{ {
return _items.empty; assert(_currentSep.empty && _current.empty,
"joiner: internal error");
if (_sep.empty)
{
// Advance to the next range in the
// input
//_items.popFront();
for (;; _items.popFront())
{
if (_items.empty) return;
if (!_items.front.empty) break;
}
_current = _items.front;
_items.popFront();
}
else
{
// Must make sure something is coming after the
// separator - it's a separator, not a terminator!
if (_items.empty) return;
_currentSep = _sep.save;
assert(!_currentSep.empty);
}
} }
@property ElementType!(ElementType!Range) front()
private void useItem()
{
assert(_currentSep.empty && _current.empty,
"joiner: internal error");
// Use the input
if (_items.empty) return;
_current = _items.front;
_items.popFront();
if (!_current.empty)
{
return;
}
// No data in the current item - toggle to use the
// separator
useSeparator();
}
this(RoR items, Separator sep)
{
_items = items;
_sep = sep;
useItem();
// We need the separator if the input has at least two
// elements
if (_current.empty && _items.empty)
{
// Vacate the whole thing
_currentSep = _currentSep.init;
}
}
@property auto empty()
{
return _current.empty && _currentSep.empty;
}
@property ElementType!(ElementType!RoR) front()
{ {
assert(!empty);
if (!_currentSep.empty) return _currentSep.front; if (!_currentSep.empty) return _currentSep.front;
if (!_items.front.empty) return _items.front.front; assert(!_current.empty);
assert(false); return _current.front;
} }
void popFront() void popFront()
{ {
assert(!empty); assert(!empty);
@ -1893,47 +1957,33 @@ auto joiner(Range, Separator)(Range r, Separator sep)
if (!_currentSep.empty) if (!_currentSep.empty)
{ {
_currentSep.popFront(); _currentSep.popFront();
if (_currentSep.empty) if (!_currentSep.empty) return;
{ useItem();
// Explore the next item in the range
if (_items.front.empty)
{
// Null item, will write a new separator
_items.popFront();
if (!_items.empty)
{
_currentSep = _sep.save;
}
}
}
} }
else else
{ {
// we're using the range // we're using the range
assert(!_items.empty && !_items.front.empty); _current.popFront();
_items.front.popFront(); if (!_current.empty) return;
if (_items.front.empty) useSeparator();
{
_items.popFront();
if (!_items.empty)
{
_currentSep = _sep.save;
}
}
} }
assert(empty || !_currentSep.empty || !_items.front.empty); // We need to re-prime the range
} }
}
auto result = Result(r, sep); static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
if (!r.empty && r.front.empty)
{
result._items.popFront();
if (!result.empty)
{ {
result._currentSep = result._sep.save; @property auto save()
{
Result copy;
copy._items = _items.save;
copy._current = _current.save;
copy._sep = _sep.save;
copy._currentSep = _currentSep.save;
return copy;
}
} }
} }
return result; return Result(r, sep);
} }
unittest unittest
@ -1941,9 +1991,9 @@ unittest
debug(std_algorithm) scope(success) debug(std_algorithm) scope(success)
writeln("unittest @", __FILE__, ":", __LINE__, " done."); writeln("unittest @", __FILE__, ":", __LINE__, " done.");
static assert(isInputRange!(typeof(joiner([""], "")))); static assert(isInputRange!(typeof(joiner([""], ""))));
static assert(!isForwardRange!(typeof(joiner([""], "")))); static assert(isForwardRange!(typeof(joiner([""], ""))));
assert(equal(joiner([""], "xyz"), "")); assert(equal(joiner([""], "xyz"), ""), text(joiner([""], "xyz")));
assert(equal(joiner(["", ""], "xyz"), "xyz")); assert(equal(joiner(["", ""], "xyz"), "xyz"), text(joiner(["", ""], "xyz")));
assert(equal(joiner(["", "abc"], "xyz"), "xyzabc")); assert(equal(joiner(["", "abc"], "xyz"), "xyzabc"));
assert(equal(joiner(["abc", ""], "xyz"), "abcxyz")); assert(equal(joiner(["abc", ""], "xyz"), "abcxyz"));
assert(equal(joiner(["abc", "def"], "xyz"), "abcxyzdef")); assert(equal(joiner(["abc", "def"], "xyz"), "abcxyzdef"));
@ -1951,6 +2001,82 @@ unittest
"Mary...has...a...little...lamb")); "Mary...has...a...little...lamb"));
} }
auto joiner(RoR)(RoR r)
if (isInputRange!RoR && isInputRange!(ElementType!RoR))
{
static struct Result
{
private:
RoR _items;
ElementType!RoR _current;
void prime()
{
for (;; _items.popFront())
{
if (_items.empty) return;
if (!_items.front.empty) break;
}
_current = _items.front;
_items.popFront();
}
public:
this(RoR r)
{
_items = r;
prime();
}
static if (isInfinite!(ElementType!RoR))
{
enum bool empty = false;
}
else
{
@property auto empty()
{
return _current.empty;
}
}
@property auto ref front()
{
assert(!empty);
return _current.front;
}
void popFront()
{
assert(!_current.empty);
_current.popFront();
if (_current.empty) prime();
}
static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
{
@property auto save()
{
Result copy;
copy._items = _items.save;
copy._current = _current.save;
return copy;
}
}
}
return Result(r);
}
unittest
{
debug(std_algorithm) scope(success)
writeln("unittest @", __FILE__, ":", __LINE__, " done.");
static assert(isInputRange!(typeof(joiner([""]))));
static assert(isForwardRange!(typeof(joiner([""]))));
assert(equal(joiner([""]), ""));
assert(equal(joiner(["", ""]), ""));
assert(equal(joiner(["", "abc"]), "abc"));
assert(equal(joiner(["abc", ""]), "abc"));
assert(equal(joiner(["abc", "def"]), "abcdef"));
assert(equal(joiner(["Mary", "has", "a", "little", "lamb"]),
"Maryhasalittlelamb"));
assert(equal(joiner(std.range.repeat("abc", 3)), "abcabcabc"));
}
// uniq // uniq
/** /**
Iterates unique consecutive elements of the given range (functionality Iterates unique consecutive elements of the given range (functionality

View file

@ -1,31 +1,24 @@
// Written in the D programming language. // Written in the D programming language.
/** /**
Copyright: Copyright Andrei Alexandrescu 2008 - 2009. Copyright: Copyright Andrei Alexandrescu 2008-.
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(WEB erdani.org, Andrei Alexandrescu) Authors: $(WEB erdani.org, Andrei Alexandrescu)
*/ */
/*
Copyright Andrei Alexandrescu 2008 - 2009.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
*/
module std.array; module std.array;
import std.c.stdio;
import core.memory; import core.memory;
import std.algorithm, std.conv, std.encoding, std.exception, std.range, import std.algorithm, std.conv, std.ctype, std.encoding, std.exception,
std.string, std.traits, std.typecons, std.utf; std.intrinsic, std.range, std.string, std.traits, std.typecons, std.utf;
private import std.c.string : memcpy; import std.c.string : memcpy;
private import std.intrinsic : bsr; version(unittest) import std.stdio, std.typetuple;
version(unittest) private import std.stdio, std.typetuple;
/** /**
Returns a newly-allocated dynamic array consisting of a copy of the input Returns a newly-allocated dynamic array consisting of a copy of the
range, static array, dynamic array, or class or struct with an $(D opApply) input range, static array, dynamic array, or class or struct with an
function $(D r). Note that narrow strings are handled $(D opApply) function $(D r). Note that narrow strings are handled as
as a special case in an overload. a special case in an overload.
Example: Example:
@ -85,27 +78,16 @@ This is handled as a special case and always returns a $(D dchar[]),
$(D const(dchar)[]), or $(D immutable(dchar)[]) depending on the constness of $(D const(dchar)[]), or $(D immutable(dchar)[]) depending on the constness of
the input. the input.
*/ */
ElementType!String[] array(String)(String str) if(isNarrowString!String) ElementType!String[] array(String)(String str) if (isNarrowString!String)
{ {
static if(is(typeof(return) == immutable)) return to!(typeof(return))(str);
{
return to!(immutable(dchar)[])(str);
}
else static if(is(typeof(return) == const))
{
return to!(const(dchar)[])(str);
}
else
{
return to!(dchar[])(str);
}
} }
version(unittest) unittest
{ {
struct TestArray { int x; string toString() { return .to!string(x); } } static struct TestArray { int x; string toString() { return .to!string(x); } }
struct OpAssign static struct OpAssign
{ {
uint num; uint num;
this(uint num) { this.num = num; } this(uint num) { this.num = num; }
@ -115,7 +97,7 @@ version(unittest)
void opAssign(T)(T rhs) { this.num = rhs.num; } void opAssign(T)(T rhs) { this.num = rhs.num; }
} }
struct OpApply static struct OpApply
{ {
int opApply(int delegate(ref int) dg) int opApply(int delegate(ref int) dg)
{ {
@ -129,10 +111,7 @@ version(unittest)
return res; return res;
} }
} }
}
unittest
{
auto a = array([1, 2, 3, 4, 5][]); auto a = array([1, 2, 3, 4, 5][]);
//writeln(a); //writeln(a);
assert(a == [ 1, 2, 3, 4, 5 ]); assert(a == [ 1, 2, 3, 4, 5 ]);
@ -170,12 +149,9 @@ equivalent to $(D empty(array)).
Example: Example:
---- ----
void main() auto a = [ 1, 2, 3 ];
{ assert(!a.empty);
auto a = [ 1, 2, 3 ]; assert(a[3 .. $].empty);
assert(!a.empty);
assert(a[3 .. $].empty);
}
---- ----
*/ */
@ -199,12 +175,9 @@ equivalent to $(D save(array)).
Example: Example:
---- ----
void main() auto a = [ 1, 2, 3 ];
{ auto b = a.save;
auto a = [ 1, 2, 3 ]; assert(b is a);
auto b = a.save;
assert(b is a);
}
---- ----
*/ */
@ -219,44 +192,36 @@ arrays. Due to the fact that nonmember functions can be called with
the first argument using the dot notation, $(D array.popFront) is the first argument using the dot notation, $(D array.popFront) is
equivalent to $(D popFront(array)). equivalent to $(D popFront(array)).
Example: Example:
---- ----
void main() int[] a = [ 1, 2, 3 ];
{ a.popFront;
int[] a = [ 1, 2, 3 ]; assert(a == [ 2, 3 ]);
a.popFront;
assert(a == [ 2, 3 ]);
}
---- ----
*/ */
void popFront(A)(ref A a) void popFront(A)(ref A a)
if(!isNarrowString!A && isDynamicArray!A && isMutable!A && !is(A == void[])) if (!isNarrowString!A && isDynamicArray!A && isMutable!A && !is(A == void[]))
{ {
alias typeof(A[0]) T;
assert(a.length, "Attempting to popFront() past the end of an array of " assert(a.length, "Attempting to popFront() past the end of an array of "
~ T.stringof); ~ typeof(a[0]).stringof);
a = a[1 .. $]; a = a[1 .. $];
} }
unittest unittest
{ {
//@@@BUG 2608@@@ auto a = [ 1, 2, 3 ];
//auto a = [ 1, 2, 3 ]; a.popFront();
int[] a = [ 1, 2, 3 ];
a.popFront;
assert(a == [ 2, 3 ]); assert(a == [ 2, 3 ]);
static assert(!__traits(compiles, popFront!(immutable int[]))); static assert(!__traits(compiles, popFront!(immutable int[])));
static assert(!__traits(compiles, popFront!(void[])));
} }
void popFront(A)(ref A a) void popFront(A)(ref A a)
if(isNarrowString!A && isMutable!A) if (isNarrowString!A && isMutable!A)
{ {
alias typeof(a[0]) T;
assert(a.length, "Attempting to popFront() past the end of an array of " assert(a.length, "Attempting to popFront() past the end of an array of "
~ T.stringof); ~ typeof(a[0]).stringof);
a = a[std.utf.stride(a, 0) .. $]; a = a[std.utf.stride(a, 0) .. $];
} }
@ -283,17 +248,14 @@ equivalent to $(D popBack(array)).
Example: Example:
---- ----
void main() int[] a = [ 1, 2, 3 ];
{ a.popBack();
int[] a = [ 1, 2, 3 ]; assert(a == [ 1, 2 ]);
a.popBack;
assert(a == [ 1, 2 ]);
}
---- ----
*/ */
void popBack(A)(ref A a) void popBack(A)(ref A a)
if(isDynamicArray!A && !isNarrowString!A && isMutable!A && !is(A == void[])) if (isDynamicArray!A && !isNarrowString!A && isMutable!A && !is(A == void[]))
{ {
assert(a.length); assert(a.length);
a = a[0 .. $ - 1]; a = a[0 .. $ - 1];
@ -301,17 +263,15 @@ if(isDynamicArray!A && !isNarrowString!A && isMutable!A && !is(A == void[]))
unittest unittest
{ {
//@@@BUG 2608@@@ auto a = [ 1, 2, 3 ];
//auto a = [ 1, 2, 3 ]; a.popBack();
int[] a = [ 1, 2, 3 ];
a.popBack;
assert(a == [ 1, 2 ]); assert(a == [ 1, 2 ]);
static assert(!__traits(compiles, popBack!(immutable int[]))); static assert(!__traits(compiles, popBack!(immutable int[])));
static assert(!__traits(compiles, popBack!(void[])));
} }
void popBack(A)(ref A a) @trusted void popBack(A)(ref A a)
if(is(A : const(char)[]) && isMutable!A) if (is(A : const(char)[]) && isMutable!A)
{ {
immutable n = a.length; immutable n = a.length;
const p = a.ptr + n; const p = a.ptr + n;
@ -333,7 +293,7 @@ if(is(A : const(char)[]) && isMutable!A)
} }
else else
{ {
assert(false, "Invalid UTF character at end of string"); throw new UtfException("Invalid UTF character at end of string");
} }
} }
@ -342,7 +302,6 @@ unittest
string s = "hello\xE2\x89\xA0"; string s = "hello\xE2\x89\xA0";
s.popBack(); s.popBack();
assert(s == "hello", s); assert(s == "hello", s);
string s3 = "\xE2\x89\xA0"; string s3 = "\xE2\x89\xA0";
auto c = s3.back; auto c = s3.back;
assert(c == cast(dchar)'\u2260'); assert(c == cast(dchar)'\u2260');
@ -352,17 +311,18 @@ unittest
static assert(!__traits(compiles, popBack!(immutable char[]))); static assert(!__traits(compiles, popBack!(immutable char[])));
} }
void popBack(A)(ref A a) @trusted void popBack(A)(ref A a)
if(is(A : const(wchar)[]) && isMutable!A) if (is(A : const(wchar)[]) && isMutable!A)
{ {
assert(a.length); assert(a.length);
if (a.length == 1) if (a.length <= 1) // this is technically == but costs nothing and is safer
{ {
a = a[0 .. 0]; a = a[0 .. 0];
return; return;
} }
immutable c = a[$ - 2]; // We can go commando from here on, we're safe; length is > 1
a = a[0 .. $ - 1 - (c >= 0xD800 && c <= 0xDBFF)]; immutable c = a.ptr[a.length - 2];
a = a.ptr[0 .. a.length - 1 - (c >= 0xD800 && c <= 0xDBFF)];
} }
unittest unittest
@ -383,31 +343,30 @@ equivalent to $(D front(array)).
Example: Example:
---- ----
void main() int[] a = [ 1, 2, 3 ];
{ assert(a.front == 1);
int[] a = [ 1, 2, 3 ];
assert(a.front == 1);
}
---- ----
*/ */
ref typeof(A[0]) front(A)(A a) ref T front(T)(T[] a)
if (is(typeof(A[0])) && !isNarrowString!A && !is(typeof(A[0]) : const(void))) if (!isNarrowString!(T[]) && !is(T[] == void[]))
{ {
assert(a.length, "Attempting to fetch the front of an empty array"); assert(a.length, "Attempting to fetch the front of an empty array");
return a[0]; return a[0];
} }
dchar front(A)(A a) if (is(typeof(A[0])) && isNarrowString!A) dchar front(A)(A a) if (isNarrowString!A)
{ {
assert(a.length, "Attempting to fetch the front of an empty array"); assert(a.length, "Attempting to fetch the front of an empty array");
size_t i = 0; size_t i = 0;
return decode(a, i); return decode(a, i);
} }
/// Ditto unittest
void front(T)(T[] a, T v) if (!isNarrowString!A)
{ {
assert(a.length); a[0] = v; auto a = [ 1, 2 ];
a.front = 4;
assert(a.front == 4);
assert(a == [ 4, 2 ]);
} }
/** /**
@ -418,27 +377,15 @@ equivalent to $(D back(array)).
Example: Example:
---- ----
void main() int[] a = [ 1, 2, 3 ];
{ assert(a.back == 3);
int[] a = [ 1, 2, 3 ];
assert(a.back == 3);
}
---- ----
*/ */
ref typeof(A.init[0]) back(A)(A a) ref typeof(A.init[0]) back(A)(A a)
if (is(typeof(A.init[0])) && !isNarrowString!A if (isDynamicArray!A && !isNarrowString!A
&& !is(typeof(A.init[0]) : const(void))) && !is(typeof(A.init[0]) : const(void)))
{ {
// @@@BUG@@@ The assert below crashes the unittest due to a bug in assert(a.length, "Attempting to fetch the back of an empty array");
// the compiler
version (bug4426)
{
assert(a.length, "Attempting to fetch the back of an empty array");
}
else
{
assert(a.length);
}
return a[$ - 1]; return a[$ - 1];
} }
@ -451,7 +398,7 @@ unittest
} }
dchar back(A)(A a) dchar back(A)(A a)
if (is(typeof(A.init[0])) && isNarrowString!A && a[0].sizeof < 4) if (isDynamicArray!A && isNarrowString!A)
{ {
assert(a.length, "Attempting to fetch the back of an empty array"); assert(a.length, "Attempting to fetch the back of an empty array");
auto n = a.length; auto n = a.length;
@ -459,7 +406,7 @@ if (is(typeof(A.init[0])) && isNarrowString!A && a[0].sizeof < 4)
if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000) if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000)
{ {
--n; --n;
return std.utf.decode(a, n); return decode(a, n);
} }
else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000) else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000)
{ {
@ -527,46 +474,49 @@ unittest
Inserts $(D stuff) in $(D container) at position $(D pos). Inserts $(D stuff) in $(D container) at position $(D pos).
*/ */
void insert(T, Range)(ref T[] array, size_t pos, Range stuff) void insert(T, Range)(ref T[] array, size_t pos, Range stuff)
if (isInputRange!Range && is(ElementType!Range : T))
{ {
static if (is(typeof(stuff[0]))) static if (hasLength!Range)
{ {
// presumably an array // @@@BUG 2130@@@
alias stuff toInsert; // immutable
//assert(!overlap(array, toInsert)); // size_t delta = toInsert.length,
// size_t oldLength = array.length,
// size_t newLength = oldLength + delta;
immutable
delta = stuff.length,
oldLength = array.length,
newLength = oldLength + delta;
// Reallocate the array to make space for new content
array = (cast(T*) core.memory.GC.realloc(array.ptr,
newLength * array[0].sizeof))[0 .. newLength];
assert(array.length == newLength);
// Move data in pos .. pos + stuff.length to the end of the array
foreach_reverse (i; pos .. oldLength)
{
// This will be guaranteed to not throw
move(array[i], array[i + delta]);
}
// Copy stuff into array
copy(stuff, array[pos .. pos + stuff.length]);
} }
else else
{ {
// presumably only one element auto app = appender!(T[])();
auto toInsert = (&stuff)[0 .. 1]; app.put(array[0 .. pos]);
app.put(stuff);
app.put(array[pos .. $]);
array = app.data;
} }
}
// @@@BUG 2130@@@ /// Ditto
// immutable void insert(T)(ref T[] array, size_t pos, T stuff)
// size_t delta = toInsert.length, {
// size_t oldLength = array.length, return insert(array, pos, (&stuff)[0 .. 1]);
// size_t newLength = oldLength + delta;
immutable
delta = toInsert.length,
oldLength = array.length,
newLength = oldLength + delta;
// Reallocate the array to make space for new content
array = (cast(T*) core.memory.GC.realloc(array.ptr,
newLength * array[0].sizeof))[0 .. newLength];
assert(array.length == newLength);
// Move data in pos .. pos + stuff.length to the end of the array
foreach_reverse (i; pos .. oldLength)
{
// This will be guaranteed to not throw
move(array[i], array[i + delta]);
}
// Copy stuff into array
foreach (e; toInsert)
{
array[pos++] = e;
}
} }
unittest unittest
@ -579,7 +529,7 @@ unittest
} }
// @@@ TODO: document this // @@@ TODO: document this
bool sameHead(T)(in T[] lhs, in T[] rhs) pure bool sameHead(T)(in T[] lhs, in T[] rhs)
{ {
return lhs.ptr == rhs.ptr; return lhs.ptr == rhs.ptr;
} }
@ -588,9 +538,9 @@ bool sameHead(T)(in T[] lhs, in T[] rhs)
* Return an array that consists of $(D s) (which must be an input * Return an array that consists of $(D s) (which must be an input
* range) repeated $(D n) times. * range) repeated $(D n) times.
*/ */
S replicate(S)(S s, size_t n) if (isSomeString!S)
S multiply(S)(S s, size_t n) if (isSomeString!S)
{ {
// Optimization for return join(std.range.repeat(s, n));
if (n == 0) if (n == 0)
return S.init; return S.init;
if (n == 1) if (n == 1)
@ -609,102 +559,228 @@ S multiply(S)(S s, size_t n) if (isSomeString!S)
return cast(S) r; return cast(S) r;
} }
ElementType!S[] multiply(S)(S s, size_t n) ElementType!S[] replicate(S)(S s, size_t n)
if (isInputRange!S && !isSomeString!S) if (isInputRange!S && !isSomeString!S)
{ {
if (n == 0) return join(std.range.repeat(s, n));
return null;
static if (hasLength!S)
{
auto r = new Unqual!(typeof(s[0]))[n * s.length];
if (s.length == 1)
r[] = s[0];
else
{
auto len = s.length;
immutable nlen = n * len;
for (size_t i = 0; i < nlen; i += len)
{
copy(s, r[i .. i + len]);
}
}
return cast(typeof(return)) r;
}
else
{
Appender!(ElementType!S) a;
for (; !s.empty; s.popFront())
{
a.put(s.front);
}
return a.data;
}
} }
unittest unittest
{ {
debug(string) printf("array.repeat.unittest\n"); debug(std_array) printf("array.repeat.unittest\n");
foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[]))
{ {
S s; S s;
s = multiply(to!S("1234"), 0); s = replicate(to!S("1234"), 0);
assert(s is null); assert(s is null);
s = multiply(to!S("1234"), 1); s = replicate(to!S("1234"), 1);
assert(cmp(s, "1234") == 0); assert(cmp(s, "1234") == 0);
s = multiply(to!S("1234"), 2); s = replicate(to!S("1234"), 2);
assert(cmp(s, "12341234") == 0); assert(cmp(s, "12341234") == 0);
s = multiply(to!S("1"), 4); s = replicate(to!S("1"), 4);
assert(cmp(s, "1111") == 0); assert(cmp(s, "1111") == 0);
s = multiply(cast(S) null, 4); s = replicate(cast(S) null, 4);
assert(s is null); assert(s is null);
} }
int[] a = [ 1, 2, 3 ]; int[] a = [ 1, 2, 3 ];
assert(multiply(a, 3) == [1, 2, 3, 1, 2, 3, 1, 2, 3]); assert(replicate(a, 3) == [1, 2, 3, 1, 2, 3, 1, 2, 3]);
}
/**************************************
Split $(D s[]) into an array of words, using whitespace as delimiter.
*/
S[] split(S)(S s) if (isSomeString!S)
{
size_t istart;
bool inword = false;
S[] result;
foreach (i; 0 .. s.length)
{
switch (s[i])
{
case ' ': case '\t': case '\f': case '\r': case '\n': case '\v':
if (inword)
{
result ~= s[istart .. i];
inword = false;
}
break;
default:
if (!inword)
{
istart = i;
inword = true;
}
break;
}
}
if (inword)
result ~= s[istart .. $];
return result;
}
unittest
{
foreach (S; TypeTuple!(string, wstring, dstring))
{
debug(string) printf("string.split1\n");
S s = " peter paul\tjerry ";
S[] words;
int i;
words = split(s);
assert(words.length == 3);
i = cmp(words[0], "peter");
assert(i == 0);
i = cmp(words[1], "paul");
assert(i == 0);
i = cmp(words[2], "jerry");
assert(i == 0);
}
}
auto splitter(String)(String s) if (isSomeString!String)
{
return std.algorithm.splitter!isspace(s);
}
unittest
{
auto a = " a bcd ef gh ";
assert(equal(splitter(a), ["", "a", "bcd", "ef", "gh"][]));
a = "";
assert(splitter(a).empty);
}
/**************************************
* Split $(D r) into an array, using $(D delim) as the delimiter.
*/
Unqual!(S1)[] split(S1, S2)(S1 s, S2 delim)
if (isForwardRange!(Unqual!S1) && isForwardRange!S2)
{
Unqual!S1 us = s;
auto app = appender!(Unqual!(S1)[])();
foreach (word; std.algorithm.splitter(us, delim))
{
app.put(word);
}
return app.data;
}
unittest
{
debug(std_array) printf("array.split\n");
foreach (S; TypeTuple!(string, wstring, dstring,
immutable(string), immutable(wstring), immutable(dstring),
char[], wchar[], dchar[],
const(char)[], const(wchar)[], const(dchar)[]))
{
S s = to!S(",peter,paul,jerry,");
int i;
auto words = split(s, ",");
assert(words.length == 5, text(words.length));
i = cmp(words[0], "");
assert(i == 0);
i = cmp(words[1], "peter");
assert(i == 0);
i = cmp(words[2], "paul");
assert(i == 0);
i = cmp(words[3], "jerry");
assert(i == 0);
i = cmp(words[4], "");
assert(i == 0);
auto s1 = s[0 .. s.length - 1]; // lop off trailing ','
words = split(s1, ",");
assert(words.length == 4);
i = cmp(words[3], "jerry");
assert(i == 0);
auto s2 = s1[1 .. s1.length]; // lop off leading ','
words = split(s2, ",");
assert(words.length == 3);
i = cmp(words[0], "peter");
assert(i == 0);
auto s3 = to!S(",,peter,,paul,,jerry,,");
words = split(s3, ",,");
//printf("words.length = %d\n", words.length);
assert(words.length == 5);
i = cmp(words[0], "");
assert(i == 0);
i = cmp(words[1], "peter");
assert(i == 0);
i = cmp(words[2], "paul");
assert(i == 0);
i = cmp(words[3], "jerry");
assert(i == 0);
i = cmp(words[4], "");
assert(i == 0);
auto s4 = s3[0 .. s3.length - 2]; // lop off trailing ',,'
words = split(s4, ",,");
assert(words.length == 4);
i = cmp(words[3], "jerry");
assert(i == 0);
auto s5 = s4[2 .. s4.length]; // lop off leading ',,'
words = split(s5, ",,");
assert(words.length == 3);
i = cmp(words[0], "peter");
assert(i == 0);
}
} }
/******************************************** /********************************************
* Concatenate all the ranges in $(D ror) together into one array; * Concatenate all the ranges in $(D ror) together into one array;
* use $(D sep) as the separator. * use $(D sep) as the separator if present, otherwise none.
*/ */
ElementEncodingType!(ElementType!RoR)[]
ElementType!RoR join(RoR, R)(RoR ror, R sep) join(RoR, R)(RoR ror, R sep)
if (isInputRange!RoR && isInputRange!(typeof(ror.front)) if (isInputRange!RoR && isInputRange!(ElementType!RoR) && isForwardRange!R)
&& !(isSomeString!(typeof(ror.front)) && isSomeString!R))
{ {
return copy(joiner(ror, sep), appender!(ElementType!RoR)()).data; if (ror.empty) return typeof(return).init;
auto iter = joiner(ror, sep);
static if (isForwardRange!RoR && hasLength!RoR
&& (hasLength!(ElementType!RoR) || isSomeString!(ElementType!RoR))
&& hasLength!R)
{
immutable resultLen = reduce!"a + b.length"(cast(size_t) 0, ror.save)
+ sep.length * (ror.length - 1);
auto result = new ElementEncodingType!(ElementType!RoR)[resultLen];
copy(iter, result);
return result;
}
else
{
return copy(iter, appender!(typeof(return))).data;
}
} }
// Specialization for strings /// Ditto
typeof(RoR.init[0]) join(RoR, R)(RoR words, R sep) ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror)
if (isSomeString!(typeof(words[0])) && isSomeString!R) if (isInputRange!RoR && isInputRange!(ElementType!RoR))
{ {
if (!words.length) return null; auto iter = joiner(ror);
auto sep2 = to!(typeof(return))(sep); static if (hasLength!RoR && hasLength!(ElementType!RoR))
immutable seplen = sep2.length;
size_t len = (words.length - 1) * seplen;
foreach (i; 0 .. words.length)
len += words[i].length;
auto result = new Unqual!(typeof(words.front[0]))[len];
size_t j;
foreach (i; 0 .. words.length)
{ {
if (i > 0) immutable resultLen = reduce!"a + b.length"(cast(size_t) 0, ror.save);
{ auto result = new Unqual!(ElementEncodingType!(ElementType!RoR))[resultLen];
result[j .. j + seplen] = sep2; copy(iter, result);
j += seplen; return cast(typeof(return)) result;
} }
immutable wlen = words[i].length; else
result[j .. j + wlen] = words[i]; {
j += wlen; return copy(iter, appender!(typeof(return))).data;
} }
assert(j == len);
return cast(typeof(return)) result;
} }
unittest unittest
@ -726,6 +802,7 @@ unittest
assert(i == 0, text(i)); assert(i == 0, text(i));
assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]); assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]);
} }
/** /**
@ -734,9 +811,8 @@ Replaces elements from $(D array) with indices ranging from $(D from)
or shrinks the array as needed. or shrinks the array as needed.
*/ */
void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff) void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
if (is(ElementType!Range == T)) if (isDynamicArray!Range && is(ElementType!Range : T))
{ {
// container = container[0 .. from] ~ stuff ~ container[to .. $];
if (overlap(array, stuff)) if (overlap(array, stuff))
{ {
// use slower/conservative method // use slower/conservative method
@ -761,7 +837,6 @@ void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
} }
} }
void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff) void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
if (!is(ElementType!Range == T) && is(Unqual!Range == void*)) if (!is(ElementType!Range == T) && is(Unqual!Range == void*))
{ {

View file

@ -31,7 +31,7 @@ Distributed under the Boost Software License, Version 1.0.
*/ */
module std.getopt; module std.getopt;
private import std.string, std.conv, std.traits, std.bitmanip, private import std.array, std.string, std.conv, std.traits, std.bitmanip,
std.algorithm, std.ctype, std.exception; std.algorithm, std.ctype, std.exception;
version (unittest) version (unittest)

View file

@ -634,6 +634,34 @@ unittest
static assert(is(ElementType!(typeof(buf)) : void)); static assert(is(ElementType!(typeof(buf)) : void));
} }
/**
The encoding element type of $(D R). For narrow strings ($(D char[]),
$(D wchar[]) and their qualified variants including $(D string) and
$(D wstring)), $(D ElementEncodingType) is the character type of the
string. For all other ranges, $(D ElementEncodingType) is the same as
$(D ElementType).
*/
template ElementEncodingType(R)
{
static if (isNarrowString!R)
alias typeof(R.init[0]) ElementEncodingType;
else
alias ElementType!R ElementEncodingType;
}
unittest
{
enum XYZ : string { a = "foo" };
auto x = front(XYZ.a);
static assert(is(ElementType!(XYZ) : dchar));
immutable char[3] a = "abc";
static assert(is(ElementType!(typeof(a)) : dchar));
int[] i;
static assert(is(ElementType!(typeof(i)) : int));
void[] buf;
static assert(is(ElementType!(typeof(buf)) : void));
}
/** /**
Returns $(D true) if $(D R) is a forward range and has swappable Returns $(D true) if $(D R) is a forward range and has swappable
elements. The following code should compile for any random-access elements. The following code should compile for any random-access
@ -2122,11 +2150,6 @@ if(isInputRange!(Unqual!Range) &&
public: public:
alias R Source; alias R Source;
static if (byRef)
alias ref .ElementType!(R) ElementType;
else
alias .ElementType!(R) ElementType;
@property bool empty() @property bool empty()
{ {
return _maxAvailable == 0 || original.empty; return _maxAvailable == 0 || original.empty;
@ -2156,7 +2179,7 @@ public:
} }
static if (hasAssignableElements!R) static if (hasAssignableElements!R)
@property auto front(ElementType v) @property auto front(ElementType!R v)
{ {
// This has to return auto instead of void because of Bug 4706. // This has to return auto instead of void because of Bug 4706.
original.front = v; original.front = v;
@ -2164,7 +2187,7 @@ public:
static if(hasMobileElements!R) static if(hasMobileElements!R)
{ {
ElementType moveFront() auto moveFront()
{ {
return .moveFront(original); return .moveFront(original);
} }
@ -2210,13 +2233,13 @@ public:
static if(hasAssignableElements!R) static if(hasAssignableElements!R)
{ {
auto back(ElementType v) auto back(ElementType!R v)
{ {
// This has to return auto instead of void because of Bug 4706. // This has to return auto instead of void because of Bug 4706.
original[this.length - 1] = v; original[this.length - 1] = v;
} }
void opIndexAssign(ElementType v, size_t index) void opIndexAssign(ElementType!R v, size_t index)
{ {
original[index] = v; original[index] = v;
} }
@ -2224,12 +2247,12 @@ public:
static if(hasMobileElements!R) static if(hasMobileElements!R)
{ {
ElementType moveBack() auto moveBack()
{ {
return .moveAt(original, this.length - 1); return .moveAt(original, this.length - 1);
} }
ElementType moveAt(size_t index) auto moveAt(size_t index)
{ {
assert(index < this.length, assert(index < this.length,
"Attempting to index out of the bounds of a " "Attempting to index out of the bounds of a "
@ -2435,9 +2458,9 @@ struct Repeat(T)
{ {
private T _value; private T _value;
/// Range primitive implementations. /// Range primitive implementations.
@property ref T front() { return _value; } @property T front() { return _value; }
/// Ditto /// Ditto
@property ref T back() { return _value; } @property T back() { return _value; }
/// Ditto /// Ditto
enum bool empty = false; enum bool empty = false;
/// Ditto /// Ditto
@ -2445,9 +2468,9 @@ struct Repeat(T)
/// Ditto /// Ditto
void popBack() {} void popBack() {}
/// Ditto /// Ditto
@property Repeat!(T) save() { return this; } @property Repeat!T save() { return this; }
/// Ditto /// Ditto
ref T opIndex(size_t) { return _value; } T opIndex(size_t) { return _value; }
} }
/// Ditto /// Ditto

View file

@ -389,7 +389,7 @@ unittest
* ditto * ditto
*/ */
ptrdiff_t lastIndexOf(Char)(const(Char)[] s, dchar c, sizediff_t lastIndexOf(Char)(const(Char)[] s, dchar c,
CaseSensitive cs = CaseSensitive.yes) CaseSensitive cs = CaseSensitive.yes)
{ {
if (cs == CaseSensitive.yes) if (cs == CaseSensitive.yes)
@ -627,7 +627,7 @@ unittest
* ditto * ditto
*/ */
ptrdiff_t lastIndexOf(Char1, Char2)(in Char1[] s, in Char2[] sub, sizediff_t lastIndexOf(Char1, Char2)(in Char1[] s, in Char2[] sub,
CaseSensitive cs = CaseSensitive.yes) if (isSomeChar!Char1 && isSomeChar!Char2) CaseSensitive cs = CaseSensitive.yes) if (isSomeChar!Char1 && isSomeChar!Char2)
{ {
if (cs == CaseSensitive.yes) if (cs == CaseSensitive.yes)
@ -1088,27 +1088,12 @@ unittest
} }
/******************************************** /********************************************
* Return a string that consists of s[] repeated n times. * Repeat $(D s) for $(D n) times. This function is scheduled for
* deprecation - use $(XREF array, replicate) instead.
*/ */
S repeat(S)(S s, size_t n) S repeat(S)(S s, size_t n)
{ {
if (n == 0) return std.array.replicate(s, n);
return S.init;
if (n == 1)
return s;
auto r = new Unqual!(typeof(s[0]))[n * s.length];
if (s.length == 1)
r[] = s[0];
else
{
auto len = s.length;
for (size_t i = 0; i < n * len; i += len)
{
r[i .. i + len] = s[];
}
}
return cast(S) r;
} }
unittest unittest
@ -1137,166 +1122,10 @@ unittest
*/ */
alias std.array.join join; alias std.array.join join;
/************************************** /**
Split $(D s[]) into an array of words, using whitespace as delimiter. * Alias for std.array.split
*/ */
alias std.array.split split;
S[] split(S)(S s) if (isSomeString!S)
{
size_t istart;
bool inword = false;
S[] result;
foreach (i; 0 .. s.length)
{
switch (s[i])
{
case ' ':
case '\t':
case '\f':
case '\r':
case '\n':
case '\v':
if (inword)
{
result ~= s[istart .. i];
inword = false;
}
break;
default:
if (!inword)
{
istart = i;
inword = true;
}
break;
}
}
if (inword)
result ~= s[istart .. $];
return result;
}
unittest
{
foreach (S; TypeTuple!(string, wstring, dstring))
{
debug(string) printf("string.split1\n");
S s = " peter paul\tjerry ";
S[] words;
int i;
words = split(s);
assert(words.length == 3);
i = cmp(words[0], "peter");
assert(i == 0);
i = cmp(words[1], "paul");
assert(i == 0);
i = cmp(words[2], "jerry");
assert(i == 0);
}
}
auto splitter(String)(String s) if (isSomeString!String)
{
//return std.regex.splitter(s, regex("[ \t\n\r]+"));
return std.algorithm.splitter!isspace(s);
}
unittest
{
auto a = " a bcd ef gh ";
//foreach (e; splitter(a)) writeln("[", e, "]");
assert(equal(splitter(a), ["", "a", "bcd", "ef", "gh"][]));
a = "";
assert(splitter(a).empty);
}
/**************************************
* Split s[] into an array of words,
* using delim[] as the delimiter.
*/
Unqual!(S1)[] split(S1, S2)(S1 s, S2 delim)
if (isSomeString!S1 && isSomeString!S2)
{
Unqual!(S1) us = s;
auto app = appender!(Unqual!(S1)[])();
foreach (word; std.algorithm.splitter(us, delim))
{
app.put(word);
}
return app.data;
}
unittest
{
debug(string) printf("string.split2\n");
foreach (S; TypeTuple!(string, wstring, dstring,
immutable(string), immutable(wstring), immutable(dstring),
char[], wchar[], dchar[],
const(char)[], const(wchar)[], const(dchar)[]))
{
S s = to!S(",peter,paul,jerry,");
int i;
auto words = split(s, ",");
assert(words.length == 5, text(words.length));
i = cmp(words[0], "");
assert(i == 0);
i = cmp(words[1], "peter");
assert(i == 0);
i = cmp(words[2], "paul");
assert(i == 0);
i = cmp(words[3], "jerry");
assert(i == 0);
i = cmp(words[4], "");
assert(i == 0);
auto s1 = s[0 .. s.length - 1]; // lop off trailing ','
words = split(s1, ",");
assert(words.length == 4);
i = cmp(words[3], "jerry");
assert(i == 0);
auto s2 = s1[1 .. s1.length]; // lop off leading ','
words = split(s2, ",");
assert(words.length == 3);
i = cmp(words[0], "peter");
assert(i == 0);
auto s3 = to!S(",,peter,,paul,,jerry,,");
words = split(s3, ",,");
//printf("words.length = %d\n", words.length);
assert(words.length == 5);
i = cmp(words[0], "");
assert(i == 0);
i = cmp(words[1], "peter");
assert(i == 0);
i = cmp(words[2], "paul");
assert(i == 0);
i = cmp(words[3], "jerry");
assert(i == 0);
i = cmp(words[4], "");
assert(i == 0);
auto s4 = s3[0 .. s3.length - 2]; // lop off trailing ',,'
words = split(s4, ",,");
assert(words.length == 4);
i = cmp(words[3], "jerry");
assert(i == 0);
auto s5 = s4[2 .. s4.length]; // lop off leading ',,'
words = split(s5, ",,");
assert(words.length == 3);
i = cmp(words[0], "peter");
assert(i == 0);
}
}
/************************************** /**************************************
* Split s[] into an array of lines, * Split s[] into an array of lines,
@ -1674,11 +1503,8 @@ unittest
/*********************************************** /***********************************************
* Returns s[] sans trailing character, if there is one. * Returns s[] sans trailing character, if there is one.
* If last two characters are CR-LF, then both are removed. * If last two characters are CR-LF, then both are removed.
*
* Deprecated: use s.popBack() instead.
*/ */
S chop(S)(S s) if (isSomeString!S)
deprecated S chop(S)(S s) if (isSomeString!S)
{ {
auto len = s.length; auto len = s.length;
if (!len) return s; if (!len) return s;