2

Is there anyway to achieve the following in C# (or any other ,Net language)?

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; } 

Where delegatedFunction is called with a variable number of parameters, according to the number of controllers in the array loopControllers? Each loopController would contain a start value, a max value and an increment value (i.e. template a for loop).

The syntax above doesn't quite work as I'm not sure any exists to capture this paradigm. But the idea is that you can specify an arbitrary number of nested loops and then the nesting is done for you by the compiler (or the runtime). So it's a kind of templated nesting where you define the loop conditions for an arbitrary number of loops and the environment constructs the loops for you.

For example

  • NestedParamsArrayLoop(delegatedFunction, loopContoller1); results in iterated calls to delegatedFunction(values for loopValue1);
  • NestedParamsArrayLoop(delegatedFunction, loopContoller1, loopController2); results in iterated calls to delegatedFunction(values for loopValue1, values for loopValue2);
  • NestedParamsArrayLoop(delegatedFunction, values for loopContoller1, values for values for loopController2, loopController3); results in iterated calls to delegatedFunction(loopValue1, values for loopValue2, values for loopValue3);

The goal of this is to avoid writing separate functions with different numbers of arguments but where the actual guts of the logic is common across them.

I hope I've done a decent job of explaining this but if not please ask!

9
  • I suppose you may achieve this by a recursive method. Commented Nov 22, 2017 at 7:56
  • 1
    The "syntax" in your question looks more like pseudo code then like c#... Commented Nov 22, 2017 at 7:56
  • Well yes it has to be pseudo-code, because I don't know how to write the real thing (if it's even possible). Commented Nov 22, 2017 at 7:57
  • So if I pass 3 "controllers" that will result in 3 nested for loops (say with loop variables i,j,k), and inside all those loops is call to delegateFunction(i,j,k)? Commented Nov 22, 2017 at 7:58
  • 1
    Seems like an XY-problem. You think the solution of your problem is Y, thus you want to solve that, instead of asking what the actual solution for your original problem X was. So what actual problem are you trying to solve? Commented Nov 22, 2017 at 7:58

3 Answers 3

1

I think this is pretty much what you want to do.

Start with a LoopController definition:

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(); } } 

Now you can define NestedParamArrayLoop like so:

public double NestedParamArrayLoop(Func<int[], double> delegatedFunction, List<LoopController> loopControllers) { double total = 0; foreach (LoopController loopController in loopControllers) { total += delegatedFunction(loopController.ToArray()); } return total; } 

Now the rest is easy:

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; } 

You could even define NestedParamArrayLoop as this:

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

Is this more like what you're after?

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; } 
Sign up to request clarification or add additional context in comments.

9 Comments

Wow let me try that.... oh man that's really clever. I had an idea that a yield might be involved.... thanks!! Will get back to you later today...
Nice. I was starting to write something similar but you where faster. One thing to note is that your implementation requires Start to be less than Stop - otherwise you get an infinite loop in your Enumerate() function.
@BitRacketeer I thought you need another thing. There are no nested loops here
@Evk - The OP needed to expand the parameters to pass to the delegated function - I think that's what he meant by a nested loop.
@Evk & Enigmativity - I want to iterate each parameter in the arbitrary-length parameter array. So with one parameter 1, 2, 3, 4. With two parameters (1,1), (1,2), (1,3), (1,4), then (2,1), (2,2), (2,3), (2,4). Then (3,1)...(3,4). And with three params (1,1,1), (1,1,2),(1,1,3),(...); then (2,1,1).(2,1,2),(2,1,3)(...) etc. I find it hard to explain in words - "arbitrary-depth nesting or a parameter array" is the closest I can get so far.
|
0

Since the OP asked for a solution in any .NET language, I wrote one in F# (translating @Enigmativity's answer). No NestedLoopController class required:

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

You can probably translate this to C# LINQ in a relatively straightforward way…

Comments

0

I think what you really need is what is called cartesian product of multiple sets. Here is good article from Eric Lippert about doing that with arbitrary number of sets in C#. So create function like this (I won't explain it because I cannot do this better than Eric did in his article):

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; } 

Then use like this:

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

outputs

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 

It's easy to replace Enumerable.Range with your LoopController, Enumerable.Range is used just as an example.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.