*
*
* *
* *
* *
** **
* *
* *
* *
*
*
어제 1등은 강성훈님이 gzip으로 출력문자를 압축해서 쉘 스크립트 뒤에 덧붙이는 식으로 하여 73바이트까지 줄인 것이라고 합니다.
저는 집에 와서 J로 잠깐 풀어봤습니다. 30바이트입니다.
' *'{~(,{:)@|:@|.^:4]4=+/~|i:4
일단 이 코드를 이해하려면 J의 이디엄들을 이해해야 합니다. 몇 가지 덩어리가 눈에 띄는데요.
' *'{~(,{:)@|:@|.^:4]4=+/~|i:4
이 부분은 0과 1을 각기 빈칸과 *로 바꾸는 코드입니다. 뒤에 달린 ~는 능동형을 수동형으로 바꿔주는 것인데, 예를 들어 3-1은 2인데, 3-~1은 -2가 됩니다. 즉, 좌측과 우측 피연산자(operand)의 순서를 바꿔주는 부사(동사를 꾸며주는)입니다. 원래 형태는 0 1 3 2 1 {'ABCDEFG' 하면 오른쪽에서 0번째, 1번째, 3번째, 2번째, 1번째로 이루어진 스트링(ABDCB)을 반환하는 것이죠. 따라서 오른쪽에서 0과 1로 이루어진 행렬이 나오는데 그걸 빈칸과 *로 바꿔주는 역할을 합니다.
' *'{~(,{:)@|:@|.^:4]4=+/~|i:4
그 다음 부분은 J에서 포크라고 부르는 것인데, g와 h가 동사일 때 (g h) y는 y g (h y)와 같습니다. h한 걸 g한다라고 이해하면 쉽습니다. ,는 스트링이나 숫자를 추가(append)하는 것이고, {:는 마지막 원소를 가져오는(take last) 것입니다. 따라서 마지막 원소를 뒤에 추가한다고 보시면 됩니다.
' *'{~(,{:)@|:@|.^:4]4=+/~|i:4
이 부분은 행렬을 시계방향으로 90도 회전시킵니다. |:는 행렬의 행과 열을 바꿔주고(Axy를 Ayx로), |.는 행렬의 원소 순서(예컨대 행의 순서)를 거꾸로 뒤집습니다. 순서가 |.한 다음 |:이므로 결과적으로는 시계방향으로 90도 회전이 됩니다.
예컨대(아래 예에서 처음 세 칸 띄어쓴 것은 사용자가 입력하고 붙어나오는 건 컴퓨터가 보여주는 결과이며, NB에서 해당줄 끝까지는 주석)
i.3 3
0 1 2
3 4 5
6 7 8
|. i.3 3
6 7 8
3 4 5
0 1 2
|: i.3 3
0 3 6
1 4 7
2 5 8
|:@|.i.3 3
6 3 0
7 4 1
8 5 2
|:@|. |:@|.i.3 3 NB. 두 번 90도 회전하면 180도 회전과 같음
8 7 6
5 4 3
2 1 0
' *'{~(,{:)@|:@|.^:4]4=+/~|i:4
이 부분은 앞의 동사를 4번 연속 적용하게 됩니다. 우리가 수학에서 아는 지수승(거듭 곱하기)의 외연을 확장한 겁니다. 예를 들어
+: 3 NB. +:는 인자를 두 배 하는(double) 동사
6
+: +: 3 NB. 두 배 한 것에 다시 두 배 하면 네 배
12
+: +: +: 3
24
+:^:(3) 3
24
+:^:(4) 3
48
+:^:(0 1 2 3 4) 3
3 6 12 24 48
앞 동사가 90도 회전한 다음에 맨 아래 원소(2차원 행렬에서 원소는 1차원 배열)를 복사해 붙이기였으므로, 이걸 4번 연속하면 원래 행렬의 네 테두리줄을 돌아가며 복사하는 셈이 됩니다. (마름모 꼭지점에서 하나씩 튀어나온 부분 처리)
' *'{~(,{:)@|:@|.^:4]4=+/~|i:4
이 부분은 마름모를 만드는 코드입니다. 아래를 보시면 이해가 되실 겁니다.
i:3 NB. i: y는 -y에서 y까지의 정수 배열을 만듭니다(steps)
_3 _2 _1 0 1 2 3
|i:3 NB. 절대값
3 2 1 0 1 2 3
+/~|i:3 NB. 3 2 1 0 1 2 3을 서로 더하되(+) 한 원소와 전체 배열을(/) 더해서 테이블을 만듦
6 5 4 3 4 5 6
5 4 3 2 3 4 5
4 3 2 1 2 3 4
3 2 1 0 1 2 3
4 3 2 1 2 3 4
5 4 3 2 3 4 5
6 5 4 3 4 5 6
3=+/~|i:3 NB. 3과 같으면 1 아니면 0
0 0 0 1 0 0 0
0 0 1 0 1 0 0
0 1 0 0 0 1 0
1 0 0 0 0 0 1
0 1 0 0 0 1 0
0 0 1 0 1 0 0
0 0 0 1 0 0 0
전체 코드의 실행순서는 위에서 설명한 것의 역순으로 됩니다. 따라서 마름모를 만들고, 그걸 90도씩 돌려가면서 꼭지점에 하나씩 점을 찍고(사실은 가장 바깥 라인을 복사), 그 행렬을 문자로 변환해주면 결과가 나옵니다.