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

@ -275,7 +275,8 @@ unittest
auto intRange = map!"a"([1,2,3]);
static assert(isRandomAccessRange!(typeof(intRange)));
foreach(DummyType; AllDummyRanges) {
foreach(DummyType; AllDummyRanges)
{
DummyType d;
auto m = map!"a * a"(d);
@ -1453,9 +1454,9 @@ public:
/// Ditto
Splitter!(Range, Separator)
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);
@ -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.
*/
struct Splitter(Range, Separator)
if (is(typeof(Range.init.front == Separator.init.front)))
if (is(typeof(Range.init.front == Separator.init.front) : bool))
{
private:
Range _input;
@ -1721,7 +1722,8 @@ struct Splitter(alias isTerminator, Range,
Slice = Select!(is(typeof(Range.init[0 .. 1])),
Range,
ElementType!(Range)[]))
if(!is(isTerminator)) {
if(!is(isTerminator))
{
private Range _input;
private size_t _end;
private alias unaryFun!isTerminator _isTerminator;
@ -1867,25 +1869,87 @@ assert(equal(joiner(["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:
Range _items;
Separator _sep, _currentSep;
public:
@property bool empty()
private RoR _items;
private ElementType!RoR _current;
private Separator _sep, _currentSep;
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;
}
@property ElementType!(ElementType!Range) front()
_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);
}
}
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 (!_items.front.empty) return _items.front.front;
assert(false);
assert(!_current.empty);
return _current.front;
}
void popFront()
{
assert(!empty);
@ -1893,47 +1957,33 @@ auto joiner(Range, Separator)(Range r, Separator sep)
if (!_currentSep.empty)
{
_currentSep.popFront();
if (_currentSep.empty)
{
// 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;
}
}
}
if (!_currentSep.empty) return;
useItem();
}
else
{
// we're using the range
assert(!_items.empty && !_items.front.empty);
_items.front.popFront();
if (_items.front.empty)
_current.popFront();
if (!_current.empty) return;
useSeparator();
}
// We need to re-prime the range
}
static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
{
_items.popFront();
if (!_items.empty)
@property auto save()
{
_currentSep = _sep.save;
Result copy;
copy._items = _items.save;
copy._current = _current.save;
copy._sep = _sep.save;
copy._currentSep = _currentSep.save;
return copy;
}
}
}
assert(empty || !_currentSep.empty || !_items.front.empty);
}
}
auto result = Result(r, sep);
if (!r.empty && r.front.empty)
{
result._items.popFront();
if (!result.empty)
{
result._currentSep = result._sep.save;
}
}
return result;
return Result(r, sep);
}
unittest
@ -1941,9 +1991,9 @@ unittest
debug(std_algorithm) scope(success)
writeln("unittest @", __FILE__, ":", __LINE__, " done.");
static assert(isInputRange!(typeof(joiner([""], ""))));
static assert(!isForwardRange!(typeof(joiner([""], ""))));
assert(equal(joiner([""], "xyz"), ""));
assert(equal(joiner(["", ""], "xyz"), "xyz"));
static assert(isForwardRange!(typeof(joiner([""], ""))));
assert(equal(joiner([""], "xyz"), ""), text(joiner([""], "xyz")));
assert(equal(joiner(["", ""], "xyz"), "xyz"), text(joiner(["", ""], "xyz")));
assert(equal(joiner(["", "abc"], "xyz"), "xyzabc"));
assert(equal(joiner(["abc", ""], "xyz"), "abcxyz"));
assert(equal(joiner(["abc", "def"], "xyz"), "abcxyzdef"));
@ -1951,6 +2001,82 @@ unittest
"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
/**
Iterates unique consecutive elements of the given range (functionality

View file

@ -1,31 +1,24 @@
// Written in the D programming language.
/**
Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
Copyright: Copyright Andrei Alexandrescu 2008-.
License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
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;
import std.c.stdio;
import core.memory;
import std.algorithm, std.conv, std.encoding, std.exception, std.range,
std.string, std.traits, std.typecons, std.utf;
private import std.c.string : memcpy;
private import std.intrinsic : bsr;
version(unittest) private import std.stdio, std.typetuple;
import std.algorithm, std.conv, std.ctype, std.encoding, std.exception,
std.intrinsic, std.range, std.string, std.traits, std.typecons, std.utf;
import std.c.string : memcpy;
version(unittest) import std.stdio, std.typetuple;
/**
Returns a newly-allocated dynamic array consisting of a copy of the input
range, static array, dynamic array, or class or struct with an $(D opApply)
function $(D r). Note that narrow strings are handled
as a special case in an overload.
Returns a newly-allocated dynamic array consisting of a copy of the
input range, static array, dynamic array, or class or struct with an
$(D opApply) function $(D r). Note that narrow strings are handled as
a special case in an overload.
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
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!(immutable(dchar)[])(str);
}
else static if(is(typeof(return) == const))
{
return to!(const(dchar)[])(str);
}
else
{
return to!(dchar[])(str);
}
return to!(typeof(return))(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;
this(uint num) { this.num = num; }
@ -115,7 +97,7 @@ version(unittest)
void opAssign(T)(T rhs) { this.num = rhs.num; }
}
struct OpApply
static struct OpApply
{
int opApply(int delegate(ref int) dg)
{
@ -129,10 +111,7 @@ version(unittest)
return res;
}
}
}
unittest
{
auto a = array([1, 2, 3, 4, 5][]);
//writeln(a);
assert(a == [ 1, 2, 3, 4, 5 ]);
@ -170,12 +149,9 @@ equivalent to $(D empty(array)).
Example:
----
void main()
{
auto a = [ 1, 2, 3 ];
assert(!a.empty);
assert(a[3 .. $].empty);
}
auto a = [ 1, 2, 3 ];
assert(!a.empty);
assert(a[3 .. $].empty);
----
*/
@ -199,12 +175,9 @@ equivalent to $(D save(array)).
Example:
----
void main()
{
auto a = [ 1, 2, 3 ];
auto b = a.save;
assert(b is a);
}
auto a = [ 1, 2, 3 ];
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
equivalent to $(D popFront(array)).
Example:
----
void main()
{
int[] a = [ 1, 2, 3 ];
a.popFront;
assert(a == [ 2, 3 ]);
}
int[] a = [ 1, 2, 3 ];
a.popFront;
assert(a == [ 2, 3 ]);
----
*/
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 "
~ T.stringof);
~ typeof(a[0]).stringof);
a = a[1 .. $];
}
unittest
{
//@@@BUG 2608@@@
//auto a = [ 1, 2, 3 ];
int[] a = [ 1, 2, 3 ];
a.popFront;
auto a = [ 1, 2, 3 ];
a.popFront();
assert(a == [ 2, 3 ]);
static assert(!__traits(compiles, popFront!(immutable int[])));
static assert(!__traits(compiles, popFront!(void[])));
}
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 "
~ T.stringof);
~ typeof(a[0]).stringof);
a = a[std.utf.stride(a, 0) .. $];
}
@ -283,17 +248,14 @@ equivalent to $(D popBack(array)).
Example:
----
void main()
{
int[] a = [ 1, 2, 3 ];
a.popBack;
assert(a == [ 1, 2 ]);
}
int[] a = [ 1, 2, 3 ];
a.popBack();
assert(a == [ 1, 2 ]);
----
*/
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);
a = a[0 .. $ - 1];
@ -301,17 +263,15 @@ if(isDynamicArray!A && !isNarrowString!A && isMutable!A && !is(A == void[]))
unittest
{
//@@@BUG 2608@@@
//auto a = [ 1, 2, 3 ];
int[] a = [ 1, 2, 3 ];
a.popBack;
auto a = [ 1, 2, 3 ];
a.popBack();
assert(a == [ 1, 2 ]);
static assert(!__traits(compiles, popBack!(immutable int[])));
static assert(!__traits(compiles, popBack!(void[])));
}
void popBack(A)(ref A a)
if(is(A : const(char)[]) && isMutable!A)
@trusted void popBack(A)(ref A a)
if (is(A : const(char)[]) && isMutable!A)
{
immutable n = a.length;
const p = a.ptr + n;
@ -333,7 +293,7 @@ if(is(A : const(char)[]) && isMutable!A)
}
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";
s.popBack();
assert(s == "hello", s);
string s3 = "\xE2\x89\xA0";
auto c = s3.back;
assert(c == cast(dchar)'\u2260');
@ -352,17 +311,18 @@ unittest
static assert(!__traits(compiles, popBack!(immutable char[])));
}
void popBack(A)(ref A a)
if(is(A : const(wchar)[]) && isMutable!A)
@trusted void popBack(A)(ref A a)
if (is(A : const(wchar)[]) && isMutable!A)
{
assert(a.length);
if (a.length == 1)
if (a.length <= 1) // this is technically == but costs nothing and is safer
{
a = a[0 .. 0];
return;
}
immutable c = a[$ - 2];
a = a[0 .. $ - 1 - (c >= 0xD800 && c <= 0xDBFF)];
// We can go commando from here on, we're safe; length is > 1
immutable c = a.ptr[a.length - 2];
a = a.ptr[0 .. a.length - 1 - (c >= 0xD800 && c <= 0xDBFF)];
}
unittest
@ -383,31 +343,30 @@ equivalent to $(D front(array)).
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)
if (is(typeof(A[0])) && !isNarrowString!A && !is(typeof(A[0]) : const(void)))
ref T front(T)(T[] a)
if (!isNarrowString!(T[]) && !is(T[] == void[]))
{
assert(a.length, "Attempting to fetch the front of an empty array");
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");
size_t i = 0;
return decode(a, i);
}
/// Ditto
void front(T)(T[] a, T v) if (!isNarrowString!A)
unittest
{
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:
----
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)
if (is(typeof(A.init[0])) && !isNarrowString!A
if (isDynamicArray!A && !isNarrowString!A
&& !is(typeof(A.init[0]) : const(void)))
{
// @@@BUG@@@ The assert below crashes the unittest due to a bug in
// the compiler
version (bug4426)
{
assert(a.length, "Attempting to fetch the back of an empty array");
}
else
{
assert(a.length);
}
return a[$ - 1];
}
@ -451,7 +398,7 @@ unittest
}
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");
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)
{
--n;
return std.utf.decode(a, n);
return decode(a, n);
}
else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000)
{
@ -527,26 +474,17 @@ unittest
Inserts $(D stuff) in $(D container) at position $(D pos).
*/
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
alias stuff toInsert;
//assert(!overlap(array, toInsert));
}
else
{
// presumably only one element
auto toInsert = (&stuff)[0 .. 1];
}
// @@@BUG 2130@@@
// immutable
// size_t delta = toInsert.length,
// size_t oldLength = array.length,
// size_t newLength = oldLength + delta;
immutable
delta = toInsert.length,
delta = stuff.length,
oldLength = array.length,
newLength = oldLength + delta;
@ -563,10 +501,22 @@ void insert(T, Range)(ref T[] array, size_t pos, Range stuff)
}
// Copy stuff into array
foreach (e; toInsert)
{
array[pos++] = e;
copy(stuff, array[pos .. pos + stuff.length]);
}
else
{
auto app = appender!(T[])();
app.put(array[0 .. pos]);
app.put(stuff);
app.put(array[pos .. $]);
array = app.data;
}
}
/// Ditto
void insert(T)(ref T[] array, size_t pos, T stuff)
{
return insert(array, pos, (&stuff)[0 .. 1]);
}
unittest
@ -579,7 +529,7 @@ unittest
}
// @@@ 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;
}
@ -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
* range) repeated $(D n) times.
*/
S multiply(S)(S s, size_t n) if (isSomeString!S)
S replicate(S)(S s, size_t n) if (isSomeString!S)
{
// Optimization for return join(std.range.repeat(s, n));
if (n == 0)
return S.init;
if (n == 1)
@ -609,102 +559,228 @@ S multiply(S)(S s, size_t n) if (isSomeString!S)
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 (n == 0)
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;
}
return join(std.range.repeat(s, n));
}
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[]))
{
S s;
s = multiply(to!S("1234"), 0);
s = replicate(to!S("1234"), 0);
assert(s is null);
s = multiply(to!S("1234"), 1);
s = replicate(to!S("1234"), 1);
assert(cmp(s, "1234") == 0);
s = multiply(to!S("1234"), 2);
s = replicate(to!S("1234"), 2);
assert(cmp(s, "12341234") == 0);
s = multiply(to!S("1"), 4);
s = replicate(to!S("1"), 4);
assert(cmp(s, "1111") == 0);
s = multiply(cast(S) null, 4);
s = replicate(cast(S) null, 4);
assert(s is null);
}
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;
* use $(D sep) as the separator.
* use $(D sep) as the separator if present, otherwise none.
*/
ElementType!RoR join(RoR, R)(RoR ror, R sep)
if (isInputRange!RoR && isInputRange!(typeof(ror.front))
&& !(isSomeString!(typeof(ror.front)) && isSomeString!R))
ElementEncodingType!(ElementType!RoR)[]
join(RoR, R)(RoR ror, R sep)
if (isInputRange!RoR && isInputRange!(ElementType!RoR) && isForwardRange!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
typeof(RoR.init[0]) join(RoR, R)(RoR words, R sep)
if (isSomeString!(typeof(words[0])) && isSomeString!R)
/// Ditto
ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror)
if (isInputRange!RoR && isInputRange!(ElementType!RoR))
{
if (!words.length) return null;
auto sep2 = to!(typeof(return))(sep);
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)
auto iter = joiner(ror);
static if (hasLength!RoR && hasLength!(ElementType!RoR))
{
if (i > 0)
{
result[j .. j + seplen] = sep2;
j += seplen;
}
immutable wlen = words[i].length;
result[j .. j + wlen] = words[i];
j += wlen;
}
assert(j == len);
immutable resultLen = reduce!"a + b.length"(cast(size_t) 0, ror.save);
auto result = new Unqual!(ElementEncodingType!(ElementType!RoR))[resultLen];
copy(iter, result);
return cast(typeof(return)) result;
}
else
{
return copy(iter, appender!(typeof(return))).data;
}
}
unittest
@ -726,6 +802,7 @@ unittest
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]]) == [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.
*/
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))
{
// 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)
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;
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;
version (unittest)

View file

@ -634,6 +634,34 @@ unittest
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
elements. The following code should compile for any random-access
@ -2122,11 +2150,6 @@ if(isInputRange!(Unqual!Range) &&
public:
alias R Source;
static if (byRef)
alias ref .ElementType!(R) ElementType;
else
alias .ElementType!(R) ElementType;
@property bool empty()
{
return _maxAvailable == 0 || original.empty;
@ -2156,7 +2179,7 @@ public:
}
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.
original.front = v;
@ -2164,7 +2187,7 @@ public:
static if(hasMobileElements!R)
{
ElementType moveFront()
auto moveFront()
{
return .moveFront(original);
}
@ -2210,13 +2233,13 @@ public:
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.
original[this.length - 1] = v;
}
void opIndexAssign(ElementType v, size_t index)
void opIndexAssign(ElementType!R v, size_t index)
{
original[index] = v;
}
@ -2224,12 +2247,12 @@ public:
static if(hasMobileElements!R)
{
ElementType moveBack()
auto moveBack()
{
return .moveAt(original, this.length - 1);
}
ElementType moveAt(size_t index)
auto moveAt(size_t index)
{
assert(index < this.length,
"Attempting to index out of the bounds of a "
@ -2435,9 +2458,9 @@ struct Repeat(T)
{
private T _value;
/// Range primitive implementations.
@property ref T front() { return _value; }
@property T front() { return _value; }
/// Ditto
@property ref T back() { return _value; }
@property T back() { return _value; }
/// Ditto
enum bool empty = false;
/// Ditto
@ -2445,9 +2468,9 @@ struct Repeat(T)
/// Ditto
void popBack() {}
/// Ditto
@property Repeat!(T) save() { return this; }
@property Repeat!T save() { return this; }
/// Ditto
ref T opIndex(size_t) { return _value; }
T opIndex(size_t) { return _value; }
}
/// Ditto

View file

@ -389,7 +389,7 @@ unittest
* ditto
*/
ptrdiff_t lastIndexOf(Char)(const(Char)[] s, dchar c,
sizediff_t lastIndexOf(Char)(const(Char)[] s, dchar c,
CaseSensitive cs = CaseSensitive.yes)
{
if (cs == CaseSensitive.yes)
@ -627,7 +627,7 @@ unittest
* 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)
{
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)
{
if (n == 0)
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;
return std.array.replicate(s, n);
}
unittest
@ -1137,166 +1122,10 @@ unittest
*/
alias std.array.join join;
/**************************************
Split $(D s[]) into an array of words, using whitespace as delimiter.
/**
* Alias for std.array.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);
}
}
alias std.array.split split;
/**************************************
* Split s[] into an array of lines,
@ -1674,11 +1503,8 @@ unittest
/***********************************************
* Returns s[] sans trailing character, if there is one.
* If last two characters are CR-LF, then both are removed.
*
* Deprecated: use s.popBack() instead.
*/
deprecated S chop(S)(S s) if (isSomeString!S)
S chop(S)(S s) if (isSomeString!S)
{
auto len = s.length;
if (!len) return s;