I wanted to play around with modular arithmetic and programmed some innocently looking function... but got totally surprised by the following unexpected behaviour:
crt <- function(x, mods = c(5, 7)) {
sapply(mods, \(y) x %% y)
}
crt <- Vectorize(crt)
crt(20)
## [,1]
## [1,] 0
## [2,] 6
crt(55)
## [,1]
## [1,] 0
## [2,] 6
crt(1:100)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,] 1 2 3 4 0 1 2 3 4 0 1 2 3 4
## [2,] 1 2 3 4 5 6 0 1 2 3 4 5 6 0
## [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26]
## [1,] 0 1 2 3 4 0 1 2 3 4 0 1
## [2,] 1 2 3 4 5 6 0 1 2 3 4 5
## [,27] [,28] [,29] [,30] [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38]
## [1,] 2 3 4 0 1 2 3 4 0 1 2 3
## [2,] 6 0 1 2 3 4 5 6 0 1 2 3
## [,39] [,40] [,41] [,42] [,43] [,44] [,45] [,46] [,47] [,48] [,49] [,50]
## [1,] 4 0 1 2 3 4 0 1 2 3 4 0
## [2,] 4 5 6 0 1 2 3 4 5 6 0 1
## [,51] [,52] [,53] [,54] [,55] [,56] [,57] [,58] [,59] [,60] [,61] [,62]
## [1,] 1 2 3 4 0 1 2 3 4 0 1 2
## [2,] 2 3 4 5 6 0 1 2 3 4 5 6
## [,63] [,64] [,65] [,66] [,67] [,68] [,69] [,70] [,71] [,72] [,73] [,74]
## [1,] 3 4 0 1 2 3 4 0 1 2 3 4
## [2,] 0 1 2 3 4 5 6 0 1 2 3 4
## [,75] [,76] [,77] [,78] [,79] [,80] [,81] [,82] [,83] [,84] [,85] [,86]
## [1,] 0 1 2 3 4 0 1 2 3 4 0 1
## [2,] 5 6 0 1 2 3 4 5 6 0 1 2
## [,87] [,88] [,89] [,90] [,91] [,92] [,93] [,94] [,95] [,96] [,97] [,98]
## [1,] 2 3 4 0 1 2 3 4 0 1 2 3
## [2,] 3 4 5 6 0 1 2 3 4 5 6 0
## [,99] [,100]
## [1,] 4 0
## [2,] 1 2
crt(x = 1:100, mods = c(12, 60))
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 1 14 3 16 5 18 7 20 9 22 11 24 1
## [26] 26 3 28 5 30 7 32 9 34 11 36 1 38 3 40 5 42 7 44 9 46 11 48 1 50
## [51] 3 52 5 54 7 56 9 58 11 0 1 2 3 4 5 6 7 8 9 10 11 12 1 14 3
## [76] 16 5 18 7 20 9 22 11 24 1 26 3 28 5 30 7 32 9 34 11 36 1 38 3 40
Why is the last function call crt(x = 1:100, mods = c(12, 60))
giving a totally different output? The first vectorized output crt(1:100)
is what I wanted and expected, the last one doesn't seem structurally different but the result is... why? And how do I fix this to get the same output as the first?
According to ?Vectorize
The arguments named in the vectorize.args argument to Vectorize are the arguments passed in the ... list to mapply. Only those that are actually passed will be vectorized; default values will not.
Here, in the OP's function, there is default value for 'mods'. If we remove it
crt <- function(x, mods) {
sapply(mods, \(y) x %% y)
}
crt <- Vectorize(crt)
-testing
> crt(1:100, mods = c(5, 7))
[1] 1 2 3 4 0 6 2 1 4 3 1 5 3 0 0 2 2 4 4 6 1 1 3 3 0 5 2 0 4 2 1 4 3 6 0 1 2 3 4 5 1 0 3 2 0 4 2 6 4 1 1 3 3 5 0 0 2 2 4 4 1 6 3 1 0 3 2 5 4 0 1 2 3 4
[75] 0 6 2 1 4 3 1 5 3 0 0 2 2 4 4 6 1 1 3 3 0 5 2 0 4 2
> crt(1:100, mods = c(12, 60))
[1] 1 2 3 4 5 6 7 8 9 10 11 12 1 14 3 16 5 18 7 20 9 22 11 24 1 26 3 28 5 30 7 32 9 34 11 36 1 38 3 40 5 42 7 44 9 46 11 48 1
[50] 50 3 52 5 54 7 56 9 58 11 0 1 2 3 4 5 6 7 8 9 10 11 12 1 14 3 16 5 18 7 20 9 22 11 24 1 26 3 28 5 30 7 32 9 34 11 36 1 38
[99] 3 40
The output format is determined at two levels here - 1) sapply
which by default uses simplify = TRUE
and Vectorize
which also by default have SIMPLIFY = TRUE
Also, based on the function defined, Vectorize
is not really needed as internally, it does the looping with *apply
functions and we already have the crt
defined with sapply
which loop over the 'mods'. The function applied %%
on those parameters is %%
which is already a vectorized function by default.