2010-04-10 12 views
5

Pracuję z wielowymiadowymi tablicami o nazwach bool, int i różnych struct. Kod przechodzi przez te tablice i wykonuje pewne operacje na określonych wartościach. Na przykład:Używanie LINQ do wyodrębniania określonych wartości z tablicy wielowymiarowej

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

Potrafię zrobić proste rzeczy LINQ, ale nie mogę zrobić tego, co bym chciał. Chciałbym użyć LINQ. Może coś

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

Ale ja nie rozumiem, jak można uzyskać xiy lokalizacje lub jak wywołać metodę w rachunku LINQ.

Byłoby wspaniale, gdybym umieścił ten kod w funkcji i mógłbym wywołać go z delegatem lub predykatem? Nie wiem, czy delegat lub predykat są poprawnymi słowami.

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

Odpowiedz

1

Składnia zapytań LINQ naprawdę nie jest przeznaczony do pracy z 2-wymiarowych struktur danych, ale można napisać metodę rozszerzenia, które będzie przekształcić tablicę 2D w sekwencji wartości, które zawierają współrzędne w oryginale Tablica 2D i wartość z tablicy. Musisz typ pomocnika do przechowywania danych:

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

Następnie można napisać metodę rozszerzenia (dla każdej tablicy 2D) tak:

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

Teraz można wreszcie zacząć używać LINQ. Aby uzyskać współrzędne wszystkich pierwiastków z tablicy 2D taka, że ​​wartość zapisana w elemencie jest true, można użyć następujących:

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

To pozwala określić kilka interesujących warunków podczas filtrowania płytek. Na przykład, jeśli chcesz dostać tylko elementy diagonalne, można napisać:

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

Aby odpowiedzieć na drugie pytanie - czy chcesz hermetyzacji zachowanie do metody, prawdopodobnie będziesz musiał użyć jakiegoś delegata do Action<...> reprezentuje funkcję rysowania, która zostanie przekazana do metody. Zależy to jednak od sygnatury typu twojej metody rysowania.

+0

Mój ojciec mówi, że muszę spróbować, ale dostaję błąd kompilacji na "yield new CoordinateValue {Wartość = arr [i, j]; X = i; Y = j;};" które mówią: "Tylko przypisania, wywoływanie, inkrementowanie, dekrementacja i nowe wyrażenia obiektowe mogą być używane jako wyrażenie". Ponadto, gdzie mogę umieścić AsEnumerable, w CoordinateValue? –

+0

I GOT IT! Dziękuję bardzo. Zamiast tego powinno być; w rachunku zysków i strat. Ponadto AsEnumerable powinno być równe . –

+0

OMG. Byłem w stanie użyć działania > i to działa. Nie wiem, co zrobiłem, ale udało mi się. –

1

Cóż, mógłby zrobić z LINQ jeśli pracujesz wystarczająco ciężko, ale to byłby ból. Wygląda na to, że twój pierwszy kawałek kodu jest absolutnie w porządku.

Posiadanie wersję tego, co jest uogólniony do podjęcia działań wydaje się bardzo rozsądne, choć:

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

Jeśli naprawdę chcą wersję LINQ, to byłoby coś takiego:

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

Możesz wykonać następujące zapytanie Linq, aby uzyskać wartości x, y, a następnie iterować je, aby uruchomić twoją metodę.

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); 
Powiązane problemy