Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

d

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.

like image 981
fwend Avatar asked Mar 31 '11 10:03

fwend


3 Answers

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.

like image 157
dsimcha Avatar answered Nov 15 '22 13:11

dsimcha


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.

like image 42
Bernard Avatar answered Nov 15 '22 13:11

Bernard


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.

like image 40
Andrei Alexandrescu Avatar answered Nov 15 '22 15:11

Andrei Alexandrescu