Search code examples
templatesdphobos

Cannot resolve type for template function


I'm trying to code up something very simple in D, but I'm having a few problems with one of the standard library template functions (specifically, nextPermutation from std.algorithm).

The crux of what I'm trying to do is to create all permutations of pandigital numbers (that is, numbers including all the values 1 to 9 exactly once).

To do this, I've done the following:

import std.algorithm;
import std.conv;

int[] pandigitals()
{
    char[] initial = "123456789".dup;
    auto pan = [to!int(initial)];
    while(nextPermutation!(initial)) {
       pan ~= to!int(initial);
    }
    return pan;
}

This gives me the error:

Error: cannot resolve type for nextPermutation!(initial)

I've also tried to explicitly set the types:

while(nextPermutation!("a<b", char[])(initial))

However, this gives an error saying it cannot match the template:

Error: template instance std.algorithm.nextPermutation!("a < b", char[]) does not match template declaration nextPermutation(alias less = "a < b", BidirectionalRange)(ref BidirectionalRange range) if (isBidirectionalRange!BidirectionalRange && hasSwappableElements!BidirectionalRange)

What is the correct form of the call meant to be?


Solution

  • Well, your first problem is that you're passing initial as a template argument instead of a function argument. The !() is for template arguments. so, instead of

    while(nextPermutation!(initial))
    

    you need to do

    while(nextPermutation(initial)) {
    

    Now, that will still give you an error.

    q.d(10): Error: template std.algorithm.nextPermutation cannot deduce function from argument types !()(char[]), candidates are:
    /usr/include/D/phobos/std/algorithm.d(12351):        std.algorithm.nextPermutation(alias less = "a<b", BidirectionalRange)(ref BidirectionalRange range) if (isBidirectionalRange!BidirectionalRange && hasSwappableElements!BidirectionalRange)
    

    And that's because hasSwappableElements!(char[]) is false, and per nextPermutations' template constraint it needs to be true for a type to work with nextPermutations.

    It's false because all strings are treated as ranges of dchar rather than their actual element type. This is because in UTF-8 (char) and UTF-16 (wchar), there are multiple code units per code point, so operating on individual code units could break up a code point, whereas in UTF-32 (dchar), there's always one code unit per code point. Essentially, if arrays of char or wchar were treated as ranges of char or wchar, you'd run a high risk of breaking up characters so that you'd end up with pieces of characters rather than whole characters. So, in general in D, if you want to operate on an individual character, you should use dchar, not char or wchar. If you're not very familiar with Unicode, I'd suggest reading this article by Joel Spoelsky on the subject.

    However, regardless of why hasSwappableElements!(char[]) is false, it is false, so you're going to need to use a different type. The simplest thing would probably be to just swap your algorithm over to using dchar[] instead.

    int[] pandigitals()
    {
        dchar[] initial = "123456789"d.dup;
        auto pan = [to!int(initial)];
        while(nextPermutation(initial)) {
           pan ~= to!int(initial);
        }
        return pan;
    }