배열 언어로 사고하기: 명령형 루프에서 암묵적 K까지
배열 언어인 K로 프로그래밍하는 것은 단순히 새로운 문법을 배우는 것 이상의 것을 요구합니다. 문제 해결을 개념화하는 방식에 대한 근본적인 변화가 필요합니다.
대부분의 개발자는 명령형 사고—일련의 단계를 정의하고 루프와 변수를 통해 상태를 관리하는 방식—에 익숙해져 있지만, 배열 언어는 데이터 전체 컬렉션에 연산을 한 번에 적용하는 선언적 접근 방식을 권장합니다.
이러한 전환은 종종 지속적인 단순화 과정으로 설명됩니다. 목표는 크고 다루기 힘든 패턴을 더 작고, 읽기 쉽고, 더 선언적인 형태로 응축하는 것입니다. 이 진화를 보여주기 위해, 고전적인 알고리즘인 행렬 곱셈의 변환 과정을 살펴볼 수 있습니다.
명령형 번역의 함정
개발자들이 K를 처음 접할 때, 위키피디아나 교과서와 같은 출처에서 알고 있는 알고리즘을 직접 번역하려는 본능이 있습니다. 행렬 곱셈의 경우, 반복 알고리즘은 다음과 같이 간단합니다:
- 결과 행렬 $C$를 초기화합니다.
- $A$의 행을 루프합니다 ($i$).
- $B$의 열을 루프합니다 ($j$).
- 세 번째 루프 ($k$)를 사용하여 $A$의 $i$번째 행과 $B$의 $j$번째 열의 내적을 계산합니다.
- 결과를 $C_{ij}$에 저장합니다.
K로 직접 번역하면 다음과 같습니다:
matmul: {
A::x
B::y
n::#A
m::#*A
p::#*B
C::(n;p)#0
i::0
j::0
k::0
sum::0
{
i::x
{
j::x
sum::0
{
k::x
sum::sum+A[i;k]*B[k;j]
}'!m
C[i;j]::sum
}'!p
}'!n
C
}
이것은 "worst-case" K 코드라고 간주됩니다. 과도한 전역 변수 할당, 깊게 중첩된 루프, 그리고 지속적인 상태 수정이라는 세 가지 주요 문제를 안고 있습니다. 배열 언어에서 이러한 방식은 안티 패턴입니다.
단순화로 가는 길
이 코드를 개선하는 것은 명령형 구조를 체계적으로 제거하는 과정을 포함합니다.
1단계: 루프를 Fold로 교체하기
sum 변수와 루프를 수동으로 관리하는 대신, K는 fold 연산자 (/)를 제공합니다. 가장 안쪽의 루프는 다음과 같이 한 줄로 응축될 수 있습니다:
C[i;j]::+/{ k::x; A[i;k]*B[k;j] }'!m
2단계: 상태와 중간 변수 제거하기
each 연산자 (')는 배열을 반환하므로, 행렬 $C$를 미리 할당하고 수정할 필요가 없습니다. 중첩된 루프는 단순히 값을 반환할 수 있습니다:
{ i::x; { j::x; +/{ k::x; A[i;k]*B[k;j] }'!m }'!p }'!n
3단계: 배열 페어링 활용하기
인덱스 변수 $i$, $j$, $k$는 본질적으로 중간 매개체입니다. 우리가 $A$의 행과 $B$의 열을 짝짓고 있다는 점을 인식함으로써, $k$ 루프를 완전히 제거할 수 있습니다:
{ j::x; +/A[i]*B[;j] }'!p }'!n
4단계: 전치 및 고차 함수 활용하기
남은 인덱스를 제거하기 위해, 행렬 $B$를 전치하고 eachleft (/:)와 eachright를 사용하여 데이터를 직접 짝짓습니다:
matmul: { A::x; B::y; A{+/x*y}/:\:B }
최종 형태: 암묵적 프로그래밍
전역 변수가 사라지면 코드는 더욱 응축될 수 있습니다. 비용이 큰 전치 연산을 제거하고 암묵적 형태 일치(K가 서로 다른 모양의 배열을 자동으로 정렬하는 방식)를 사용하면 함수는 다음과 같이 됩니다:
matmul: {x{+/x*y}\:y}
마지막으로, "trains"(명시적 인자 없이 함수의 합성)의 규칙을 적용하면, 궁극적인 암묵적 버전이 완성됩니다:
matmul: (+/*)\:
배열 언어에 대한 관점
K의 강력함은 간결함에 드러나지만, 논란의 여지가 없는 것은 아닙니다. 커뮤니티에서는 종종 배열 언어를 정규 표현식에 비유합니다. 대화형적 탐색과 빠른 프로토타이핑에는 믿을 수 없을 정도로 강력하지만, 과도하게 압축되면 잠재적으로 "write-only"가 될 수 있습니다.
Hacker News의 한 관찰자가 언급했듯이:
K/Q/etc.는 아직 사용해 보지 못했습니다. 너무 미친 것 같거든요... 기본적으로 정규 표현식의 수학적 버전입니다. 매우 간결하고 강력하지만, 거의 write-only입니다. 대화형 사용에는 확실히 매우 유용합니다. 하지만 정규 표현식을 작성할 때 "save"를 누르는 것이 두려워진다면 그것은 위험 신호입니다...
그럼에도 불구하고, "K로 사고하기"는 패턴 단순화의 여정입니다. 명령형에서 선언적 방식으로 이동함으로써, 개발자는 복잡한 수학적 연산을 단 몇 글자로 표현할 수 있으며, 계산 방식(how)이 아닌 무엇을 계산하는지(what)에 초점을 맞출 수 있습니다.