如何在APL中实现累积?

时间:2015-06-17 06:07:41

标签: arrays apl dyalog

考虑以下简单的代码:

  a ← ⍳5
  el1 ← a+2
  el2 ← +/el1 
  el1 el2

  ┌─────────┬──┐
  │3 4 5 6 7│25│
  └─────────┴──┘

我只是将{2}添加到a并将数组相加,然后返回包含两个操作结果的数组。据我了解,在这种情况下,APL解释器必须在阵列上进行2次

这是在APL中执行此操作的正确方法,还是语言具有某种累加器,类似于函数式编程语言提供的(这样我只能在阵列上运行一次)?

3 个答案:

答案 0 :(得分:1)

我会说这是做到这一点的方法。但是在考虑性能时,优化一下可能会很有趣:

{(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% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕ 

答案 1 :(得分:1)

因此,APL中没有任何特定的累积功能。我想大多数APLers将+ / A和X + 2视为原始操作,其中解释器必须在数组上重复迭代一个事实。

但是,假设您的APL系统支持用户定义的运算符,您可以轻松编写一种累积运算符。也就是说,如果目标是对数组进行单次传递。像

这样的东西
ejs

代码类似于

(⍳5) (+ accum +) 2

这是一个非常简单的解决方案,可以正确使用+和x以及 fn1的标识为1 的其他函数。性能不如示例那样好。另请注意,解决方案开始类似于还原。

回到普通APL,如果你不需要中间结果, ∇ r←a(fn1 accum fn2)b;x [1] r←0 [2] :For x :In a [3] r←r fn1 x fn2 b [4] :End ∇ 就可以了。实际上,+/ a + 2+/ a x 2的代码片段非常常见。同样对于矢量a,内部产品也可以工作,+/ a * 2a +.x 2也不常见。

最后也是最不重要的,未来的优化APL解释器或编译器可以使用 loop fusion 在内部执行此操作,只需一个循环而不是两个循环。

答案 2 :(得分:1)

如果我理解您的问题,那么您首先要将+2映射到a,然后通过加法来减少它。 例如在haskell中:(请注意reduce是foldl

foldl (+) 0 $ map (2+) [1..5] 

很明显,如果不做优化就需要两次通过,例如循环融合。

但是

我怀疑这是一个错误的问题,您真正要寻找的是scan(在某些语言中是accumulate)。

Haskell:

scanl (+) 0 [1..5]
-- [0,1,3,6,10,15]

APL:

+\⍳5
⍝  1 3 6 10 15

在这种情况下,输出序列将包含中间值和结果。