Петля над массивом неизвестного измерения в C#
Предыдущий пост объясняет, как перебирать записи в одномерном или двумерном массиве, но что вы будете делать, если не знаете, сколько измерений имеет массив? По общему признанию, это необычная ситуация, но даже в этом случае вы можете перебирать значения, чтобы принять меры на них.
В этом примере используется следующий код для инициализации трехмерного массива и отображения его текстового представления. Метод ArrayTextValue, который описывается в скором времени, пересекает массив для создания текстового представления.
private void Form1_Load(object sender, EventArgs e)
{
// Список значений в массиве неизвестных измерений.
string[, ,] values3 =
{
{
{ "(0, 0, 0)", "(0, 0, 1)" },
{ "(0, 1, 0)", "(0, 1, 1)" },
{ "(0, 2, 0)", "(0, 2, 1)" },
},
{
{ "(1, 0, 0)", "(1, 0, 1)" },
{ "(1, 1, 0)", "(1, 1, 1)" },
{ "(1, 2, 0)", "(1, 2, 1)" },
},
{
{ "(2, 0, 0)", "(2, 0, 1)" },
{ "(2, 1, 0)", "(2, 1, 1)" },
{ "(2, 2, 0)", "(2, 2, 1)" },
},
{
{ "(3, 0, 0)", "(3, 0, 1)" },
{ "(3, 1, 0)", "(3, 1, 1)" },
{ "(3, 2, 0)", "(3, 2, 1)" },
},
};
txtValues.Text = ArrayTextValue(values3);
txtValues.Select(0, 0);
}
Один из самых больших трюков здесь - это то, что вы хотите делать со значениями. В этом примере я решил создать строку, показанную на картинке. Он отображает каждый размер массива с отступом и окружен фигурными фигурными скобками, несколько похожим на предыдущий код, который инициализирует массив. Самый внутренний уровень массива отображается в одной строке со значениями, заключенными в круглые скобки.
В этом примере используется рекурсия для посещения каждого из параметров массива. Когда он посещает последнее измерение, код создает строку, содержащую значения в одной строке, как показано на рисунке. Этот метод является гибким и не настолько жестким, хотя он может быть немного запутанным.
В следующем коде показан метод ArrayTextValue, который создает текстовое представление.
// Возвращаем строку, содержащую значения в
// массив неизвестного измерения.
private string ArrayTextValue(Array values)
{
// Делаем массив для хранения индексов.
int num_dimensions = values.Rank;
int[] indices = new int[num_dimensions];
// Получить и отобразить текстовое представление массива.
return GetArrayTextValues(values, 0, indices, 0);
}
Этот код использует свойство массива Rank, чтобы получить количество измерений массива. Затем он заставляет массив удерживать индексы для каждого измерения. Например, если есть 4 измерения, и массив индексов содержит значения {2, 3, 3, 5}, тогда код рассматривает значения массива [2, 3, 3, 5] . р>
Затем метод вызывает метод GetArrayTextValues для выполнения реальной работы, передавая ему массив, количество, на которое он должен отступать от текста (изначально 0), массива индексов, индекса в массив, который GetArrayTextValues должен учитывать. Этот код передает 0 для окончательного аргумента, поэтому GetArrayTextValues начинается с назначения первого индекса в массиве индексов.
В следующем коде показан метод GetArrayTextValues.
// Рекурсивно возвращать строковое представление
// значения в массиве из данной позиции вперед.
private string GetArrayTextValues(Array values, int indent,
int[] indices, int dimension_num)
{
string spaces = new string(' ', indent);
string txt = spaces + "{";
// Перебираем значения по этому индексу.
int max_index = values.GetUpperBound(dimension_num);
for (int i = 0; i <= max_index; i++)
{
indices[dimension_num] = i;
// Смотрите, является ли это последним измерением.
if (dimension_num == values.Rank - 2)
{
// Это следующий за последним измерением. Верните значение.
txt += Environment.NewLine + spaces + " { " +
GetArrayInnermostData(values, indices) + " }";
}
else
{
// Это не последнее измерение. Recurse.
txt += Environment.NewLine +
GetArrayTextValues(values, indent + 4,
indices, dimension_num + 1);
}
}
txt += Environment.NewLine + spaces + "}";
return txt;
}
Этот метод отвечает за рекурсию. Он начинается с создания строки, которая правильно отступом для этого уровня массива. Затем он получает верхнюю границу для рассматриваемого измерения и делает переменную i петлей над диапазоном значений, допустимых для этого измерения. Для каждого значения индекса в этом измерении программа проверяет, рассматривает ли он измерение второго по величине массива.
Если это второе измерение, код вызывает метод GetInnermostData, чтобы получить представление последнего измерения данных - измерение, которое содержит фактические данные, которые должны отображаться в одна строка вывода.
Если это не второе измерение, метод вызывает себя рекурсивно для обработки следующего измерения массива.
Следующий код показывает метод GetArrayInnermostData.
// Возвращает самую внутреннюю строку данных, разделенную пробелами.
private string GetArrayInnermostData(Array values, int[] indices)
{
string txt = "";
// Получить индекс последнего измерения.
int dimension_num = values.Rank - 1;
// Сопоставляем значения.
int max_index = values.GetUpperBound(dimension_num);
for (int i = 0; i <= max_index; i++)
{
indices[dimension_num] = i;
txt += " " + values.GetValue(indices).ToString();
}
txt = txt.Substring(1);
return txt;
}
Этот метод довольно прост. Он получает верхнюю границу для окончательного измерения и петли над своим диапазоном, соединяя значения массива.
