Search code examples
rapplymapplyoptional-arguments

When can mapply's MoreArgs argument not be replaced by R's vector recycling rules?


I'm trying to come up with an example of when mapply's MoreArgs argument is useful. I have been utterly defeated. Outrageously, even the example given in mapply's documentation is inadequate. The docs give mapply(rep, times = 1:4, MoreArgs = list(x = 42)) as their only example of the use of MoreArgs, but I have found that R's vector recycling rules mean that mapply(rep, times = 1:4, 42) gives exactly the same output.

So when can MoreArgs not be replaced by just deleting the corresponding MoreArgs = list(...) wrapper? I tried to come up with some examples myself, but I failed every time. For example, mapply(rnorm,1:10,11:20,MoreArgs = list(5),SIMPLIFY = FALSE) is identical to mapply(rnorm,1:10,11:20,5,SIMPLIFY = FALSE).

Following the lead of the comments, I've also tried examples where we need to recycle a vector for each call to the function that mapply is being called on. However, it seems that mapply has no difference in functionality between using a list containing said vector - e.g. list(c(0.6,0.3,0.1)) - as a ... argument or as a MoreArgs argument. In the MoreArgs case, the content of the list is recycled as expected and in the ... case, the vector recycling rules mean that the content of the list will be recycled in each case, giving identical functionality to MoreArgs. This brings me back to the original question: When can the MoreArgs argument of mapply be used to give functionality that cannot be gotten from the ... argument and the vector recycling rules? I've yet to see a case where identical functionality cannot be gained by simply deleting the MoreArgs= argument and letting the relevant parts pass to ....


Solution

  • I've yet to see a case where identical functionality cannot be gained by simply deleting the MoreArgs= argument and letting the relevant parts pass to ....

    This is wrong, but only slightly. Compare:

    options(max.print = 50)#Before running this, make sure that you know how to undo it.
    
    > mapply(sum,1:5,MoreArgs=list(runif(10),runif(10000)))
    [1] 5019.831 5020.831 5021.831 5022.831 5023.831
    
    > mapply(sum,1:5,list(runif(10)),list(runif(10000)))
    [1] 5069.321 5070.321 5071.321 5072.321 5073.321
    
    > mapply(sum,1:5,list(runif(10),runif(10000)))
    [1]    6.658275 4984.177882    8.658275 4986.177882   10.658275
    
    > mapply(sum,1:5,runif(10),runif(10000))
     [1] 1.750417 3.286090 3.186474 5.310268 5.962829 1.343564 2.325567 3.928796 4.955376
    [10] 5.507385 1.992290 3.454536 3.399763 5.242883 5.589296 1.637056 2.964259 3.839006
    [19] 5.647123 5.883139 1.863512 2.827110 3.633137 5.174900 5.365155 2.022725 3.139846
    [28] 3.830624 5.064546 5.697612 1.242803 3.456888 3.726114 5.271773 5.881724 1.533730
    [37] 2.489976 3.509690 5.657166 5.400823 1.972689 2.858276 3.571505 5.582752 5.482381
    [46] 1.956237 2.497409 3.864434 5.389969 5.965341
     [ reached getOption("max.print") -- omitted 9950 entries ]
    Warning message:
    In mapply(sum, 1:5, list(runif(10), runif(10000))) :
      longer argument not a multiple of length of shorter
    

    In the first case, every element of the list in the MoreArgs argument is recycled for each call. Similarly, the second case is recycling both runif(10) and runif(10000) for each call, giving behavior that I'm confident to call identical. The fourth case only exists to shows what we get if we're silly enough to not use any lists at all.

    My above quoted claim is that the first and third cases should be identical. This is clearly not the case. If we try to use one list (rather than two, as our second case did) without MoreArgs, R's normal vector recycling rules will have us reuse the value of runif(10) for the first, third, and fifth calls, and use runif(10000) for the second and fourth, and give us a warning due to this odd behavior.

    In conclusion, it still appears that the MoreArgs argument can always be replaced by R's vector recycling rules (despite my previous answer), but not in the exact way that I said in the question. The truth appears to be that MoreArgs=list(foo,bar,etc) is equivalent to using list(foo), list(bar), and list(etc) as ... arguments to mapply. Notice that this is not the same as using list(foo,bar,etc) as a ... argument. So, ultimately: You will not always get identical functionality from omitting the MoreArgs argument.

    As a final minor detail: Omitting the MoreArgs argument is harmless, but omitting the ... argument and using MoreArgs instead gives unexpected output, usually an empty list.