C #: установка всех значений в массиве с произвольными измерениями - PullRequest
3 голосов
/ 27 сентября 2010

Я ищу способ установить каждое значение в многомерном массиве на одно значение. Проблема в том, что число измерений неизвестно во время компиляции - оно может быть одномерным, оно может быть четырехмерным. Поскольку foreach не позволяет вам устанавливать значения, как я могу достичь этой цели? Большое спасибо.

Ответы [ 5 ]

4 голосов
/ 27 сентября 2010

Хотя эта проблема на первый взгляд кажется простой, на самом деле она сложнее, чем кажется. Однако, признавая, что посещение каждой позиции в многомерном (или даже зубчатом) массиве является декартовым произведениемоперация на множестве индексов массива - мы можем упростить решение ... и в конечном итоге написать более элегантное решение.

Мы собираемся использовать LINQ Эрика ЛиппертаДекартовой продукт реализация для выполнения тяжелой работы. Вы можете прочитать больше о том, как это работает, в его блоге, если хотите.

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

public static class EnumerableExt
{
    // Eric Lippert's Cartesian Product operator...
    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
          this IEnumerable<IEnumerable<T>> sequences)
    {
        IEnumerable<IEnumerable<T>> emptyProduct = 
                   new[] { Enumerable.Empty<T>() };
        return sequences.Aggregate(
          emptyProduct,
          (accumulator, sequence) =>
            from accseq in accumulator
            from item in sequence
            select accseq.Concat(new[] { item }));
    }
}

class MDFill
{
    public static void Main()
    {
        // create an arbitrary multidimensional array
        Array mdArray = new int[2,3,4,5];

        // create a sequences of sequences representing all of the possible
        // index positions of each dimension within the MD-array
        var dimensionBounds = 
            Enumerable.Range(0, mdArray.Rank)
               .Select(x => Enumerable.Range(mdArray.GetLowerBound(x),
                      mdArray.GetUpperBound(x) - mdArray.GetLowerBound(x)+1));

        // use the cartesian product to visit every permutation of indexes
        // in the MD array and set each position to a specific value...
        int someValue = 100;
        foreach( var indexSet in dimensionBounds.CartesianProduct() )
        {
            mdArray.SetValue( someValue, indexSet.ToArray() );
        }
    }
}

Теперь тривиально выделить этот код в метод многократного использования, который можно использовать как для зубчатых, так и для многомерных массивов.... или любую структуру данных, которую можно рассматривать как прямоугольный массив.

1 голос
/ 27 сентября 2010

Array.Length сообщит вам количество элементов, которые массив был объявлен для хранения, поэтому массив массивов (прямоугольных или зубчатых) можно просмотреть следующим образом:

for(var i=0; i<myMDArray.Length; i++)
   for(var j=0; j < myMDArray[i].Length; i++)
      DoSomethingTo(myMDArray[i][j]);

Если массив прямоугольный (все дочерние массивы имеют одинаковую длину), вы можете получить длину первого массива и сохранить ее в переменной; это немного улучшит производительность.

Когда число измерений неизвестно, это можно сделать рекурсивным:

public void SetValueOn(Array theArray, Object theValue)
{
   if(theArray[0] is Array) //we haven't hit bottom yet
      for(int a=0;a<theArray.Length;a++)
         SetValueOn(theArray[a], theValue);
   else if(theValue.GetType().IsAssignableFrom(theArray[0].GetType()))
      for(int i=0;i<theArray.Length;i++)
         theArray[i] = theValue;
   else throw new ArgumentException(
         "theValue is not assignable to elements of theArray");
}
0 голосов
/ 27 сентября 2010

Метод Array.Clear позволит вам очистить (установить значение по умолчанию) все элементы в многомерном массиве (т. Е. int[,]).Поэтому, если вы просто хотите очистить массив, вы можете написать Array.Clear(myArray, 0, myArray.Length);

Похоже, не существует метода, который установил бы все элементы массива в произвольное значение.

Обратите внимание, что если бы вы использовали это для зубчатого массива (то есть int[][]), вы бы получили массив нулевых ссылок на массивы.То есть, если бы вы написали:

int[][] myArray;
// do some stuff to initialize the array
// now clear the array
Array.Clear(myArray, 0, myArray.Length);

Тогда myArray[0] будет null.

0 голосов
/ 27 сентября 2010

Я думаю, что прямого способа сделать это не существует, поэтому вам нужно использовать свойство Rank, чтобы получить число измерений, и метод SetValue (который принимает в качестве аргумента массив с индексом для каждого измерения).).

Небольшой фрагмент кода для начала работы (для стандартных многомерных массивов):

bool IncrementLastIndex(Array ar, int[] indices) {
  // Return 'false' if indices[i] == ar.GetLength(i) for all 'i'
  // otherwise, find the last index such that it can be incremented,
  // increment it and set all larger indices to 0
  for(int dim = indices.Length - 1; dim >= 0; dim--) { 
    if (indices[dim] < ar.GetLength(dim)) {
      indices[dim]++;
      for(int i = dim + 1; i < indices.Length; i++) indices[i] = 0;
      return;
    }
  }
}

void ClearArray(Array ar, object val) {
  var indices = new int[ar.Rank];
  do {
    // Set the value in the array to specified value
    ar.SetValue(val, indices);
  } while(IncrementLastIndex(ar, indices));
}
0 голосов
/ 27 сентября 2010

Вы говорите, что хотите перебирать каждый элемент и (если доступно) каждое измерение массива и устанавливать каждое значение по пути?

Если бы это было так, вы бы сделали рекурсивную функцию, которая выполняет итерации измерений и устанавливает значения. Проверьте свойство Array.Rank на MSDN и функцию Array.GetUpperBound на MSDN.

Наконец, я уверен, что у общего типа List<T> есть какой-то способ сделать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...