试图实现zipWith

Nor*_*löw 4 d range 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;
}
Run Code Online (Sandbox Code Playgroud)

但是

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

失败,错误

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))
Run Code Online (Sandbox Code Playgroud)

而且我不明白为什么.有线索吗?

更新:

在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]);
}
Run Code Online (Sandbox Code Playgroud)

是否有可能通过字符串扩展它以支持nary fun's zipWith!"a+b+c"(x,x,x)?我特别要求,因为我注意到naryFunstd.functional 中有代码,但它被注释掉了.

Vla*_*eev 5

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

    auto zipWith(alias fun, Ranges...)( // ...
    
    Run Code Online (Sandbox Code Playgroud)
  2. 您需要导入std.algorithmmap.

  3. std.range.zip将返回一个范围std.typecons.Tuple,它不会自动扩展到lambda的两个参数.您需要显式扩展元组.

固定代码:

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);
}
Run Code Online (Sandbox Code Playgroud)

是否有可能通过字符串扩展它以支持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);
}
Run Code Online (Sandbox Code Playgroud)