【发布时间】:2015-11-16 14:30:48
【问题描述】:
在 R 中,常见的函数调用模式如下所示:
child = function(a, b, c) {
a * b - c
}
parent = function(a, b, c) {
result = child(a=a, b=b, c=c)
}
这种名称的重复很有帮助,因为如果要更改子参数的顺序,或者要在列表中添加其他变量,它可以防止潜在的潜在错误:
childReordered = function(c, b, a) { # same args in different order
a * b - c
}
parent = function(a, b, c) {
result = childReordered(a, b, c) # probably an error
}
但是当参数的名称变长时,这会变得很麻烦:
child = function(longVariableNameA, longVariableNameB, longVariableNameC) {
longVariableNameA * longVariableNameB - longVariableNameC
}
parent = function(longVariableNameA, longVariableNameB, longVariableNameC) {
child(longVariableNameA=longVariableNameA, longVariableNameB=longVariableNameB, longVariableNameC=longVariableNameB)
}
我想有一种方法来获得命名参数的安全性,而无需再次实际键入名称。当我只能修改父母而不是孩子时,我希望能够做到这一点。我会设想这样的事情:
parent = function(a, b, c) {
result = child(params(a, b, c))
}
params() 是一个新函数,它根据变量的名称将未命名的参数转换为命名参数。例如:
child(params(c,b,a)) == child(a=a, b=b, c=c)
“pryr”中有几个功能接近于此,但我还没有想出如何将它们结合起来做我想要的。 named_dots(c,b,a) 返回list(c=c, b=b, a=a),而standardise_call() 也有类似的操作,但是我还没弄清楚如何能够将结果转换成可以传递给未修改的child() 的东西。
我希望能够混合使用隐式和显式命名参数:
child(params(b=3, c=a, a)) == child(b=3, c=a, a=a)
如果能够混入一些未命名的常量(不是变量),并在传递给子进程时将它们视为命名参数,那也很好。
child(params(7, c, b=x)) == child(a=7, c=c, b=x) # desired
named_dots(7, c, b=x) == list("7"=7, c=c, b=x) # undesired
但以非 R 方式,我更愿意提出错误,而不是试图应付可能的程序员错误:
child(params(c, 7, b=x)) # ERROR: unnamed parameters must be first
是否有现有的工具可以做到这一点?将现有功能拼凑起来做我想做的简单方法?更好的方法来实现在更改参数列表的情况下获得安全的相同目标而无需繁琐的重复?对我建议的语法进行改进以使其更加安全?
赏金前澄清:parent() 和 child() 函数都应被视为不可更改。我对使用不同的界面进行包装不感兴趣。相反,我在这里寻找一种方法来编写建议的params() 函数,以一种可以动态重写参数列表的通用方式,以便parent() 和child() 可以直接使用安全但非详细语法。
赏金后澄清:虽然反转父子关系并使用 do.call() 是一种有用的技术,但它不是我在这里寻找的技术。相反,我正在寻找一种接受“...”参数的方法,将其修改为具有命名参数,然后以封闭函数将接受的形式返回它。正如其他人所暗示的那样,这可能真的是不可能的。就个人而言,我目前认为 C 级扩展是可能的,我希望这个扩展已经存在。也许vadr 包可以满足我的需求? https://github.com/crowding/vadr#dot-dot-dot-lists-and-missing-values
部分功劳:让赏金到期我觉得很愚蠢。如果没有完整的解决方案,我会将其奖励给任何提供至少一个必要步骤的概念证明的人。例如,修改函数中的“...”参数,然后将其传递给另一个函数而不使用 do.call()。或者以父母可以使用的方式返回未修改的“...”参数。或者任何最能指出直接解决方案的方法,甚至是一些有用的链接:http://r.789695.n4.nabble.com/internal-manipulation-of-td4682090.html 但我不愿意将它授予一个以(否则完全合理)前提为“你不想这样做”,或者“那是不可能的,所以这是一个替代方案”。
赏金奖励:有几个非常有用且实用的答案,但我选择将赏金奖励给@crowding。虽然他(可能正确)断言我想要的东西是不可能的,但我认为他的回答最接近我所追求的“理想主义”方法。我还认为他的vadr 包可能是解决方案的一个很好的起点,无论它是否符合我的(可能不切实际的)设计目标。如果有人想出一种方法来完成不可能的事情,那么“公认的答案”仍然有待商榷。感谢其他答案和建议,希望它们能帮助某人将这些部分组合在一起以获得更强大的 R 语法。
【问题讨论】:
-
do.call和match.call并不能满足您的所有需求,但我认为仔细研究这些功能可能会让您感兴趣。顺便说一句,你的问题让我想起了前段时间的a question I asked,很遗憾没有得到答复。 -
如果您提供一些测试用例来验证可能的解决方案会有所帮助。
-
解决方案是任何函数 params(),这样 child(params(c,b,a)) 的计算结果总是与 child(c=c, b=b, a=a) 相同。您和 mnel 都使用包装器提供了实用的解决方案,希望这对能够使用 do.call() 语法的其他人有用。但是出于这个问题和赏金的目的,我只是在寻找一种方法来编写一个接受多参数“...”参数的函数,将其修改为具有命名参数,并以父级的方式返回它函数将接受这个单个参数来代替它通常会接收的多个参数。
-
@NathanKurz 我可以得到您的最后一条评论,您仍然需要修改对这些函数的每次调用以在其中添加新函数,因此重构使用包装器并不比您要求的复杂for...(并且 do.call 方法不会反转关系)
-
很好奇@Mnel 的回答以及我对此的解释未能解决您的问题。我想这可能是“
parent和child都应该被认为是不可更改的”,但是在您自己的示例中,您可以使用child(params(...))更改parent。我认为这与params(child)的变化幅度相同,这基本上是 mnel / my answer 所做的。