可以将任意长度的参数数组转换为嵌套循环吗?

时间:2017-11-22 07:50:53

标签: c# loops syntax nested

无论如何在C#(或任何其他网络语言)中实现以下目标?

public double nestedParamArrayLoop(function delegatedFunction, LoopControllers loopControllers)
{
    double total = 0;

    NestedLoopControllers loopControllers = new NestedLoopControllers(loopController, loopMaxes);

    foreach(LoopController loopController in loopControllers);
    {
        nestedfor (loopController)
        {
            // this line results in one or more loopControllers being passed in
            total += delegatedFunction(loopController);
        }
    }

    return total;
}

public double delegatedFunction(params int[] arguments)
{
    // dummy function to compute product of values
    long product = 1;
    for (int i = 0; i < arguments.Count ; i++)
        product *= arguments[i];
    return product;
}

如果使用可变数量的参数调用delegatedFunction,那么根据数组​​loopControllers中的控制器数量?每个loopController都包含一个起始值,一个最大值和一个增量值(即模板a for for循环)。

上面的语法并不常用,因为我不确定是否存在捕获此范例。但是这个想法是你可以指定任意数量的嵌套循环,然后由编译器(或运行时)为你完成嵌套。所以它是一种模板化的嵌套,你可以为任意数量的循环定义循环条件,环境为你构造循环。

例如

  • NestedParamsArrayLoop(delegatedFunction,loopContoller1);导致对delegatedFunction的迭代调用(loopValue1的值);
  • NestedParamsArrayLoop(delegatedFunction,loopContoller1,loopController2);结果是 对delegatedFunction的迭代调用(loopValue1的值,loopValue2的值);
  • NestedParamsArrayLoop(delegatedFunction,loopContoller1的值,loopController2,loopController3的值的值);结果是 对delegatedFunction的迭代调用(loopValue1,loopValue2的值,loopValue3的值);

这样做的目的是避免使用不同数量的参数编写单独的函数,但逻辑的实际内容在它们之间是通用的。

我希望我在解释这个方面做得不错,但如果没有,请问!

3 个答案:

答案 0 :(得分:1)

我认为这几乎就是你想做的事情。

LoopController定义开始:

public class LoopController : IEnumerable<int>
{
    public int Start;
    public int End;
    public int Increment;

    private IEnumerable<int> Enumerate()
    {
        var i = this.Start;
        while (i <= this.End)
        {
            yield return i;
            i += this.Increment;
        }
    }

    public IEnumerator<int> GetEnumerator()
    {
        return this.Enumerate().GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

现在您可以像这样定义NestedParamArrayLoop

public double NestedParamArrayLoop(Func<int[], double> delegatedFunction, List<LoopController> loopControllers)
{
    double total = 0;

    foreach (LoopController loopController in loopControllers)
    {
        total += delegatedFunction(loopController.ToArray());
    }

    return total;
}

现在剩下的很简单:

void Main()
{
    var loopControllers = new List<LoopController>()
    {
        new LoopController() { Start = 4, End = 10, Increment = 2 },
        new LoopController() { Start = 17, End = 19, Increment = 1 },
    };
    Console.WriteLine(NestedParamArrayLoop(DelegatedFunction, loopControllers));
}

public double DelegatedFunction(params int[] arguments)
{
    long product = 1;
    for (int i = 0; i < arguments.Count(); i++)
        product *= arguments[i];
    return product;
}

您甚至可以将NestedParamArrayLoop定义为:

public double NestedParamArrayLoop(Func<int[], double> delegatedFunction, List<LoopController> loopControllers)
{
    return
        loopControllers
            .Select(lc => delegatedFunction(lc.ToArray()))
            .Sum();
}

这更像是你之后的事吗?

public double NestedParamArrayLoop(Func<int[], double> delegatedFunction, List<LoopController> loopControllers)
{
    Func<IEnumerable<int>, IEnumerable<IEnumerable<int>>> getAllSubsets = null;
    getAllSubsets = xs =>
        (xs == null || !xs.Any())
            ? Enumerable.Empty<IEnumerable<int>>()
            : xs.Skip(1).Any()
                ? getAllSubsets(xs.Skip(1))
                    .SelectMany(ys => new[] { ys, xs.Take(1).Concat(ys) })
                : new[] { Enumerable.Empty<int>(), xs.Take(1) };

    double total = 0;


    foreach (LoopController loopController in loopControllers)
    {
        foreach (var subset in getAllSubsets(loopController))
        {
            total += delegatedFunction(subset.ToArray());
        }
    }

    return total;
}

答案 1 :(得分:0)

由于OP要求使用任何.NET语言的解决方案,我在F#中编写了一个(翻译@Enigmativity的答案)。不需要NestedLoopController类:

[[ 4 .. 2 .. 10 ]; [ 17 .. 1 .. 19 ]]
|> Seq.map (fun args ->
    (1L, args)
    ||> Seq.fold (fun a x -> a * int64 x))
|> Seq.sum
|> printfn "%d"

您可以以相对简单的方式将其转换为C#LINQ ......

答案 2 :(得分:0)

我认为你真正需要的是多套笛卡尔积。 Here是Eric Lippert关于在C#中使用任意数量的集合做的好文章。所以创建这样的函数(我不会解释它,因为我不能比Eric在他的文章中做得更好):

static IEnumerable<IEnumerable<T>> CartesianProduct<T>(params IEnumerable<T>[] sources) {
    IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };
    foreach (var source in sources) {
        var tmp = source;
        result = result.SelectMany(
            seq => tmp,
            (seq, item) => seq.Concat(new[] { item }));
    }               
    return result;
}

然后像这样使用:

foreach (var n in CartesianProduct(Enumerable.Range(1, 4), Enumerable.Range(1, 4))) {
    Console.WriteLine(String.Join(", ", n));
    // in your case: delegatedFunction(n);
}

输出

1, 1
1, 2
1, 3
1, 4
2, 1
2, 2
2, 3
2, 4
3, 1
3, 2
3, 3
3, 4
4, 1
4, 2
4, 3
4, 4

使用Enumerable.Range替换LoopController很容易,Enumerable.Range仅作为示例使用。