I recently used the following line of code, expecting to get an error. To my surprise, I was given an output:
> outer(1:5,5:10,c=1:3,function(a,b,c) 10*a + 100*b + 1000*c)
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1510 3610 2710 1810 3910 3010
[2,] 2520 1620 3720 2820 1920 4020
[3,] 3530 2630 1730 3830 2930 2030
[4,] 1540 3640 2740 1840 3940 3040
[5,] 2550 1650 3750 2850 1950 4050
It appears that the code is being evaluated to outer(1:5,5:10,function(a,b) 10*a + 100*b)+1000*(1:3)
. Why is this? And as a follow-up, is there any clear reason why this doesn't give a warning? To my mind, a user who entered code like this was probably expecting an output covering all a, b, and c values.
This is expected behaviour based on R's recycling rules. It has nothing to do with outer
as such, though it might be a surprise if you think outer
is somehow applying a function across margins.
Instead, outer
takes two vectors X
and Y
as its first two arguments. It takes X
and rep
licates it length(Y)
times. Similarly, it takes Y
and rep
licates it length(X)
times. Then it just runs your function FUN
on these two long vectors, passing the long X
as the first argument and the long Y
as the second argument. Any other arguments to FUN
have to be passed directly as arguments to outer via ...
(as you have done with c = 1:3
).
The result is a single long vector which is turned into a matrix by writing its dim
attribute as the original values of length(X)
by length(Y)
.
Now, in the specific example you gave, X
has 5 elements (1:5) and Y
has 6 (5:10). Therefore your anonymous function is called on two length-30 vectors and a single length-3 vector. R's recycling rules dictate that if the recycled vector fits neatly into the longer vector without partial recycling, no warning is emitted.
To see this, take your anonymous function and try it outside outer
with two length-30 vectors and one length-3 vector:
f <- function(a, b, c) 10*a + 100*b + 1000*c
f(1:30, 1:30, 1:3)
#> [1] 1110 2220 3330 1440 2550 3660 1770 2880 3990 2100 3210 4320 2430
#> [14] 3540 4650 2760 3870 4980 3090 4200 5310 3420 4530 5640 3750 4860
#> [27] 5970 4080 5190 6300
3 recycles nicely into 30, so there is no warning.
Conversely, if the product of the length of the two vectors you pass to outer
is not a multiple of 3, you will get a warning:
outer(1:5,6:10,c=1:3,function(a,b,c) 10*a + 100*b + 1000*c)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 1610 3710 2810 1910 4010
#> [2,] 2620 1720 3820 2920 2020
#> [3,] 3630 2730 1830 3930 3030
#> [4,] 1640 3740 2840 1940 4040
#> [5,] 2650 1750 3850 2950 2050
#> Warning message:
#> In 10 * a + 100 * b + 1000 * c :
#> longer object length is not a multiple of shorter object length