递归默认参数引用下R中x[i]返回x的原因及行为合理性问询
R中递归默认参数与索引操作的行为差异
基础递归默认参数错误示例
默认参数在函数作用域内求值,直接引用同名参数会触发递归引用错误,这是预期行为:
i = 1 f1 = function(i=i) i f1() # Error: promise already under evaluation: recursive default argument reference
单括号[的异常静默行为
当递归默认参数出现在单括号索引中时,不会报错,但返回结果不符合预期:
x = 1:5 i = 1:3 f2 = function(x, i=i) x[i] f2(x) # 无报错,但返回x而非预期的x[i]
即使全局环境中不存在i,该静默错误依然存在:
x = 1:5 if (exists("i")) rm(i) f3 = function(x, i=i) x[i] f3(x) # 无报错,仍返回x,哪怕i不存在
双括号[[的合理报错行为
使用双括号索引时,会正常抛出缺失下标的错误:
x = 1:5 i = 1:3 f4 = function(x, i=i) x[[i]] f4(x) # Error: missing subscript
问题解答
1. f2和f3的行为是预期设计还是bug?
这是预期设计,并非bug。
2. 背后的技术逻辑
要理解这个行为,得从R的参数求值规则和[/[[的实现差异两方面拆解:
- 默认参数的Promise机制:R的函数默认参数是延迟求值的Promise对象。定义
function(x, i=i)时,参数i的默认值是一个Promise,指向表达式i,但这个表达式的求值环境是函数的调用环境(此处为全局环境)。不过直接在函数体调用i时,会触发递归解析:要获取i的值就得解析它的默认Promise,而Promise的表达式又是i,形成循环,所以f1()报错。 [的宽松处理逻辑:[运算符设计时支持灵活子集选择,当遇到缺失的下标(这里因递归引用导致i的Promise无法解析,处于"missing"状态),会将其视为"空选择"——等价于x[],直接返回整个对象x,而非报错。[[的严格性:[[是用于提取单个元素的运算符,设计上要求下标必须明确存在且有效,不允许缺失。因此当i处于missing状态时,[[直接抛出"missing subscript"错误,符合其定位。
简单来说:[允许缺失下标并返回全集,[[要求下标必须存在,结合默认参数的递归引用导致下标处于missing状态,最终出现两种索引方式的行为差异。
内容的提问来源于stack exchange,提问作者rafsav
相关产品推荐
相关产品推荐

