Search code examples
d

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


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 should be used in stead, but it gives me the same error.


Solution

  • 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.