[英]How do I implement accumulation in APL?
考虑以下简单的代码:
a ← ⍳5
el1 ← a+2
el2 ← +/el1
el1 el2
┌─────────┬──┐
│3 4 5 6 7│25│
└─────────┴──┘
我只是将a
添加2并将数组相加,然后返回包含两个操作结果的数组。 据我了解,在这种情况下,APL解释器必须在阵列上进行2次 。
这是在APL中执行此操作的正确方法,还是语言具有某种累加器,类似于函数式编程语言提供的内容(这样我只能在阵列上运行一次)?
我会说这是做到这一点的方法。 但是在考虑性能时,优化一下可能会很有趣:
{(2×⍴⍵)++/⍵}⍳5
25
因为在这种情况下你只会经历一次数组并且之后会添加两个标量。 但'优势'取决于数组的长度:
]runtime {(2×⍴⍵)++/⍵}⍳5 {+/2+⍳⍵}5 -compare
{(2×⍴⍵)++/⍵}⍳5 → 2.0E¯6 | 0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* {+/2+⍳⍵}5 → 1.4E¯6 | -29% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
对于短数组,添加到每个元素的速度更快。 但元素越多,你获得的就越多:
]runtime {(2×⍴⍵)++/⍵}⍳5 {+/2+⍳⍵}500 -compare
{(2×⍴⍵)++/⍵}⍳5 → 1.5E¯6 | 0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* {+/2+⍳⍵}500 → 2.4E¯6 | +59% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
]runtime {(2×⍴⍵)++/⍵}⍳5 {+/2+⍳⍵}5000 -compare
{(2×⍴⍵)++/⍵}⍳5 → 1.6E¯6 | 0% ⎕⎕⎕⎕
* {+/2+⍳⍵}5000 → 1.5E¯5 | +822% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
因此,APL中没有任何特定的累积功能。 我想大多数APLers将+ / A和X + 2视为原始操作,其中解释器必须在数组上重复迭代生活中的事实。
但是,假设您的APL系统支持用户定义的运算符,您可以轻松编写一种累积运算符。 也就是说, 如果目标是对数组进行单次传递 。 就像是
(⍳5) (+ accum +) 2
代码在哪里
∇ r←a(fn1 accum fn2)b;x
[1] r←0
[2] :For x :In a
[3] r←r fn1 x fn2 b
[4] :End
∇
这是一个非常简单的解决方案,可以正确使用+和x以及fn1标识为1的其他函数。 性能不如示例那样好。 还要注意解决方案开始类似于还原。
恢复正常APL,如果你不需要中间结果, +/ a + 2
就可以了。 实际上, +/ ax 2
或+/ a * 2
代码片段非常常见。 同样对于矢量a,内积也可以工作, a +.x 2
和a +.* 2
也不常见。
最后也是最不重要的,未来优化APL解释器或编译器可以采用循环融合在内部执行此操作,只需一个循环而不是两个循环。
如果我理解你的问题,那么你首先将+2
映射到a
,然后通过添加来减少它。 例如在haskell中:(注意reduce是foldl
)
foldl (+) 0 $ map (2+) [1..5]
显然,如果没有优化,例如循环融合,则需要两次通过。
我怀疑这是一个提出错误问题的案例,你实际上正在寻找的是scan
(在某些语言中accumulate
)。
哈斯克尔:
scanl (+) 0 [1..5]
-- [0,1,3,6,10,15]
APL:
+\⍳5
⍝ 1 3 6 10 15
在这种情况下,输出序列将包含中间值和结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.