On 05/22/2017 10:13 PM, Enjoys Math wrote:
immutable struct String(T) {
Marking a whole struct as `immutable` is usually a mistake. It applies
to all methods, making them unusable on mutable and const instances.
public:
this(immutable T[] s) {
This means you can create `String`s only with immutable arrays. Again,
not what you want usually. You can use `inout` to make a constructor
that accepts mutable, const, and immutable arrays when constructing
mutable, const, and immutable instances, respectively:
----
this(inout T[] s) inout { ... }
----
If `inout` doesn't work for some reason, you will have to define
distinct constructors:
----
this(T[] s) { ... }
this(const T[] s) const { ... }
this(immutable T[] s) immutable { ... }
----
this.s = s;
}
alias s this;
string toString() const { return to!string(s); }
string flattened() const { string t = ""; foreach (c; s) t ~=
to!string(c); return t; }
size_t toHash() const @system pure nothrow
{
return hashOf(flattened());
}
bool opEquals(ref const String t) const @system pure nothrow
{
if (t.length != this.length)
return false;
for(size_t k=0; k < t.length; k++)
Off topic: foreach (k; 0 .. t.length)
if (t.s[k] != this.s[k]) return false;
Seeing how `k` is used, could also make it:
----
foreach (k, e; t)
if (e != this.s[k]) return false;
----
return true;
}
// A compressible is a substring that occurs (non-overlappingly)
>=2 and has a length >= 3
// or one that occurs >= 3 and has a length of 2
String[] compressibles() const {
auto count = compressibleCount();
String[] substrings;
foreach (s, n; count)
substrings ~= s;
return substrings;
}
size_t[String] compressibleCount() const {
auto count = substringCount(2, size_t(s.length / 2));
Off topic: substringCount's second parameter is an int, not a size_t.
Converting from size_t to int like this works with -m32, but if large
values are possible, you've got a bug. With -m64 it simply doesn't work.
You can use `to!int` which throws on failure.
foreach (str, n; count)
if (str.length == 2 && n < 3 || str.length >= 3 && n < 2)
count.remove(str);
return count;
}
String[] substrings(int minLen=0, int maxLen=-1) const {
auto count = substringCount();
String[] substrings;
foreach (s, n; count)
substrings ~= s;
return substrings;
}
size_t[String] substringCount(int minLen=0, int maxLen=-1) const {
if (maxLen == -1)
maxLen = s.length;
Same bug-prone conversion from size_t to int as above.
assert (maxLen <= s.length && minLen >=0 && minLen <= maxLen);
size_t[String] count;
if (minLen == 0)
count[empty] = s.length + 1;
`empty` is not a String, it's a T[]. Works when you change `empty` to
`static empty = String([]);`.
for (size_t i=0; i < s.length - minLen; i++) {
size_t max_len = min(s.length - i, maxLen);
for (size_t l=1; l < max_len; l++) {
auto substr = String(this.s[i..i+l]);
We're in a const method, so `this.s[i..i+l]` is const as well. If you
want to return a result with mutable keys, you have to `.dup` that slice
of s.
Alternatively, drop `const` from the method, or return a result with
const keys. Though returning `const` has similar repercussions as
returning immutable keys (next paragraphs).
Arguably, keys of associative arrays should be immutable. When the keys
change and the associative isn't re-hashed, it's going to behave
erratically.
Making the keys immutable would mean you have to `.idup` the slices.
You'd also have `.dup` in `compressibles` and `substring` if they're
supposed to return mutable `String`s.
if (!(substr in count))
count[substr] = 0;
else
count[substr] ++;
}
}
return count;
}
public:
immutable(T)[] empty = [];
private
T[] s;
}