Чистота Фобоса уменьшить

Почему std.algorithm.reduce на Фобосе не чисто? Это нерешенная проблема или есть причина, по которой этого не может быть?

Имеет ли это какое-то отношение к вопросу: "Как выглядит чистая функция", — задал Андрей в заключительной лекции на DConf 2013?

См.: http://forum.dlang.orgthread/[email protected]

Я хочу, чтобы функция sparseness в следующем коде была чистой. Думаю, я всегда могу заменить reduce циклом foreach, верно?

import std.algorithm: reduce, min, max;
import std.typetuple: templateAnd;
import std.traits: isArray, Unqual;
import std.range: ElementType, isInputRange, isBidirectionalRange, isFloatingPoint;

//** Returns: true if $(D a) is set to the default value of its type. */
bool defaulted(T)(T x) @safe pure nothrow { return x == T.init; }
alias defaulted untouched;

/** Returns: Number of Default-Initialized (Zero) Elements in $(D range). */
size_t sparseness(T)(in T x, int recurseDepth = -1) @trusted /* pure nothrow */ {
    import std.traits: isStaticArray;
    static if (isStaticArray!T ||
               isInputRange!T) {
        import std.range: empty;
        immutable isEmpty = x.empty;
        if (isEmpty || recurseDepth == 0) {
            return isEmpty;
        } else {
            const nextDepth = (recurseDepth == -1 ?
                               recurseDepth :
                               recurseDepth - 1);
            static if (isStaticArray!T) { // TODO: We can't algorithms be applied to static arrays?
                typeof(return) ret;
                foreach (ref elt; x) { ret += elt.sparseness(nextDepth); }
                return ret;
            } else {
                import std.algorithm: map, reduce;
                return reduce!"a+b"(x.map!(a => a.sparseness(nextDepth)));
            }
        }
    } else static if (isFloatingPoint!T) {
        return x == 0; // explicit zero because T.init is nan here
    } else {
        return x.defaulted;
    }
}
unittest {
    assert(1.sparseness == 0);
    assert(0.sparseness == 1);
    assert(0.0.sparseness == 1);
    assert(0.1.sparseness == 0);
    assert(0.0f.sparseness == 1);
    assert(0.1f.sparseness == 0);
    assert("".sparseness == 1);
    assert(null.sparseness == 1);
    immutable ubyte[3]    x3   = [1, 2, 3];    assert(x3[].sparseness == 0);
    immutable float[3]    f3   = [1, 2, 3];    assert(f3[].sparseness == 0);
    immutable ubyte[2][2] x22  = [0, 1, 0, 1]; assert(x22[].sparseness == 2);
    immutable ubyte[2][2] x22z = [0, 0, 0, 0]; assert(x22z[].sparseness == 4);
}

Обновление:

Вместо этого я решил использовать isIterable и foreach вместо вышеперечисленных, так как это работает и для меня прямо сейчас и делает вещи @safe pure nothrow. Я не вижу необходимости прямо сейчас использовать функции высшего порядка для решения этой проблемы. Я также нашел, что грядущий std.rational Давида Симчаса очень естественно использовать здесь:

import rational: Rational;

/** Returns: Number of Default-Initialized (Zero) Elements in $(D x) at
    recursion depth $(D depth).
*/
Rational!ulong sparseness(T)(in T x, int depth = -1) @safe pure nothrow {
    alias R = typeof(return); // rational shorthand
    static if (isIterable!T) {
        import std.range: empty;
        immutable isEmpty = x.empty;
        if (isEmpty || depth == 0) {
            return R(isEmpty, 1);
        } else {
            immutable nextDepth = (depth == -1 ? depth : depth - 1);
            ulong nums, denoms;
            foreach (ref elt; x) {
                auto sub = elt.sparseness(nextDepth);
                nums += sub.numerator;
                denoms += sub.denominator;
            }
            return R(nums, denoms);
        }
    } else static if (isFloatingPoint!T) {
        return R(x == 0, 1); // explicit zero because T.init is nan here
    } else {
        return R(x.defaulted, 1);
    }
}

person Nordlöw    schedule 26.12.2013    source источник
comment
Это может быть связано с тем, что помощник unaryFunc не является чистым. Что произойдет, если вы измените a+b, чтобы вместо этого ссылаться на отдельную написанную вами функцию, которая явно помечена как чистая? так что pure int add(int a, int b) { return a+b; } в области модуля, а затем reduce!add(...). Я знаю, что это немного хлопотно, но просто хочу посмотреть, что произойдет. (Я бы проверил сам, но мой кабель все еще отсутствует дома, поэтому я не могу удаленно подключиться, и я нахожусь на своем устройстве без инструментов разработчика!)   -  person Adam D. Ruppe    schedule 26.12.2013


Ответы (1)


Если вы измените nextDepth на immutable, а не на const, то sparseness будет pure.

Я считаю, что это ошибка, это может быть связано с тем, что замыкание передается reduce, захватывая nextDepth, и по какой-то причине думает, что оно может быть изменчивым, потому что это const. Однако значения, объявленные как const, идентичны значениям, объявленным как immutable — разница проявляется только косвенно — поэтому я считаю, что это ошибка.

Вы можете зарегистрировать минимальный случай воспроизведения как ошибку.

(однако это не может быть nothrow, потому что reduce на самом деле может кидать)

person Peter Alexander    schedule 26.12.2013