프로그래밍을 하다보면 2차원에 관련된 데이터를 처리하는 경우가 많다.
보통 rows,columns 위치에 특정 데이터가 들어있는 구조인데
C#에서는 int[,] 처럼 2차원 배열을 사용하거나 int[][] (List<List<int>>) 가변배열(가변 리스트)을 사용한다.
하지만 2차원 배열을 사용해보면 초기화를 하려면 이중 루프를 쓰거나 모든 요소를 적어주고
특정 조건의 값만 변경하려면 검사 로직과 변경 로직을 나눠서 써야한다던지 하는 불편함이 있다.
예를 들어 x는 짝수이며 y는 2인 모든값을 특정값으로 변경하는 로직이 있다면 다음과 같이 작성해야한다.
public void Initialize() {
int[,] data=new int[4,4];
for (int i=0;i<data.GetLength(0);i++) {
for (int j=0;j<data.GetLength(1);j++) {
if (i%2==0 && j==2) {
data[i,j]=20;
}
}
}
}
그래서 Matrix클래스를 작성해보도록 하였다. (여기서 Matrix는 수학에서의 Matrix와는 다르다)
내부 구조는 1차원 List를 생성하여 X,Y값을 고정시켜 Index접근을 Pair한 값(X,Y)만 접근 할 수 있게 했다.
(x,y,item)<Tuple> 이터레이터를 반환하는 함수를 만들어 Linq를 사용하기 용이하게 만들었고
다양한 생성자를 주어 기본적인 배열이나 List와도 호환이 가능하게 하고 검색과 변경을 편하게 만들었다.
//일부 내용이 빠져있다.
//실제 코드는 https://gitlab.com/tkdgns1284/bss_framework/blob/master/Runtime/Csharp/DataModel/Matrix.cs
public class Matrix<T> : IEnumerable<(int x,int y,T item)> {
public Matrix(int x,int y) {
Size = (x, y);
for (int i=0;i<x*y;i++) {
data.Add(default);
}
}
public T this[int x,int y] {
get => data[x*Size.y+y];
set => data[x * Size.y + y] = value;
}
public T this[(int x, int y) pair] {
get => data[pair.x * Size.y + pair.y];
set => data[pair.x * Size.y + pair.y] = value;
}
public readonly (int x, int y) Size;
private List<T> data = new List<T>();//내부구조는 1차원배열
/// <summary>
/// (x,y,item) 튜플의 이터레이터를 반환합니다.
/// </summary>
public IEnumerable<(int x,int y,T item)> GetEnumerable() {
for (int i=0;i<Size.x;i++) {
for (int j=0;j<Size.y;j++) {
yield return (i,j,this[i, j]);
}
}
}
/// <summary>
/// Predicate에 해당하는 요소를 item 값으로 변경합니다.
/// </summary>
public void Set(T item, Func<(int x, int y, T item), bool> predicate) {
foreach(var it in GetEnumerable()) {
if(!predicate(it))
continue;
this[it.x, it.y] = item;
}
}
}
Matrix클래스를 이용해서 같은 행동을 하는 함수를 정의해보았다.
public void Initialize() {
Matrix<int> data=new Matrix<int>(4,4);
//For문과 If문 같은 요소가 사라지고 간단해졌다.
data.Set(20,t=>{ return t.x % 2 == 0 && t.y == 2; });
}