配列言語での思考:命令的なループから暗黙的Kへ
配列言語であるKでのプログラミングは、単に新しい構文を学ぶこと以上のものを必要とします。それは、問題解決を概念化する方法における根本的な転換を必要とします。ほとんどの開発者は、命令的な思考(一連の手順を定義し、ループや変数を通じて状態を管理すること)を訓練されていますが、配列言語は、データ全体のコレクションに対して一度に操作を適用する宣言的なアプローチを推奨します。
この移行は、しばしば継続的な簡素化のプロセスとして説明されます。目標は、大きく扱いにくいパターンを取り出し、より小さく、読みやすく、より宣言的な形式に凝縮することです。この進化を説明するために、古典的なアルゴリズムである行列の乗算を例に挙げて検討してみましょう。
命令的な翻訳の罠
開発者が初めてKに触れるとき、Wikipediaや教科書のようなソースから既知のアルゴリズムを直接翻訳しようとする本能がしばしば働きます。行列の乗算の場合、反復アルゴリズムは単純です。
- 結果の行列 $C$ を初期化する。
- $A$ の行をループする ($i$)。
- $B$ の列をループする ($j$)。
- 3番目のループ ($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}
これは「ワーストケース」のKコードと見なされます。これには3つの主要な問題があります。過剰なグローバル代入、深くネストされたループ、そして状態の絶え間ない変更です。配列言語において、これらはアンチパターンです。
簡素化への道
このコードを洗練させるには、命令的な足場を体系的に取り除いていく必要があります。
ステップ 1: ループをFoldで置き換える
sum 変数を手動で管理しループを使用する代わりに、Kはfold演算子 (/) を提供します。最も内側のループは、1行に凝縮できます。
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の力は簡潔さにありますが、議論の余地もあります。コミュニティはしばしば配列言語を正規表現(regular expressions)と比較します。対話的な探索や迅速なプログラミングトタイプ作成には信じられないほど強力ですが、過度に圧縮されると「書き込み専用(write-only)」になる可能性があります。
Hacker Newsのある観察者は次のように述べています。
I still haven't used K/Q/etc. because they look insane... they are basically the maths equivalent of regexes. Super terse and powerful. Pretty much write-only. Very useful for interactive use, definitely. But if you find yourself hitting "save" on a regex that's a red flag...
このように、命令的な思考から宣言的な思考へと移行することで、開発者は複雑な数学的演算をわずか数文字で表現でき、「どのように」計算するかではなく、「何を」計算するかという点に焦点を移すことができます。
(注:原文の英語は原文のままです)