APL in R PDF
APL in R PDF
JAN DE LEEUW
C ONTENTS
1.
Introduction
2.
Functions
2.1.
Compress
2.2.
Decode
2.3.
Drop
2.4.
Encode
2.5.
Expand
2.6.
Inner Product
2.7.
Join
2.8.
Member Of
2.9.
Outer Product
2.10.
Ravel
2.11.
Rank
2.12.
Reduce
2.13.
Replicate
JAN DE LEEUW
2.14.
Reshape
2.15.
Rotate
10
2.16.
Scan
10
2.17.
Select
11
2.18.
Shape
12
2.19.
Take
12
2.20.
Transpose
12
3.
Utilities
References
13
13
APL IN R
1. I NTRODUCTION
APL was introduced by Iverson [1962]. It is an array language, with many functions to manipulate multidimensional arrays. R also has multidimensional arrays,
but far fewer functions to work with them.
In R there are no scalars, there are vectors of length one. For a vector x in R we
have dim(x) equal to NULL and length(x)>0. For an array, including a matrix,
we have length(dim(x))>0. APL is an array language, which means everything
is an array. For each array both the shape A and the rank A are defined. Scalars
are arrays with shape equal to one, vectors are arrays with rank equal to one.
In 1994 I coded most APL array operations in XLISP-STAT. The code is still
available at http://idisk.mac.com/jdeleeuw-Public/utilities/apl/array.lsp. There are
some important differences between the R and Lisp versions, because Lisp and
APL both have Cs row-major ordering, while R (like Matlab) has Fortrans
column-major ordering in the array layout. The R version of APL uses columnmajor ordering. By slightly changing the two basic building blocks of our code, the
aplDecode() and aplEncode() functions, it would be easy to choose between
row-major and column-major layouts. But this would make it more complicated to
use the code with the rest of R.
Because of layout, the two arrays A3 3 327 and array(1:27,rep(3,3))
are different. But what is really helpful in linking the two environments is that
,A3 3 327 and as.vector(array(1:27,rep(3,3)), which both ravel
the array to a vector, give the same result, the vector 27 or 1:27. This is, of
course, because ravelling an array is the inverse of reshaping a vector.
Most of the functions in R are written with arrays of numbers in mind. Most of
them will work for array with elements of type logical, and quite a few of them
will also work for arrays of type character. We have to keep in mind, however, that
APL and R treat character arrays quite differently. In R we have length("aa")
equal to 1, because "aa" is a vector with as its single element the string "aa". R
has no primitive character type, characters are just strings which happen to have
only one character in them. In APL strings themselves are vectors of characters,
and "aa" is 2. In R we can say a<-array("aa",c(2,2,2)), but in APL this
gives a domain error. In APL we can say 2 2 2"aa", which gives the same
result as 2 2 2"a" or 2 2 2'a'.
JAN DE LEEUW
In this version of the code we have not implemented the nested arrays of APL-2.
Nesting gives every array A not just a shape A and a rank A, but also a depth.
The depth of an array of numbers of characters is one, the depth of a nested array
is the maximum depth of its elements.
There are many dialects of APL, and quite a few languages derived from APL, such
as A+ and J. For APL-I we use Helzer [1989] and for APL2 we use IBM [1988]
and MicroAPL [2007].
2. F UNCTIONS
2.1. Compress. Compress [IBM, 1988, p. 9192] is Replicate L/R in the special case that L is binary. So look under Replicate.
2.2. Decode.
2.2.1. Definition. Decode [IBM, 1988, p. 94] The dyadic operator LR is known
as Base Value [Helzer, 1989, p. 17-21] in APL-I.
If L is scalar and R is a vector, then LR is the polynomial r1 xm1 + r2 xm2 + +
rm evaluated at L. This means that if the ri are nonnegative integers less than L,
then LR gives the base-10 equivalent of the base-L number R.
If the arguments L and R are vectors of the same length. In the array context,
decode returns the index of element x[b] in an array x with dim(x)=a. Obviously
the R implementation, which uses colum-major ordering, will give results different
from the APL implementation. In APL the expression 3 3 31 2 3 evaluates
to 18, while aplDecode(1:3,rep(3,3)) gives 22.
2.2.2. R code.
1
aplDecode<-function(ind,base) {
if (length(base) == 1)
base<-array(base,aplShape(ind))
3
4
b<-c(1,butLast(cumprod(base)))
return(1+sum(b*(ind-1)))
2.2.3.
APL IN R
2.3. Drop.
1
aplDrop<-function(a,x,drop=FALSE) {
sa<-aplShape(a); ra<-aplRank(a)
y<-as.list(rep(0,ra))
for (i in 1:ra) {
9
10
return(aplSelect(a,y,drop))
}
aplEncode<-function(rep,base) {
b<-c(1,butLast(cumprod(base)))
r<-rep(0,length(b)); s<-rep-1
for (j in length(base):1) {
r[j]<-s%/%b[j]
s<-s-r[j]*b[j]
}
7
8
return(1+r)
2.5. Expand.
1
aplEXV<-function(x,y) {
z<-rep(0,length(y))
m<-which(y)
if (length(m) != length(x))
stop("Incorrect vector length in aplEXV")
5
6
z[which(y)]<-x
return(z)
6
1
JAN DE LEEUW
aplExpand<-function(x,y,axis=1) {
if (is.vector(x)) return(aplEXV(x,y))
if (m != d[axis])
stop("Incorrect dimension length in aplEX")
5
6
z<-array(0,e)
apply(z,(1:n)[-axis],function(i) z[i]<-x[i])
aplIPV<-function(x,y,f="*",g="+"){
if (length(x) != length(y))
stop("Incorrect vector length in aplIPV")
3
4
if (length(x) == 0) return(x)
z<-match.fun(f)(x,y)
return(aplRDV(z,g))
aplInnerProduct<-function(a,b,f="*",g="+") {
sa<-aplShape(a); sb<-aplShape(b)
ra<-aplRank(a); rb<-aplRank(b)
ia<-1:(ra-1); ib<-(ra-1)+(1:(rb-1))
ff<-match.fun(f); gg<-match.fun(g)
ns<-last(sa); nt<-first(sb)
if (ns != nt)
8
9
10
z<-array(0,sz)
11
for (i in 1:nz) {
12
ivec<-aplEncode(i,sz)
13
for (j in 1:ns) {
14
aa<-a[aplDecode(c(ivec[ia],j),sa)]
15
bb<-b[aplDecode(c(j,ivec[ib]),sb)]
16
z[i]<-gg(z[i],ff(aa,bb))
17
18
APL IN R
19
return(z)
20
2.7. Join.
1
aplJN<-function(a,b,k) {
if (!identical(sa[-k],sb[-k]))
ra)
z<-array(0,sz)
for (i in 1:nz) {
10
11
ivec<-aplEncode(i,sz)
12
13
14
15
return(z)
16
aplMemberOf<-function(a,b) {
if (!identical(typeof(a),typeof(b)))
warning("Arguments of different types in aplMO")
arrTest(a); arrTest(b)
sa<-aplShape(a); sb<-aplShape(b)
na<-prod(sa); nb<-prod(sb)
z<-array(0,sa)
for (i in 1:na) {
9
10
11
z[i]<-0; aa<-a[i]
for (j in 1:nb)
if (identical(aa,b[j])) z[i]<-1
JAN DE LEEUW
12
13
return(z)
14
aplOP<-function(x,y,f="*") return(outer(x,y,f))
2.10. Ravel.
1
aplRV<-function(a) as.vector(a)
2.11. Rank.
1
aplRank<-function(a) aplShape(aplShape(a))
2.12. Reduce.
1
aplRDV<-function(x,f="+") {
if (length(x) == 0) return(x)
s<-x[1]
if (length(x) == 1) return(s)
for (i in 2:length(x))
s<-match.fun(f)(s,x[i])
return(s)
7
8
aplReduce<-function(a,k,f="+") {
if (is.vector(a))
return(aplRDV(a,f))
ia<-(1:ra)[-k]
sz<-sa[ia]
ff<-match.fun(f)
z<-array(0,sz); nz<-prod(sz)
for (i in 1:na) {
10
ivec<-aplEncode(i,sa)
11
jind<-aplDecode(ivec[-k],sz)
12
z[jind]<-ff(z[jind],a[i])
APL IN R
13
14
return(z)
15
2.13. Replicate.
1
aplRPV<-function(x,y) {
n<-aplShape(x); m<-aplShape(y)
if (m == 1) y<-rep(y,n)
if (length(y) != n)
stop("Length Error in aplCRV")
5
6
z<-vector()
for (i in 1:n)
z<-c(z,rep(x[i],y[i]))
8
9
10
return(z)
}
aplReplicate<-function(x,y,k) {
if (is.vector(x)) return(aplRPV(x,y))
if (sy == 1) y<-rep(y,sk)
if (length(y) != sk)
stop("Length Error in aplRPV")
6
7
gg<-aplCRV(1:sk,y)
z<-array(0,sz)
for (i in 1:nz){
10
11
jvec<-aplEncode(i,sz)
12
jvec[k]<-gg[jvec[k]]
13
z[i]<-x[aplDecode(jvec,sx)]
14
15
return(z)
16
2.14. Reshape.
1
aplReshape<-function(a,d) return(array(a,d))
10
JAN DE LEEUW
2.15. Rotate. Rotate [Helzer, 1989, p. 191193] shifts the elements of a vector or
array dimension. In APL we write AB.
1
aplRTV<-function(a,k) {
n<-aplShape(a)
if (k == 0) return(a)
if (k > 0)
return(c(a[-(1:k)],a[1:k]))
if (k < 0)
return(c(a[(n+k+1):n],a[1:(n+k)]))
7
8
aplRotate<-function(a,x,k) {
if (is.vector(a)) return(aplRTV(a,k))
sa<-aplShape(a); sx<-aplShape(x)
if (sx == 1) x<-array(x,sa[-k])
if (!identical(sa[-k],aplShape(x)))
stop("Index Error in aplRotate")
6
7
for (i in 1:nz) {
ivec<-aplEncode(i,sz)
9
10
xx<-x[aplDecode(ivec[-k],sx)]
11
ak<-rep(0,sk)
12
for (j in 1:sk) {
13
jvec<-ivec; jvec[k]<-j
14
ak[j]<-a[aplDecode(jvec,sz)]
15
16
bk<-aplRTV(ak,xx)
17
for (j in 1:sk) {
18
jvec<-ivec; jvec[k]<-j
19
z[aplDecode(jvec,sz)]<-bk[j]
20
21
22
return(z)
23
2.16. Scan.
APL IN R
1
11
aplSCV<-function(x,f="+") {
return(sapply(1:length(x),function(i) aplRDV(x[1:i],f
)))
aplSC<-function(a,k,f="+") {
if (is.vector(a)) return(aplSCV(a,f))
ff<-match.fun(f)
na<-prod(sa); z<-a
for (i in 1:na) {
ivec<-aplEncode(i,sa)
sk<-ivec[k]
if (sk == 1) z[i]<-a[i]
else z[i]<-ff(z[aplDecode(ivec-u,sa)],a[i])
10
11
12
return(z)
13
2.17. Select.
1
aplSelect<-function(a,x,drop=FALSE) {
sa<-aplShape(a); ra<-aplRank(a)
sz<-sapply(x,length)
z<-array(0,sz); nz<-prod(sz)
for (i in 1:nz) {
ivec<-aplEncode(i,sz)
jvec<-vector()
for (j in 1:ra)
jvec<-c(jvec,x[[j]][ivec[j]])
9
10
z[i]<-a[aplDecode(jvec,sa)]
11
12
13
12
JAN DE LEEUW
2.18. Shape. Shape is the monadic version of , while reshape is the dyadic version. Shape gives the dimensions of an array, an Reshape modifies them. aplRank
() is not really a standard APL function, but we use it as shorthand for A.
1
aplShape<-function(a) {
if (is.vector(a)) return(length(a))
return(dim(a))
2.19. Take.
1
aplTK<-function(a,x,drop=FALSE) {
sa<-aplShape(a); ra<-aplRank(a)
y<-as.list(rep(0,ra))
for (i in 1:ra) {
9
10
return(aplSelect(a,y,drop))
}
2.20. Transpose. APL has both a monadic A and a dyadic BA transpose. This
APL transpose has a somewhat tortuous relationship with Rs aperm().
The monadic aplTranspose(a) and aperm(a) are always the same, they
reverse the order of the dimensions.
If x is a permutation of 1:aplRank(a), then aperm(a,x) is actually equal to
aplTranspose(a,order(x)). For permutations we could consequently define
aplTranspose(a,x) simply as aperm(a,order(x)) (which would undoubtedly be more efficient as well).
If x is not a permutation, then aperm(a,x) is undefined, but aplTranspose(a,
x) can still be defined in some cases.
If x has aplRank(a) elements equal to one of 1:m, with each of 1:m occurring
a least once, then aplTranspose(a,x) has rank m. For obvious reasons dyadic
transpose is not used a great deal.
APL IN R
1
aplTranspose<-function(a,x=rev(1:aplRank(a))) {
sa<-aplShape(a); ra<-aplRank(a)
if (length(x) != ra)
stop("Length Error in aplTranspose")
4
5
rz<-max(x); sz<-rep(0,rz)
for (i in 1:rz)
sz[i]<-min(sa[which(x==i)])
7
8
nz<-prod(sz)
z<-array(0,sz)
10
13
for (i in 1:nz)
z[i]<-a[aplDecode(aplEncode(i,sz)[x],sa)]
11
12
return(z)
13
3. U TILITIES
first<-function(x) return(x[1])
2
3
butFirst<-function(x) return(x[-1])
4
5
last<-function(x) return(x[length(x)])
6
7
butLast<-function(x) return(x[-length(x)])
8
9
unit<-function(i,n) ifelse(i==(1:n),1,0)
R EFERENCES
G. Helzer. An Encyclopedia of APL. I-APL LTD, St. Albans, G.B., second edition,
1989.
IBM. APL2 Programming: Language Reference. IBM, San Jose, California,
Fourth edition, April 1988.
K. Iverson. A Programming Language. Wiley, 1962.
MicroAPL. APLX Language Manual. MicroAPL Ltd., Version 4 edition, November 2007.
14
JAN DE LEEUW