试图实现zipWith

时间:2014-01-18 22:48:01

标签: range d higher-order-functions

我正在尝试实现经典的高阶范围zipWith,如下所示

import std.traits: allSatisfy;
import std.range: isInputRange;

auto zipWith(fun, Ranges...)(Ranges ranges) if (Ranges.length >= 2 && allSatisfy!(isInputRange, Ranges))
{
    import std.range: zip;
    return zip(ranges).map!fun;
}

但是

unittest
{
    auto x = [1, 2, 3, 4, 5];
    zipWith!((a, b) => a + b)(x, x);
}

失败并显示错误

template algorithm_ex.zipWith cannot deduce function from argument types !((a, b) => a + b)(int[], int[]), candidates are: (d-dmd-unittest)
algorithm_ex.zipWith(fun, Ranges...)(Ranges ranges) if (Ranges.length && allSatisfy!(isInputRange, Ranges))

我不明白为什么。有线索吗?

更新

在Cyber​​Shadows回答之后,我现在有了

import std.traits: allSatisfy;

/** Zip $(D ranges) together with operation $(D fun).
   TODO: Simplify when Issue 8715 is fixed providing zipWith
 */
auto zipWith(alias fun, Ranges...)(Ranges ranges) if (Ranges.length >= 2 && allSatisfy!(isInputRange, Ranges)) {
    import std.range: zip;
    import std.algorithm: map;
    import std.functional: binaryFun;
    static if (ranges.length == 2)
        return zip(ranges).map!(a => binaryFun!fun(a.expand));
    else if (ranges.length >= 3)
        return zip(ranges).map!(a => naryFun(a.expand));
    else
        static assert(false, "Need at least 2 range arguments.");
}
unittest {
    auto x = [1, 2, 3];
    import std.array: array;
    assert(zipWith!"a+b"(x, x).array == [2, 4, 6]);
    assert(zipWith!((a, b) => a + b)(x, x).array == [2, 4, 6]);
    assert(zipWith!"a+b+c"(x, x, x).array == [3, 6, 9]);
}

是否可以通过字符串扩展它来支持nary fun,例如zipWith!"a+b+c"(x,x,x)?我特别要求,因为我注意到std.functional中存在naryFun的代码,但它被注释掉了。

1 个答案:

答案 0 :(得分:5)

  1. 您必须将fun模板参数声明为alias参数,否则将其声明为类型参数:

    auto zipWith(alias fun, Ranges...)( // ...
    
  2. 您需要为std.algorithm导入map

  3. std.range.zip将返回std.typecons.Tuple的范围,该范围不会自动扩展为lambda的两个参数。您需要明确地扩展元组。

  4. 固定代码:

    import std.traits: allSatisfy;
    import std.range: isInputRange;
    import std.algorithm: map;
    
    auto zipWith(alias fun, Ranges...)(Ranges ranges) if (Ranges.length >= 2 && allSatisfy!(isInputRange, Ranges))
    {
        import std.range: zip;
        return zip(ranges).map!(t => fun(t.expand));
    }
    
    unittest
    {
        auto x = [1, 2, 3, 4, 5];
        zipWith!((a, b) => a + b)(x, x);
    }
    

      

    是否可以通过字符串扩展它以支持nary fun's,例如zipWith!"a+b+c"(x,x,x)

    我不明白为什么不:

    import std.string;
    
    private string genNaryFun(string fun, V...)()
    {
        string code;
        foreach (n, v; V)
            code ~= "alias values[%d] %s;".format(n, cast(char)('a'+n));
        code ~= "return " ~ fun ~ ";";
        return code;
    }
    
    template naryFun(string fun)
    {
        auto naryFun(V...)(V values)
        {
            mixin(genNaryFun!(fun, V));
        }
    }
    
    unittest
    {
        alias naryFun!"a + b + c" test;
        assert(test(1, 2, 3) == 6);
    }