用数组语言思考 (2022)

2026-05-22 1 阅读 tosh
用数组语言思考 您可以在 GitHub 上查看本章的完整源代码。既然您现在已经熟悉了 K,那么让我们开始一些编程吧。大多数 K 编程都是通过 REPL 进行的,因为迭代以前的代码非常有用。 ngn/k 和 rlfe 具有使用向上/向下箭头键的历史,这应该足以开始在 K 中开发更大的程序。函数在 REPL 中进行测试,然后转移到实际代码中。请注意,ngn/k 的 beautifulprinting 始终返回有效的 k 数据,您可以预先计算一些内容以加快程序速度。 K 脚本总是像在 repl 中键入一样执行,即:执行每一行,并打印其返回值,除非它以分号结尾。脚本还允许多行定义,这方便了可读性。通常,您可能会将工作保存在脚本中,并希望在 repl 中使用它。为了使用您存储的数据和函数,只需在 repl 中执行 \l file.k ,您的文件将被执行,并且其数据将被加载。您可以多次将文件加载到 REPL 中,从而覆盖旧数据。使用 \ 访问的 repl 帮助还列出了更多有用的命令。 K 编程(以及一般的数组编程)是一个简化模式的连续过程。一个大的、笨拙的模式有一种或多种方法可以压缩成一个更小、更具声明性、易于阅读的模式。如果您想更好地理解它,请在 APL 中的模式和反模式:逃离初学者的高原 - Aaron Hsu - Dyalog '17 中详细讨论这一点。大多数人在 K 中遇到的一个常见问题是需要将常见的、众所周知的算法转换为 K,通常取自 geeksforgeeks 等编程网站或维基百科文章。让我们举一个例子:矩阵乘法。从这篇维基百科文章中,矩阵乘法的迭代算法如下: 输入:矩阵 A 和 B 设 C 为适当大小的新矩阵 对于 i 从 1 到 n: 对于 j 从 1 到 p:让 sum = 0 对于 k 从 1 到 m: Set sum ← sum + Aik × Bkj Set Cij ← sum Return C 如果需要,可以尝试将其转换为 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 代码我曾经写过,因为我们试图像命令式语言一样编写 K,而 K 不太适合这种设计。主要问题是: 很多很多的全局变量被分配了多个嵌套循环大量的修改幸运的是,这里我们可以简化很多事情,我们可以一一解决这些问题。让我们从最里面的循环开始: sum::0 { k::x sum::sum+A[i;k]*B[k;j] }'!m C[i;j]::sum 我们可以做的第一个也是最简单的修复是使用折叠 ( / ) 求和。 C[i;j]::+/{ k::x A[i;k]*B[k;j] }'!m 1 个全局故障,还有 9 个故障。我们可以删除的下一个全局是 C 。由于 ' (each) 返回一个数组,因此不需要修改 C。我们可以简单地返回嵌套循环的值。 { i::x { j::x +/{ k::x A[i;k]*B[k;j] }'!m }'!p }'!n} 现在,我们有了三个没有修改的循环,这使我们的工作变得更加容易。现在要查看的主要变量是 i 、 j 和 k 。 i 索引 A 的每一行。 j 索引 B 的每一列。 k 索引 A 的每一列和 B 的行。基本上,k 负责将 A 的每一行与 B 的每一列配对,然后将它们相乘。因此,我们可以消除这里的中间人,直接匹配它们而无需 k 。这也消除了一个循环,并且不再需要 m 。 { j::x +/A[i]*B[;j] }'!p }'!n} 接下来,要删除 j ,我们需要获取 B 的每一列并将其与 A[i] 配对。为此,我们转置 B 并将每个元素与每个右 ( /: ) 配对。 matmul: { A::x B::y n::#A i::0 { i::x A[i]{+/x*y}/:+B}'!n } 为了删除 i ,我们做了类似的事情:使用 everyleft 将 A 的每一行与 B 的每一列配对。 matmul: { A::x B::y A{+/x*y}/:\:+B } 我们不需要更多的全局变量! matmul: {x{+/x*y}/:\:+y} 现在就是K中的矩阵乘法。这是矩阵乘法最直接的算法转换。现在我们将研究缩短它并删除更多循环的方法。 +(转置)的成本很高,我们可以将其删除。我们目前所做的事情很幼稚。我们可以将 B 的每一行与 A 的整体一致,而不是将 x 的每一行与 y 的每一列相乘,隐式地执行相同的操作。 matmul: {x{+/x*y}\:y} 现在,我们有了一个可以轻松默认的函数。根据第 3 章的规则,我们得到最终结果: matmul: (+/*)\: 一个值得您自豪的矩阵乘法函数。这个过程可能看起来有很多步骤,但是当您练习 K 技能时,压缩代码将变得更加容易和直观。矩阵乘法是一个简单的过程,与 K 的数组支持配合得很好。我们将在以后的章节中看到更多与 K 不能很好配合的算法,以及如何处理它们。