У меня есть следующее семейство функций C #:

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

Я бы хотел одну версию функции, которая могла бы возвращать структуру данных произвольного размера с соответствующим количеством внутренних циклов:

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

Не похоже, что это возможно - не так ли? А есть ли способ лучше?

2
Ed Graham 26 Фев 2014 в 02:31
6
Это не проблема с вашим кодом GetNumCases(), это проблема с тем, как вы представляете свои данные. Dictionary<int, Dictionary<int, Dictionary<int, double>>> - ужасно плохой способ представления данных и должен быть наказан тюремным заключением. Вам нужно сесть и подумать, с какими данными вы имеете дело, и создать правильный набор классов strongly typed, которые представляют эти данные, вместо того, чтобы использовать словари и int для всего.
 – 
Federico Berasategui
26 Фев 2014 в 02:35
1
Многие параметры метода делают вашу программу недоступной для обслуживания. Произвольное количество параметров универсального типа универсального типа с параметрами более универсального типа универсального типа сделает вашу программу совершенно невозможной для написания, отладки, сопровождения и использования в дальнейшем.
 – 
Arve Systad
26 Фев 2014 в 02:38
Спасибо за комментарии, ребята. У меня есть n-мерный (гипер) куб данных: например, структура, эквивалентная двойным значениям [i_1, i_2, ..., i_n] с n индексами, где n устанавливается во время выполнения. Какие-либо предложения?
 – 
Ed Graham
26 Фев 2014 в 03:28

3 ответа

Лучший ответ

Не слушайте скептиков;) Да, возможно, с небольшой корректировкой. Это был интересный вопрос, поэтому я сам хотел убедиться.

Это единственный метод, о котором вы просили:

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

Тестовый код для сравнения с вашим собственным 3D-методом. Я взял на себя смелость заполнить пустые поля фиктивными данными.

    // 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");            
    }
1
JonPall 26 Фев 2014 в 05:04
Я очень признателен, Джон - спасибо! Я попробую завтра.
 – 
Ed Graham
26 Фев 2014 в 10:46

Это просто невозможно, потому что ваш возвращаемый тип должен быть известен во время компиляции, но псевдокод, который вы написали, зависит от количества элементов в varIDs, которое неизвестно до времени выполнения.

3
John Gibb 26 Фев 2014 в 02:47
Спасибо, Джон - я подумал, что может быть общий или шаблонный тип, который каким-то образом справится с этим. Итак, что люди обычно делают в этой ситуации (когда они хотят вернуть n-мерную структуру)?
 – 
Ed Graham
26 Фев 2014 в 03:02
1
@EdGraham: Обычно в такой ситуации они переосмысливают свою модель данных. Вам нужно спросить себя, чего вы на самом деле пытаетесь достичь, и есть ли другая организация данных, которая была бы более интуитивно понятной и простой для кодирования.
 – 
Jim Mischel
26 Фев 2014 в 03:09
Спасибо, Джим. У меня есть n-мерный (гипер) куб данных: например, структура, эквивалентная двойным значениям [i_1, i_2, ..., i_n], где n устанавливается во время выполнения. Есть идеи для переосмысления?
 – 
Ed Graham
26 Фев 2014 в 03:26
Возможно, если тип возвращаемого значения - Dictionary . Взгляните на мой код. Не очень красиво, но работает.
 – 
JonPall
26 Фев 2014 в 04:54
Правда, это возможно, если вы готовы отказаться от типобезопасности ... На вопрос, похоже, он хотел сохранить строгую типизацию. @EdGraham Существует способ писать шаблоны, которые превращаются в код C # с использованием шаблонов T4, которые можно использовать для генерации конечного числа измерений. Подробнее см. hanselman.com/blog/…
 – 
John Gibb
26 Фев 2014 в 08:23

Как насчет этого?

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

Использование:

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

Простое расширение:

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

Использование:

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

Другой подход - использовать dynamic.

1
Grozz 26 Фев 2014 в 07:11
Большое спасибо, Grozz, я займусь этим завтра.
 – 
Ed Graham
26 Фев 2014 в 10:46