如何为未计算的赋值表达式定义 S3 方法?

Kon*_*lph 5 r r-s3 non-standard-evaluation

\n

tl;drR CMD check当我为 S3 类实现泛型时会抱怨<-,因为它认为该函数是参数不正确的替换函数。

\n
\n

我需要定义一组 S3 泛型来遍历未计算的 R 表达式的 AST。

\n

出于演示目的,请考虑以下 S3 泛型及其方法:

\n
walk = function (x) UseMethod(\'walk\')\n\nwalk.default = function (x) message(\'default\')\n\nwalk.name = function (x) message(\'name\')\n\nwalk.call = function (x) message(\'call\')\n
Run Code Online (Sandbox Code Playgroud)\n

这工作正常:

\n
tests = alist(\'2\', c, f(1))\n\ninvisible(lapply(tests, walk))\n
Run Code Online (Sandbox Code Playgroud)\n
walk = function (x) UseMethod(\'walk\')\n\nwalk.default = function (x) message(\'default\')\n\nwalk.name = function (x) message(\'name\')\n\nwalk.call = function (x) message(\'call\')\n
Run Code Online (Sandbox Code Playgroud)\n

然而,有相当多的调用表达式的 S3 类是 \xe2\x80\x99t call;例如:

\n
tests2 = alist(for (x in y) ., if (.) ., x <- .)\ninvisible(lapply(tests2, walk))\n
Run Code Online (Sandbox Code Playgroud)\n
tests = alist(\'2\', c, f(1))\n\ninvisible(lapply(tests, walk))\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\xa6 哎呀。我希望这些表达式被视为调用。我可以通过添加更多方法来做到这一点:

\n
walk.for = function (x) message(\'for\')\n\nwalk.if = function (x) message(\'if\')\n\n`walk.<-` = function (x) message(\'<-\')\n\n# \xe2\x80\xa6 and so on for other syntax constructs.\n
Run Code Online (Sandbox Code Playgroud)\n

现在我\xe2\x80\x99m 在调用时得到了预期的结果walk

\n
default\nname\ncall\n
Run Code Online (Sandbox Code Playgroud)\n

然而,这段代码是包的一部分,并且R CMD check抱怨 的定义,walk.<-因为它认为该函数是一个替换函数:

\n
\n
W  checking replacement functions ...\n    \xe2\x80\x98walk.<-\xe2\x80\x99\n  The argument of a replacement function which corresponds to the right\n  hand side must be named \xe2\x80\x98value\xe2\x80\x99.\n
Run Code Online (Sandbox Code Playgroud)\n
\n

我明白为什么我\xe2\x80\x99m 收到警告(事实上,这看起来像是为 定义替换函数的拙劣尝试walk.)。但当然这不是\xe2\x80\x99t替换函数,因此警告是误报。那么我应该如何<-明确地为该类实现 S3 泛型呢?或者这是一个错误R CMD check?我可以摆脱警告吗?我可以\xe2\x80\x99t 向函数添加其他参数。

\n

Kon*_*lph 2

编写 R 扩展,第1.5.2 节注册 S3 方法提供了一个解决方案:

\n
\n

可以指定第三个参数S3method,即用作方法的函数,例如

\n
S3method(print, check_so_symbols, .print.via.format)\n
Run Code Online (Sandbox Code Playgroud)\n

什么时候print.check_so_symbols不需要。

\n
\n

这意味着我们不定义`walk.<-`,而是执行以下操作:

\n

实施于R/\xe2\x80\xb9some-file\xe2\x80\xba.r

\n
walk_assign = function (x) message(\'<-\')\n
Run Code Online (Sandbox Code Playgroud)\n

(与其他 S3 方法不同,函数名称并不重要。)

\n

NAMESPACE宣言:

\n
S3method(walk, \'<-\', walk_assign)\n
Run Code Online (Sandbox Code Playgroud)\n

.S3method(\'walk\', \'<-\', \\(x) message(\'<-\'))这实际上与在常规 R 脚本内调用相同。有趣的是,似乎.S3method() 可以在包内工作(而不是基于上述的NAMESPACE解决方案),但 function\xe2\x80\x99s 文档表示不要在包中使用它,因此将来可能会中断。

\n
\n

当然,正如艾伦·卡梅伦(Allan Cameron)评论的那样,一个可以说更简单的解决方案是将外壳<-与内部融合default并使用if内部walk.default来消除歧义:

\n
walk.default = function (x) {\n    if (inherits(x, \'<-\')) {\n        message(\'<-\')\n    } else {\n        message(\'default\')\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然而,我个人不喜欢将 S3 调度与if.

\n