R语言中为何mapply的MoreArgs参数内做变量赋值与...参数内结果不同
两次调用结果差异的核心原因
所有差异来自R的参数求值规则,以及mapply对不同参数的处理逻辑,拆解如下:
基础规则
- R里只要是出现在函数调用参数位置的赋值表达式,会在调用发生时直接在全局执行环境跑一遍,不会等函数内部真的用到这个参数才执行。赋值本身是带副作用的操作,求值时机比函数内部的参数读取要早得多。
- mapply对两类参数的分工是完全隔离的:
...位置的输入,就是用来跑循环的源数据——mapply会根据这些输入的长度定循环次数,每次把每个输入里的对应元素拿出来,凑成参数传给FUN执行MoreArgs位置的内容,是每次循环都要带上的固定参数——它不会参与计算循环次数,只有真的进入循环、要调用FUN的时候,才会把这些内容打包传进去
逐次调用的实际执行流程
- 第一次调用:
mapply(sum, list(x <- x + 1))- 首先在全局环境跑参数里的
list(x <- x +1):x从初始值1变成2,返回一个长度为1的列表,列表里的元素就是赋值后x的值2 - mapply看到
...位置的输入是这个长度为1的列表,就定循环次数为1 - 把列表里的2拿出来,调用
sum(2)得到2,最终返回这个结果 - 因为赋值操作在构造列表的时候就跑完了,所以全局的x最后是2,和实际跑出来的结果完全对得上
- 首先在全局环境跑参数里的
- 第二次调用:
mapply(sum, MoreArgs = list(x <- x + 1))- 首先在全局环境跑参数里的
list(x <-x +1):x从2变成3,返回一个内容为3的列表 - mapply检查
...位置:根本没有收到任何用来跑循环的源数据,传入的dots列表长度是0 - 按照mapply的内部逻辑,只要
...没有输入,就直接返回空列表,根本不会碰sum这个函数,更别说把MoreArgs的参数传进去了 - x最后变成3的原因是:MoreArgs对应的那个list表达式本身是写在调用的参数位置上的,R在构造这个函数调用的时候,就已经把里面的赋值操作在全局环境跑完了——不管后面mapply会不会用到这个参数,赋值的副作用已经产生了。
- 首先在全局环境跑参数里的
为什么之前会预判两次结果一致
之前觉得两次会出一样的结果,本质是混淆了两个逻辑:
- 误以为MoreArgs里的表达式会被直接当成FUN的参数执行,实际上只有mapply真的进入循环流程,才会把MoreArgs的内容传给FUN
- 误以为参数里的赋值会等到函数内部才执行,实际上只要是写在参数列表里的赋值语法,调用的时候就会立刻在全局环境产生副作用,和函数后续用不用这个参数没关系
内容的提问来源于stack exchange,提问作者J. Mini
相关产品推荐
相关产品推荐

