简体   繁体   中英

Returning an n-dimensional data structure?

I have the following family of C# functions:

// 1D version
public Dictionary<int, double> GetNumCases1D(string varID1)
{
  Dictionary<int, double> cases1D = new Dictionary<int, double>();
  foreach (int value1 in ValuesFromVariable(varID1))
  {
    // do work
  }
  return cases1D;
}
// 2D version
public Dictionary<int, Dictionary<int, double>> GetNumCases2D(string varID1, string varID2)
{
  Dictionary<int, Dictionary<int, double>> cases2D = new Dictionary<int, Dictionary<int, double>>();
  foreach (int value2 in ValuesFromVariable(varID2))
  {
    // do work
    Dictionary<int, double> cases1D = new Dictionary<int, double>();
    foreach (int value in ValuesFromVariable(varID1))
    {
      // do more work
    }
  }
  return cases2D;
}
// 3D version
public Dictionary<int, Dictionary<int, Dictionary<int, double>>> GetNumCases3D(string varID1, string varID2, string varID3)
{
  Dictionary<int, Dictionary<int, Dictionary<int, double>>> cases3D = new Dictionary<int, Dictionary<int, Dictionary<int, double>>>();
  foreach (int value3 in ValuesFromVariable(varID3))
  {
    // do work
    Dictionary<int, Dictionary<int, double>> cases2D = new Dictionary<int, Dictionary<int, double>>();
    foreach (int value2 in ValuesFromVariable(varID2))
    {
      // do more work
      Dictionary<int, double> cases1D = new Dictionary<int, double>();
      foreach (int value in ValuesFromVariable(varID1))
      {
        // do more work^2
      }
    }
  }
  return cases3D;
}

What I would like is a single function version that could return an arbitrarily dimensioned data-structure, with a corresponding number of inner loops:

// n-dimensional version
public Dictionary<int, Dictionary<int, ... Dictionary<int, double>...>> GetNumCasesND(List<string> varIDs)
{
  Dictionary<int, Dictionary<int, ... Dictionary<int, double>...>> casesND = new Dictionary<int, Dictionary<int, ... Dictionary<int, double>...>>();
  foreach (int value1 in ValuesFromVariable(varIDs[0]))
  {
    // ??
    foreach (int value2 in ValuesFromVariable(varIDs[1]))
    {
      // ??
      ...
      foreach (int valueN in ValuesFromVariable(varIDs[N-1]))
      {
        // ??
      }
    }
  }
  return casesND;
}

This doesn't look like it's possible -- is it? And is there a better way?

这是不可能的,因为必须在编译时知道您的返回类型,但是您编写的伪代码取决于varIDs中的元素数量,直到运行时才知道。

Don´t listen to the naysayers ;) Yes, it is possible, with a little adjustment. It was an interesting question, so I wanted to be sure myself.

This is the single method you asked for:

    public Dictionary<int, object> GetNumCasesNDim(params string[] input)
    {
        var result = new Dictionary<int, object>();
        int dimensions = input.Length;

        if (dimensions == 1)
        {
            foreach (int value in ValuesFromVariable(input[dimensions - 1]))
            {
                result.Add(value, 0.01d /*dummy double*/);
            }
        }
        else
        {
            foreach (int value in ValuesFromVariable(input[dimensions - 1]))
            {
                var nextParams = new List<string>(input);
                nextParams.RemoveAt(nextParams.Count - 1);
                result.Add(value, GetNumCasesNDim(nextParams.ToArray()));                    
            }
        }

        return result;
    }

Test code, to compare with your own 3D method. I took the liberty to fill out the blanks with dummy data.

    // 3D version
    public Dictionary<int, Dictionary<int, Dictionary<int, double>>> GetNumCases3D(string varID1, string varID2, string varID3)
    {
        var cases3D = new Dictionary<int, Dictionary<int, Dictionary<int, double>>>();
        foreach (int value3 in ValuesFromVariable(varID3))
        {
            var cases2D = new Dictionary<int, Dictionary<int, double>>();
            cases3D[value3] = cases2D;
            foreach (int value2 in ValuesFromVariable(varID2))
            {
                var cases1D = new Dictionary<int, double>();
                cases2D[value2] = cases1D;
                foreach (int value in ValuesFromVariable(varID1))
                {
                    cases1D.Add(value, value + 0.1d);
                }
            }
        }
        return cases3D;
    }

    private static int nIndex;
    private List<int> ValuesFromVariable(string s)
    {
        var result = new List<int>();
        for (int i = 0; i < s.Length; ++i)
            result.Add(++nIndex);
        return result;
    }

    // n-dimensional version
    public Dictionary<int, object> GetNumCasesNDim(params string[] input)
    {
        var result = new Dictionary<int, object>();
        int dimensions = input.Length;

        if (dimensions == 1)
        {
            foreach (int value in ValuesFromVariable(input[dimensions - 1]))
            {
                result.Add(value, 0.01d);
            }
        }
        else
        {
            foreach (int value in ValuesFromVariable(input[dimensions - 1]))
            {
                var nextParams = new List<string>(input);
                int index = nextParams.Count - 1;
                nextParams.RemoveAt(index);
                result.Add(value, GetNumCasesNDim(nextParams.ToArray()));                    
            }
        }

        return result;
    }

    private void test()
    {
        nIndex = 0;
        var dim3 = GetNumCases3D("this", "is", "a");

        nIndex = 0;
        var testDimN = GetNumCasesNDim("this", "is", "a");

        nIndex = 0;
        var test2DimN = GetNumCasesNDim("this", "is", "a", "test");            
    }

How about something like this?

class MultidimensionalArray<TKey, TValue>
{
    private readonly Dictionary<string, TValue> _impl = new Dictionary<string, TValue>();

    public TValue this[IEnumerable<TKey> index]
    {    
        get { return _impl[ToStringKey(index)]; }
        set { _impl[ToStringKey(index)] = value; }
    }

    public TValue this[params TKey[] index]
    {
        get { return this[index.AsEnumerable()]; }
        set { this[index.AsEnumerable()] = value; }
    }

    private string ToStringKey(IEnumerable<TKey> key)
    {
        return string.Join(";", key.Select(k => k.ToString()));
    }
}

Usage:

var arr = new MultidimensionalArray<int, double>();
arr[1, 2, 3] = 3.5;
Console.WriteLine(arr[1, 2, 3]);

Easy extension:

class MultidimensionalArray<TValue>: MultidimensionalArray<object, TValue> {}

Usage:

var arr = new MultidimensionalArray<double>();
arr['Y', "Hell", 0] = 3.5;
Console.WriteLine(arr['Y', "Hell", 0]);

Another approach would be to use dynamic .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM