오늘은 Ruby의 transpose 메서드를 활용해 다차원 배열(그리드)을 손쉽게 다루는 방법을 알아보겠습니다.
3×3 정사각형 형태의 다차원 배열이 있다고 가정해 봅시다. 그리고 이 배열의 행(row)을 열(column)로 변환하고 싶다고 해보죠.
왜 이런 작업이 필요할까요?
대표적인 예가 바로 틱택토(Tic-Tac-Toe) 게임입니다. 게임 보드를 그리드 형태로 저장한 뒤, 승리 조건을 확인하려면 행, 열, 대각선을 모두 검사해야 하기 때문입니다.
그런데 여기에 문제가 하나 있습니다. 그리드를 배열로 저장하면 행에는 직접 접근할 수 있지만, 열에는 그렇지 않다는 점입니다.
인덱스로 접근하는 번거로운 방법
여기서 말하는 '직접 접근'이란 each, map 같은 메서드로 배열을 순회할 때 불필요한 인덱싱 없이 간편하게 데이터를 다룰 수 있다는 의미입니다.
예제를 살펴보겠습니다. 먼저 그리드입니다:
grid = [ [1,2,3], [4,5,6], [7,8,9] ]
첫 번째 열을 가져오려면 인덱스를 일일이 참조해야 합니다.
예를 들어 첫 번째 열은 다음과 같습니다:
[grid[0][0], grid[1][0], grid[2][0]] # [1, 4, 7]
반면 첫 번째 행은 아주 간단합니다:
grid[0] # [1, 2, 3]
열도 행만큼 쉽게 다룰 수 있는 방법은 없을까요?
Transpose 메서드로 해결하는 쉬운 방법
해답은 바로 Array#transpose 메서드입니다. 이 메서드는 행렬의 행과 열을 서로 뒤바꿔 새로운 배열을 반환해 줍니다.
예제:
columns = grid.transpose
네, 딱 한 줄이면 끝납니다!
이제 첫 번째 열을 이렇게 간단히 가져올 수 있습니다:
columns[0] # [1, 4, 7]
다양한 내장 메서드를 알고 있으면 이처럼 많은 수고를 덜 수 있습니다. 🙂
틱택토 게임에 적용하기
전체 구현을 자세히 설명하지는 않고, 이 메서드가 실제 프로젝트에서 어떻게 활용되는지만 보여드리겠습니다.
틱택토에서 승리하려면 한 행, 한 열, 또는 대각선 중 하나를 자신의 말로 모두 채워야 합니다.
먼저 행을 검사하는 코드입니다:
def check_rows
@board.each { |row| return row.first if all_equal?(row) }
end
그리고 열을 검사하는 코드입니다:
def check_columns
@board.transpose.each { |row| return row.first if all_equal?(row) }
end
두 코드의 유일한 차이가 바로 transpose 메서드 하나라는 점에 주목하세요!
참고로 위 코드에서 사용된 all_equal? 메서드는 다음과 같이 구현할 수 있습니다:
def all_equal?(row)
return if row.first == nil
row.each_cons(2).all? { |x,y| x == y }
end
each_cons 메서드에 대해 더 궁금하다면 Enumerable 관련 포스트를 참고해 보세요.
마무리
이번 글에서는 Ruby의 transpose 메서드에 대해 배웠습니다. 완전한 그리드 형태의 배열이 주어졌을 때, transpose를 사용하면 행을 열로 변환해 훨씬 편리하게 접근할 수 있습니다.
이 글이 도움이 되었다면, 더 깊이 있는 Ruby 학습을 위해 제 저서 『Ruby Deep Dive』도 확인해 보시길 추천합니다.