2010-04-10 3 views
5

Я работаю с многомерными массивами bool, int и различными struct. Код проходит через эти массивы и выполняет некоторую операцию с определенными значениями. Например,Использование LINQ для извлечения конкретных значений из многомерного массива

 for (int x = 0; x < this.Size.Width; x++) { 
      for (int y = 0; y < this.Size.Height; y++) { 
       if (this.Map[x, y]) { 
        DrawTerrain(this.Tile[x, y].Location, Terrain.Water); 
       } 
      } 
     } 

Я могу сделать простой материал LINQ, но я не могу делать то, что хотел. Я бы хотел использовать LINQ. Может быть что-то вроде

from x in this.Map where x == true execute DrawTerrain(...)

Но я не понимаю, как я могу получить й и у места или как вызвать метод в операторе LINQ.

Также было бы здорово, если бы я мог поместить этот код в функцию и иметь возможность называть его делегатом или предикатом? Я не знаю, правильные ли слова делегат или предикат.

void Draw(Delegate draw, bool[,] map, struct[,] tiles) 
     from x in map where x == true draw(titles[x,y]).invoke; 
    } 

ответ

1

Синтаксис запросов LINQ не был предназначен для работы с 2-мерными структурами данных, но вы можете написать метод расширения, который будет преобразовывать 2D-массив в последовательность значений, которые содержат координаты в оригинале 2D-массив и значение из массива. Вам нужен тип помощника для хранения данных:

class CoordinateValue<T> { 
    public T Value { get; set; } 
    public int X { get; set; } 
    public int Y { get; set; } 
} 

Тогда вы можете написать метод расширения (для любого 2D массива), как это:

IEnumerable<CoordinateValue<T>> AsEnumerable(this T[,] arr) { 
    for (int i = 0; i < arr.GetLength(0); i++) 
    for (int j = 0; j < arr.GetLength(0); j++) 
     yield new CoordinateValue<T> { Value = arr[i, j]; X = i; Y = j; }; 
} 

Теперь вы можете, наконец, начать использовать LINQ. Чтобы получить координаты всех элементов из 2D массива таким образом, что значение, которое хранится в элементе true, вы можете использовать следующее:

var toDraw = from tile in this.Map.AsEnumerable() 
      where tile.Value == true 
      select tile; 

// tile contains the coordinates (X, Y) and the original value (Value) 
foreach(var tile in toDraw) 
    FillTile(tile.X, tile.Y); 

Это позволяет определить некоторые интересные условия при фильтрации плитки. Например, если вы хотите получить только диагональные элементы, вы могли бы написать:

var toDraw = from tile in this.Map.AsEnumerable() 
      where tile.Value == true && tile.X == tile.Y 
      select tile; 

Чтобы ответить на ваш второй вопрос - если вы хотите, чтобы инкапсулировать поведение в метод, вам, вероятно, придется использовать некоторые Action<...> делегата представляют функцию рисования, которая будет передана методу. Однако это зависит от сигнатуры типа вашего метода рисования.

+0

Мой отец говорит, что мне нужно попробовать свой путь, но я получаю ошибку компиляции: «получить новый CoordinateValue {Value = arr [i, j]; X = i; Y = j;}; которые говорят: «В качестве оператора можно использовать только назначение, вызов, приращение, декремент и новые выражения объекта». Кроме того, где я помещаю AsEnumerable, в CoordinateValue? –

+0

Я ПОЛУЧИЛ ЭТО! Огромное спасибо. Должно быть, вместо; в отчете о доходности. Кроме того, AsEnumerable должен быть AsEnumerable . –

+0

OMG. Я смог использовать Action >, и он работает. Я не знаю, что я сделал, но у меня все получилось. –

1

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

Имея версию того, что обобщенно принять меры, кажется очень разумным, хотя:

public delegate void Drawer(int x, int y, Tile tile); 

public void Draw(Drawer drawer, bool[,] map, Tile[,] tiles) { 
    for (int x = 0; x < this.Size.Width; x++) { 
     for (int y = 0; y < this.Size.Height; y++) { 
      if (this.Map[x, y]) { 
       drawer(x, y, tiles[x, y]); 
      } 
     } 
    } 
} 

Если вы действительно хотите версию LINQ, это будет что-то вроде:

var query = from x in Enumerable.Range(0, Size.Width) 
      from y in Enumerable.Range(0, Size.Height) 
      where Map[x, y] 
      select new { x, y }; 

foreach (var pair in query) 
{ 
    DoWhatever(pair.x, pair.y, tiles[pair.x, pair.y]); 
} 
1

Вы можете выполнить следующий запрос Linq, чтобы получить значения x, y, затем перебрать их, чтобы запустить ваш метод.

var points = from x in Enumerable.Range(0, this.Size.Width - 1) 
      from y in Enumerable.Range(0, this.Size.Width - 1) 
      where this.Map[x, y] 
      select new { X = x, Y = y }; 
foreach (var p in points) 
    DrawTerrain(this.Tile[p.X, p.Y].Location, Terrain.Water); 
Смежные вопросы