繁体   English   中英

如何在APL中实现累积?

[英]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 2a +.* 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM