参数通过点('...')传递时的部分匹配混淆

时间:2014-08-26 19:14:26

标签: r methods r-s3

我一直在研究一个R包,它只是一个图形数据库的REST API包装器。我有一个函数createNode,它返回一个包含类nodeentity的对象:

# Connect to the db.
graph = startGraph("http://localhost:7474/db/data/")

# Create two nodes in the db.
alice = createNode(graph, name = "Alice")
bob = createNode(graph, name = "Bob")

> class(alice)
[1] "node"   "entity"
> class(bob)
[1] "node"   "entity"

我有另一个函数createRel,它在数据库中的两个节点之间创建一个关系。它规定如下:

createRel = function(fromNode, type, toNode, ...) {
  UseMethod("createRel")
}

createRel.default = function(fromNode, ...) {
  stop("Invalid object. Must supply node object.")
}

createRel.node = function(fromNode, type, toNode, ...) {
  params = list(...)

  # Check if toNode is a node.
  stopifnot("node" %in% class(toNode))

  # Making REST API calls through RCurl and stuff.
}

...允许用户以key = value的形式向关系添加任意数量的属性。例如,

rel = createRel(alice, "KNOWS", bob, since = 2000, through = "Work")

这会在db中创建一个(Alice) - [KNOWS] - >(Bob)关系,其中包含属性sincethrough及其各自的值。但是,如果用户在from参数中指定了键to...的属性,则R会对fromNodetoNode的类感到困惑。

使用键from指定属性会导致对fromNode类的混淆。它正在使用createRel.default

> createRel(alice, "KNOWS", bob, from = "Work")

Error in createRel.default(alice, "KNOWS", bob, from = "Work") : 
Invalid object. Must supply node object. 
3 stop("Invalid object. Must supply node object.") 
2 createRel.default(alice, "KNOWS", bob, from = "Work") 
1 createRel(alice, "KNOWS", bob, from = "Work")

同样,如果用户指定的密钥为to的属性,则会对toNode的类产生混淆,并在stopifnot()处停止:

Error: "node" %in% class(toNode) is not TRUE 
4 stop(sprintf(ngettext(length(r), "%s is not TRUE", "%s are not all TRUE"), 
  ch), call. = FALSE, domain = NA) 
3 stopifnot("node" %in% class(toNode)) 
2 createRel.node(alice, "KNOWS", bob, to = "Something") 
1 createRel(alice, "KNOWS", bob, to = "Something")

我发现在createRel中明确设置参数可以正常工作:

rel = createRel(fromNode = alice,
                type = "KNOWS",
                toNode = bob,
                from = "Work",
                to = "Something")
# OK

但我想知道如何编辑我的createRel函数,以便以下语法可以正常工作:

rel = createRel(alice, "KNOWS", bob, from = "Work", to = "Something")
# Errors galore.

打开该问题的GitHub用户提到它很可能与发送时setAs发生冲突,其中包含名为fromto的参数。一种解决方案是摆脱...并将createRel更改为以下内容:

createRel = function(fromNode, type, toNode, params = list()) {
  UseMethod("createRel")
}

createRel.default = function(fromNode, ...) {
  stop("Invalid object. Must supply node object.")
}

createRel.node = function(fromNode, type, toNode, params = list()) {
  # Check if toNode is a node.
  stopifnot("node" %in% class(toNode))

  # Making REST API calls through RCurl and stuff.
}

但是,我想在做出这个改变之前看看我是否有其他选择。

1 个答案:

答案 0 :(得分:1)

不是真的答案,但是......

问题是用户提供的参数'from'正在(部分)匹配形式参数'fromNode'。

f = function(fromNode, ...) fromNode
f(1, from=2)
## [1] 2

规则在RShowDoc('R-lang')的第4.3.2节中概述,其中命名参数完全匹配,然后部分匹配,然后按位置分配未命名的参数。

除了使用单字母参数名称之外,很难知道如何强制执行完全匹配!实际上,对于泛型,这可能不像听起来那样陈旧 - x是一个非常通用的变量名。如果'from'和'to'是...的常见参数,你可以将参数列表更改为“fromNode ,, ...,from,to”,检查函数体中是否缺少(from),并执行因此,我不认为这会令人愉快,并且用户会不变地提供“来回”的论据。

通过设置全局options()来强制完全匹配(和错误,通过warn = 2)可能有助于调试(虽然那时你可能知道你在寻找什么!)它没有帮助正在尝试编写代码以便为用户工作的软件包作者。

在R-devel邮件列表上询问是否可能有时间更改此行为(在“多个版本”的时间范围内)可能是合理的;部分匹配可能是标签完成前几天的“便利”。