开发者

D2: std.algorithm.indexOf doesn't work anymore

开发者 https://www.devze.com 2023-02-20 19:24 出处:网络
I posted the following code on rosettacode.org for the task of converting Arabic and Roman numerals.

I posted the following code on rosettacode.org for the task of converting Arabic and Roman numerals.

import std.regex, std.array, std.algorithm;

immutable {
    int[] weights = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
    string[] symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", 
                        "V", "IV", "I"];
}

string toRoman(int n) {
    auto app = appender!string;
    foreach (i, w; weights) {
        while (n >= w) {
            app.put(symbols[i]);
            n -= w;
        }
        if (n == 0) break;
    }
    return app.data;
}

int toArabic(string s) {
    int arabic;
    foreach (m; match(s, "CM|CD|XC|XL|IX|IV|[MDCLXVI]")) {
        arabic += weights[symbols.indexOf(m.hit)];
    }
    return arabic;
}

It used to work just fine, but now I get a compiler error.

Error: template std.algorithm.indexOf(alias pred = "a == b",R1,R2) if (is(typeof(startsWith!(pred)(haystack,needl e)))) does not match any function template declaration

According to the documentation indexOf is deprecated, and countUntil sho开发者_如何转开发uld be used in stead, but it gives me the same error.


Long story but I'll try to keep it short:

std.algorithm.indexOf expects an input range, which is a structural type that must define front, popFront() and empty. For arrays, these methods are defined in std.array and work via uniform function call syntax, which allows fun(someArray) to work the same as someArray.fun().

immutable string[] is not an input range, since popFront removes the first element of the array, which cannot be done for an immutable type. The fact that this used to work was a bug.

I've updated the Rosetta Code entry to change symbols to an immutable(string)[]. Here, the elements of symbols are immutable, but the array may be sliced and reassigned. For example:

void main() {
    immutable string[] s1 = ["a", "b", "c"];
    immutable(string)[] s2 = ["d", "e", "f"];

    s2 = s2[1..$];  // This is what std.array.popFront does under the hood.
    assert(s2 == ["e", "f"]);  // Passes.
    s2[1] = "g";     // Error:  Can't modify immutable data.

    s1 = s1[1..$];  // Error:  Can't modify immutable data.
    s1[1] = "g";    // Error:  Can't modify immutable data.
}

immutable string[] is implicitly convertible to immutable(string)[] but implicit function template instantiation (often denoted IFTI; this is what's used to instantiate the indexOf template) is not smart enough try this.


I believe this is a bug in std.algorithm. If you remove the immutable qualifier, the code works as is. I think indexOf/countUntil should work on immutable arrays, but at the moment it does not.

You can make them manifest constants (precede each declaration with enum) and it appears to work. Amusingly, this may also be a bug.


Apologies for the breakage; I introduced it. I concur with dsimcha's description and proposed fix.

We are considering a simple change to the language to account for this simple case. That would automatically peel off one level of qualifiers when passing a value of a qualified type into a function. By that (for now hypothetical) rule, qualifier(T[]) would become (when passed to a function) qualifier(T)[] and qualifier(T*) would become qualifier(T)*. This would allow your example to work. The disadvantage is that a function would not be able to distinguish the top-level qualifier but I believe that that does not harm any concrete use.

0

精彩评论

暂无评论...
验证码 换一张
取 消