Improvements to replicate(); documented splitter() for strings; renamed replace() in place to replaceInPlace(); removed replace() that takes void* in the last position; moved replace() from string to array and generalized it; attached constraint to functional.not; more cleanup of std.string; improved std.algorithm.util and count to accept ranges; improved constraint in std.algorithm.remove

This commit is contained in:
Andrei Alexandrescu 2011-01-21 08:39:39 +00:00
parent 6c89581e2f
commit 272ceaa9a6
5 changed files with 173 additions and 161 deletions

View file

@ -805,10 +805,9 @@ assert(r1 == [ 2.5 ]);
*/
template filter(alias pred)
{
Filter!(unaryFun!(pred), Range)
filter(Range)(Range rs)
auto filter(Range)(Range rs)
{
return typeof(return)(rs);
return Filter!(unaryFun!(pred), Range)(rs);
}
}
@ -1790,7 +1789,7 @@ if(!is(isTerminator))
_input.popFront();
}
assert(!_input.empty && !_isTerminator(_input.front));
// Prime _end
// Prepare _end
_end = 1;
while (_end < _input.length && !_isTerminator(_input[_end]))
{
@ -1967,7 +1966,6 @@ if (isForwardRange!RoR && isInputRange!(ElementType!RoR)
if (!_current.empty) return;
useSeparator();
}
// We need to re-prime the range
}
static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
@ -2009,7 +2007,7 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
private:
RoR _items;
ElementType!RoR _current;
void prime()
void prepare()
{
for (;; _items.popFront())
{
@ -2023,7 +2021,7 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
this(RoR r)
{
_items = r;
prime();
prepare();
}
static if (isInfinite!(ElementType!RoR))
{
@ -2045,7 +2043,7 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
{
assert(!_current.empty);
_current.popFront();
if (_current.empty) prime();
if (_current.empty) prepare();
}
static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
{
@ -2565,7 +2563,7 @@ if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2
}
else
{
// Prime the search with needle's first element
// Prepare the search with needle's first element
if (needle.empty) return haystack;
haystack = .find!pred(haystack, needle.front);
if (haystack.empty) return haystack;
@ -3200,7 +3198,7 @@ struct Until(alias pred, Range, Sentinel) if (isInputRange!Range)
static if (is(Sentinel == void))
return unaryFun!pred(_input.front);
else
return binaryFun!pred(_input.front, _sentinel);
return startsWith!pred(_input, _sentinel);
}
void popFront()
@ -3274,6 +3272,7 @@ unittest
static assert(isForwardRange!(typeof(until!"a == 2"(a, OpenRight.no))));
assert(equal(a.until(7), [1, 2, 4][]));
assert(equal(a.until([7, 2]), [1, 2, 4, 7][]));
assert(equal(a.until(7, OpenRight.no), [1, 2, 4, 7][]));
assert(equal(until!"a == 2"(a, OpenRight.no), [1, 2][]));
}
@ -3736,18 +3735,33 @@ unittest
// count
/**
Counts the number of elements $(D x) in $(D r) for which $(D pred(x,
value)) is $(D true). $(D pred) defaults to equality. Performs $(BIGOH
r.length) evaluations of $(D pred).
The first version counts the number of elements $(D x) in $(D r) for
which $(D pred(x, value)) is $(D true). $(D pred) defaults to
equality. Performs $(BIGOH r.length) evaluations of $(D pred).
The second version returns the number of times $(D needle) occurs in
$(D haystack). Throws an exception if $(D needle.empty), as the _count
of the empty range in any range would be infinite. Overlapped counts
are not considered, for example $(D count("aaa", "aa")) is $(D 1), not
$(D 2).
The third version counts the elements for which $(D pred(x)) is $(D
true). Performs $(BIGOH r.length) evaluations of $(D pred).
Example:
----
// count elements in range
int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
assert(count(a, 2) == 3);
assert(count!("a > b")(a, 2) == 5);
// count range in range
assert(count("abcadfabf", "ab") == 2);
assert(count("ababab", "abab") == 1);
assert(count("ababab", "abx") == 0);
// count predicate in range
assert(count!("a > 1")(a) == 8);
----
*/
size_t count(alias pred = "a == b", Range, E)(Range r, E value)
if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, value)) == bool))
{
@ -3773,20 +3787,15 @@ unittest
assert(count!("a == '語'")("日本語"d) == 1);
}
/**
* Returns the number of times $(D needle) occurs in $(D
* haystack). Throws an exception if $(D needle.empty), as the _count
* of the empty range in any range would be infinite. Overlapped
* counts are not considered, for example $(D count("aaa", "aa")) is
* $(D 1), not $(D 2).
*
* Example:
----
assert(count("abcadfabf", "ab") == 2);
assert(count("ababab", "abab") == 1);
assert(count("ababab", "abx") == 0);
----
*/
unittest
{
debug(std_algorithm) printf("algorithm.count.unittest\n");
string s = "This is a fofofof list";
string sub = "fof";
assert(count(s, sub) == 2);
}
/// Ditto
size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
if (isInputRange!R1 && isForwardRange!R2 && is(typeof(binaryFun!pred(haystack, needle)) == bool))
{
@ -3805,16 +3814,7 @@ unittest
assert(count("ababab", "abx") == 0);
}
/**
Counts the number of elements $(D x) in $(D r) for which $(D pred(x))
is $(D true). Performs $(BIGOH r.length) evaluations of $(D pred).
Example:
----
int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
assert(count!("a > 1")(a) == 8);
----
*/
/// Ditto
size_t count(alias pred = "true", Range)(Range r) if (isInputRange!(Range))
{
size_t result;
@ -5045,7 +5045,8 @@ cases.))
Range remove
(SwapStrategy s = SwapStrategy.stable, Range, Offset...)
(Range range, Offset offset)
if (isBidirectionalRange!Range && hasLength!Range && s != SwapStrategy.stable)
if (isBidirectionalRange!Range && hasLength!Range && s != SwapStrategy.stable
&& Offset.length >= 1)
{
enum bool tupleLeft = is(typeof(offset[0][0]))
&& is(typeof(offset[0][1]));
@ -5150,8 +5151,9 @@ if (isBidirectionalRange!Range && hasLength!Range && s != SwapStrategy.stable)
Range remove
(SwapStrategy s = SwapStrategy.stable, Range, Offset...)
(Range range, Offset offset)
if (isForwardRange!Range && !isBidirectionalRange!Range
if ((isForwardRange!Range && !isBidirectionalRange!Range
|| !hasLength!Range || s == SwapStrategy.stable)
&& Offset.length >= 1)
{
auto result = range;
auto src = range, tgt = range;

View file

@ -5,6 +5,8 @@ Copyright: Copyright Andrei Alexandrescu 2008-.
License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(WEB erdani.org, Andrei Alexandrescu)
Functions and types that manipulate built-in arrays.
*/
module std.array;
@ -190,12 +192,14 @@ assert(b is a);
Implements the range interface primitive $(D popFront) for built-in
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)).
equivalent to $(D popFront(array)). For $(GLOSSARY narrow strings),
$(D popFront) automaticaly advances to the next $(GLOSSARY code
point).
Example:
----
int[] a = [ 1, 2, 3 ];
a.popFront;
a.popFront();
assert(a == [ 2, 3 ]);
----
*/
@ -217,6 +221,7 @@ unittest
static assert(!__traits(compiles, popFront!(void[])));
}
// Specialization for narrow strings
void popFront(A)(ref A a)
if (isNarrowString!A && isMutable!A)
{
@ -243,7 +248,8 @@ unittest
Implements the range interface primitive $(D popBack) for built-in
arrays. Due to the fact that nonmember functions can be called with
the first argument using the dot notation, $(D array.popBack) is
equivalent to $(D popBack(array)).
equivalent to $(D popBack(array)). For $(GLOSSARY narrow strings), $(D
popFront) automaticaly eliminates the last $(GLOSSARY code point).
Example:
@ -270,6 +276,7 @@ unittest
static assert(!__traits(compiles, popBack!(void[])));
}
// Specialization for arrays of char
@trusted void popBack(A)(ref A a)
if (is(A : const(char)[]) && isMutable!A)
{
@ -311,6 +318,7 @@ unittest
static assert(!__traits(compiles, popBack!(immutable char[])));
}
// Specialization for arrays of wchar
@trusted void popBack(A)(ref A a)
if (is(A : const(wchar)[]) && isMutable!A)
{
@ -338,7 +346,9 @@ unittest
Implements the range interface primitive $(D front) for built-in
arrays. Due to the fact that nonmember functions can be called with
the first argument using the dot notation, $(D array.front) is
equivalent to $(D front(array)).
equivalent to $(D front(array)). For $(GLOSSARY narrow strings), $(D
front) automaticaly returns the first $(GLOSSARY code point) as a $(D
dchar).
Example:
@ -373,7 +383,9 @@ unittest
Implements the range interface primitive $(D back) for built-in
arrays. Due to the fact that nonmember functions can be called with
the first argument using the dot notation, $(D array.back) is
equivalent to $(D back(array)).
equivalent to $(D back(array)). For $(GLOSSARY narrow strings), $(D
back) automaticaly returns the last $(GLOSSARY code point) as a $(D
dchar).
Example:
----
@ -381,9 +393,7 @@ int[] a = [ 1, 2, 3 ];
assert(a.back == 3);
----
*/
ref typeof(A.init[0]) back(A)(A a)
if (isDynamicArray!A && !isNarrowString!A
&& !is(typeof(A.init[0]) : const(void)))
ref T back(T)(T[] a) if (!isNarrowString!(T[]))
{
assert(a.length, "Attempting to fetch the back of an empty array");
return a[$ - 1];
@ -397,36 +407,35 @@ unittest
assert(a.back == 7);
}
// Specialization for strings
dchar back(A)(A a)
if (isDynamicArray!A && isNarrowString!A)
{
assert(a.length, "Attempting to fetch the back of an empty array");
auto n = a.length;
const p = a.ptr + n;
if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000)
{
--n;
return decode(a, n);
}
else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000)
{
n -= 2;
return decode(a, n);
}
else if (n >= 3 && (p[-3] & 0b1100_0000) != 0b1000_0000)
{
n -= 3;
return decode(a, n);
}
else if (n >= 4 && (p[-4] & 0b1100_0000) != 0b1000_0000)
{
n -= 4;
return decode(a, n);
}
else
{
throw new UtfException("Invalid UTF character at end of string");
throw new UtfException(a.length
? "Invalid UTF character at end of string"
: "Attempting to fetch the back of an empty array");
}
return decode(a, n);
}
// overlap
@ -448,8 +457,8 @@ assert(overlap(a, b).empty);
*/
T[] overlap(T)(T[] r1, T[] r2) @trusted pure nothrow
{
T* max(T* a, T* b) nothrow { return a > b ? a : b; }
T* min(T* a, T* b) nothrow { return a < b ? a : b; }
static T* max(T* a, T* b) nothrow { return a > b ? a : b; }
static T* min(T* a, T* b) nothrow { return a < b ? a : b; }
auto b = max(r1.ptr, r2.ptr);
auto e = min(r1.ptr + r1.length, r2.ptr + r2.length);
return b < e ? b[0 .. e - b] : null;
@ -471,18 +480,14 @@ unittest
}
/**
Inserts $(D stuff) in $(D container) at position $(D pos).
Inserts $(D stuff) (which must be an input range or a single item) in
$(D array) at position $(D pos).
*/
void insert(T, Range)(ref T[] array, size_t pos, Range stuff)
if (isInputRange!Range && is(ElementEncodingType!Range : T))
{
static if (hasLength!Range)
{
// @@@BUG 2130@@@
// immutable
// size_t delta = toInsert.length,
// size_t oldLength = array.length,
// size_t newLength = oldLength + delta;
immutable
delta = stuff.length,
oldLength = array.length,
@ -535,10 +540,12 @@ pure 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.
Returns an array that consists of $(D s) (which must be an input
range) repeated $(D n) times. This function allocates, fills, and
returns a new array. For a lazy version, refer to $(XREF
range,repeat).
*/
S replicate(S)(S s, size_t n) if (isSomeString!S)
S replicate(S)(S s, size_t n) if (isDynamicArray!S)
{
// Optimization for return join(std.range.repeat(s, n));
if (n == 0)
@ -550,8 +557,8 @@ S replicate(S)(S s, size_t n) if (isSomeString!S)
r[] = s[0];
else
{
auto len = s.length;
for (size_t i = 0; i < n * len; i += len)
immutable len = s.length, nlen = n * len;
for (size_t i = 0; i < nlen; i += len)
{
r[i .. i + len] = s[];
}
@ -560,7 +567,7 @@ S replicate(S)(S s, size_t n) if (isSomeString!S)
}
ElementType!S[] replicate(S)(S s, size_t n)
if (isInputRange!S && !isSomeString!S)
if (isInputRange!S && !isDynamicArray!S)
{
return join(std.range.repeat(s, n));
}
@ -590,7 +597,9 @@ unittest
}
/**************************************
Split $(D s[]) into an array of words, using whitespace as delimiter.
Split the string $(D s) into an array of words, using whitespace as
delimiter. Runs of whitespace are merged together (no empty words are
produced).
*/
S[] split(S)(S s) if (isSomeString!S)
@ -629,22 +638,21 @@ 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);
S s = " \t\npeter paul\tjerry \n";
assert(equal(split(s), [ to!S("peter"), to!S("paul"), to!S("jerry") ]));
}
}
/**
Splits a string by whitespace.
Example:
----
auto a = " a bcd ef gh ";
assert(equal(splitter(a), ["", "a", "bcd", "ef", "gh"][]));
----
*/
auto splitter(String)(String s) if (isSomeString!String)
{
return std.algorithm.splitter!isspace(s);
@ -659,7 +667,7 @@ unittest
}
/**************************************
* Split $(D r) into an array, using $(D delim) as the delimiter.
* Splits $(D s) into an array, using $(D delim) as the delimiter.
*/
Unqual!(S1)[] split(S1, S2)(S1 s, S2 delim)
if (isForwardRange!(Unqual!S1) && isForwardRange!S2)
@ -810,7 +818,7 @@ Replaces elements from $(D array) with indices ranging from $(D from)
(inclusive) to $(D to) (exclusive) with the range $(D stuff). Expands
or shrinks the array as needed.
*/
void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
if (isDynamicArray!Range && is(ElementType!Range : T))
{
if (overlap(array, stuff))
@ -837,52 +845,45 @@ if (isDynamicArray!Range && is(ElementType!Range : T))
}
}
void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
if (!is(ElementType!Range == T) && is(Unqual!Range == void*))
{
replace(array, from, to, cast(T[])[]);
}
unittest
{
int[] a = [1, 4, 5];
replace(a, 1u, 2u, [2, 3, 4]);
replaceInPlace(a, 1u, 2u, [2, 3, 4]);
assert(a == [1, 2, 3, 4, 5]);
replace(a, 1u, 2u, cast(int[])[]);
replaceInPlace(a, 1u, 2u, cast(int[])[]);
assert(a == [1, 3, 4, 5]);
replace(a, 1u, 2u, null);
assert(a == [1, 4, 5]);
}
/********************************************
* Replace occurrences of from[] with to[] in s[].
* Replace occurrences of $(D from) with $(D to) in $(D a). Returns a
* new array.
*/
C1[] replace(C1, C2, C3)(C1[] s, C2[] from, C3[] to)
//if (is(typeof(s[0] == from[0])) && is(typeof(to[0]) : C1))
R1 replace(R1, R2, R3)(R1 subject, R2 from, R3 to)
if (isDynamicArray!R1 && isForwardRange!R2 && isForwardRange!R3)
{
if (from.length == 0) return s;
typeof(s.dup) p;
if (from.empty) return subject;
auto app = appender!R1();
for (;;)
{
auto s1 = std.algorithm.find(s, from);
if (!s1.length)
auto balance = std.algorithm.find(subject, from.save);
if (balance.empty)
{
if (p is null) return s;
p ~= s;
if (app.data.empty) return subject;
app.put(subject);
break;
}
p ~= s[0 .. s.length - s1.length];
p ~= to;
s = s1[from.length .. $];
app.put(subject[0 .. subject.length - balance.length]);
app.put(to.save);
subject = balance[from.length .. $];
}
return cast(C1[]) p;
return app.data;
}
unittest
{
debug(string) printf("string.replace.unittest\n");
debug(string) printf("array.replace.unittest\n");
alias TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])
TestTypes;
@ -908,8 +909,8 @@ unittest
}
/*****************************
* Return an array that is $(D s[]) with $(D slice[]) replaced by $(D
* replacement[]).
Return an array that is $(D s) with $(D slice) replaced by $(D
replacement[]).
*/
T[] replaceSlice(T)(T[] s, in T[] slice, in T[] replacement)
@ -1020,7 +1021,8 @@ done.
else
{
// didn't work, must reallocate
auto bi = GC.qalloc(newCapacity * T.sizeof, (typeid(T[]).next.flags & 1) ? 0 : GC.BlkAttr.NO_SCAN);
auto bi = GC.qalloc(newCapacity * T.sizeof,
(typeid(T[]).next.flags & 1) ? 0 : GC.BlkAttr.NO_SCAN);
_data.capacity = bi.size / T.sizeof;
if(len)
memcpy(bi.base, _data.arr.ptr, len * T.sizeof);
@ -1071,7 +1073,8 @@ Returns the managed array.
else
{
// didn't work, must reallocate
auto bi = GC.qalloc(newlen * T.sizeof, (typeid(T[]).next.flags & 1) ? 0 : GC.BlkAttr.NO_SCAN);
auto bi = GC.qalloc(newlen * T.sizeof,
(typeid(T[]).next.flags & 1) ? 0 : GC.BlkAttr.NO_SCAN);
_data.capacity = bi.size / T.sizeof;
if(len)
memcpy(bi.base, _data.arr.ptr, len * T.sizeof);

View file

@ -278,9 +278,9 @@ string a = " Hello, world!";
assert(find!(not!isspace)(a) == "Hello, world!");
----
*/
template not(alias pred)
template not(alias pred) if (is(typeof(!unaryFun!pred(args))))
{
bool not(T...)(T args) { return !pred(args); }
auto not(T...)(T args) { return !binaryFun!pred(args); }
}
/**

View file

@ -80,7 +80,7 @@ version(Posix)
version (Windows) alias std.string.icmp fcmp;
version (Posix) alias std.string.cmp fcmp;
version (Posix) alias std.algorithm.cmp fcmp;
/**************************
* Extracts the extension from a filename or path.

View file

@ -1,38 +1,69 @@
// Written in the D programming language.
/**
String handling functions. Objects of types $(D _string), $(D
wstring), and $(D dstring) are value types and cannot be mutated
element-by-element. For using mutation during building strings, use
$(D char[]), $(D wchar[]), or $(D dchar[]). The $(D *_string) types
are preferable because they don't exhibit undesired aliasing, thus
making code more robust.
String handling functions. Objects of types $(D _string), $(D
wstring), and $(D dstring) are value types and cannot be mutated
element-by-element. For using mutation during building strings, use
$(D char[]), $(D wchar[]), or $(D dchar[]). The $(D *_string) types
are preferable because they don't exhibit undesired aliasing, thus
making code more robust.
Macros:
WIKI = Phobos/StdString
Macros: WIKI = Phobos/StdString
Copyright: Copyright Digital Mars 2007-.
License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(WEB digitalmars.com, Walter Bright), $(WEB erdani.org,
Andrei Alexandrescu)
$(B $(RED IMPORTANT NOTE:)) Beginning with version 2.052, the
following symbols have been generalized beyond strings and moved to
different modules. This action was prompted by the fact that
generalized routines belong better in other places, although they
still work for strings as expected. In order to use moved symbols, you
will need to import the respective modules as follows:
$(BOOKTABLE ,
$(TR $(TH Symbol) $(TH Comment))
$(TR $(TD $(D cmp)) $(TD Moved to $(XREF algorithm, cmp) and
generalized to work for all input ranges and accept a custom
predicate.))
$(TR $(TD $(D count)) $(TD Moved to $(XREF algorithm, count) and
generalized to accept a custom predicate.))
$(TR $(TD $(D replace)) $(TD Moved to $(XREF array, replace).))
$(TR $(TD $(D ByCodeUnit)) $(TD Removed.))
$(TR $(TD $(D insert)) $(TD Use $(XREF array, insert) instead.))
$(TR $(TD $(D join)) $(TD Use $(XREF array, join) instead.))
$(TR $(TD $(D repeat)) $(TD Use $(XREF array, replicate) instead.))
$(TR $(TD $(D replace)) $(TD Use $(XREF array, replace) instead.))
$(TR $(TD $(D replaceSlice)) $(TD Use $(XREF array, replace) instead.))
$(TR $(TD $(D split)) $(TD Use $(XREF array, split) instead.))
)
Copyright: Copyright Digital Mars 2007 - 2009.
License: <a href="http: //www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
Authors: $(WEB digitalmars.com, Walter Bright),
$(WEB erdani.org, Andrei Alexandrescu)
*/
/*
Copyright Digital Mars 2007 - 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.string;
//debug=string; // uncomment to turn on debugging printf's
private import core.exception : onRangeError;
import core.exception : onRangeError;
import core.vararg, core.stdc.stdio, core.stdc.stdlib,
core.stdc.string, std.algorithm,
core.stdc.string/*, std.algorithm*/,
std.conv, std.ctype, std.encoding, std.exception, std.format,
std.functional, std.metastrings, std.range, std.regex, std.stdio,
std.traits, std.typetuple, std.uni, std.utf;
public import std.algorithm : startsWith, endsWith;
public import std.algorithm : startsWith, endsWith, cmp, count;
public import std.array : join, split;
version(Windows) extern (C)
@ -95,12 +126,6 @@ $(TR $(TD $(D > 0)) $(TD $(D s1 > s2)))
*/
alias std.algorithm.cmp cmp;
/*********************************
* ditto
*/
int icmp(alias pred = "a < b", S1, S2)(S1 s1, S2 s2)
if (is(Unqual!(ElementType!S1) == dchar) && is(Unqual!(ElementType!S2) == dchar))
{
@ -1557,24 +1582,6 @@ unittest
assert(i == 0);
}
/***********************************************
* Count up all instances of sub[] in s[].
*/
alias std.algorithm.count count;
unittest
{
debug(string) printf("string.count.unittest\n");
string s = "This is a fofofof list";
string sub = "fof";
size_t i;
i = count(s, sub);
assert(i == 2);
}
/************************************************
* Replace tabs with the appropriate number of spaces.
* tabsize is the distance between tab stops.