【发布时间】:2014-08-26 19:14:26
【问题描述】:
我一直在研究一个 R 包,它只是一个图形数据库的 REST API 包装器。我有一个函数createNode,它返回一个类node和entity的对象:
# 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")
这会在数据库中创建一个 (Alice)-[KNOWS]->(Bob) 关系,其中包含属性 since 和 through 及其各自的值。但是,如果用户在 ... 参数中使用键 from 或 to 指定属性,R 就会对 fromNode 和 toNode 的类感到困惑。
使用键 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 发生冲突,其参数名为 from 和 to。一种解决方案是去掉 ... 并将 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.
}
但是,在进行此更改之前,我想看看我是否还有其他选择。
【问题讨论】:
-
问题在于用户提供的参数“from”正在(部分)匹配正式参数“fromNode”。
f = function(fromNode, ...) fromNode; f(1, from=2)返回2。您可以添加一个from参数并检查正文中的缺失(来自),但这可能会令人不快。嗯,单字母参数名称似乎是唯一可靠的解决方案! -
哇,我没有意识到参数有资格进行部分匹配。这肯定是出乎意料的行为。
-
@MartinMorgan,
?options有warnPartialMatchArgs选项。是否可以以某种方式打开和关闭部分匹配? -
@DanielKrizian 你总是可以
options(warn=2);如果您正在编写代码供他人使用,options()将无济于事;RShowDoc('R-lang')第 4.3.2 节是描述参数匹配的地方。 -
关闭部分匹配的一种方法是将您的函数定义为
fun(..., arg1, arg2, etc),因为点后的参数禁用部分匹配,但这不适用于S3调度......另外两种选择:使用像.fromNode这样不太可能用作键值的 var 名称,或者实际上将键值对作为列表提交:fun(x, keyvals=list(from=blah, to=blah))