在R中允许用户定义的运算符,但似乎只接受% %
之类的运算符。是否有可能绕过这个限制来定义运算符,例如>>
,或者不像% %
的东西?
运算符必须是真正的运算符,以便我们可以像1 >> 2
一样使用它,而不必像">>"(1,2)
那样使用它。
不,R只允许你
`+`
或者`<-`
)和%…%
包围它们来定义新的中缀运算符。这些是我们必须遵守的规则。但是,在这些规则中,一切都是公平的。例如,我们可以为字符串重新定义`+`
以执行连接,而不会破坏其正常含义(添加):
`+`
# function (e1, e2) .Primitive("+")
这是旧的定义,我们希望为数字保留:
`+.default` = .Primitive('+')
`+.character` = paste0`1
`+` = function (e1, e2) UseMethod('+')
1 + 2
# [1] 3
'hello' + 'world'
# [1] "helloworld"
这利用了R中的S3类系统,使`+`
在其第一个参数的类型上完全通用。
因此可以重新定义的运算符列表非常折衷。首先,它包含以下运算符:
+
,-
,*
,/
,^
,&
,|
,:
,::
,:::
,$
,=
,<-
,<<-
,==
,<
,<=
,>
,>=
,!=
,~
,&&
,||
,!
,?
,@
,:=
,(
,{
,[
,[[
在此列表中,一个特定的运算符值得注意:`:=`
是一个可覆盖的运算符(例如{data.table}使用它)但与此列表中的大多数其他运算符不同,它没有默认实现。
同样,您可以定义几乎所有运算符的赋值版本,而不仅仅是具有预定义赋值的赋值版本(例如`[<-`
)。例如,`(<-`
和`{<-`
也缺少默认实现。这就是以下代码失败的原因:
a = 1
(a) = 2
# Error in (a) = 2 : could not find function "(<-"
{a} = 3
# Error in { : could not find function "{<-"
但是可以通过定义运算符使代码工作:
`(<-` = `{<-` = function (x, value) value
每个函数都有相同的功能,包括几乎所有的运算符1,甚至是控制结构(参见本答案下面的注释)。
相比之下,`**`
不是真正的算子:它是`^`
的语法别名。用户可以定义自己的`**`
函数,但不能通过代码a ** b
调用它,因此它不是一个可覆盖的运算符(除了通过重写`^`
)。
同样,您可以通过重新定义->
来覆盖<-
(仅限)。这似乎很少有意义 - 但至少有一个很好的例子,定义较少冗长的lambda:
> sapply(1 : 4, x -> 2 * x)
[1] 2 4 6 8
1唯一的例外是赋值运算符:由于语言的a <- b <- c
,你不能通过重新定义`<-<-`
(和`=<-`
,`<<-`
和`:=`
相同)来改变operator precedence and associativity rules的含义。但是,以下代码调用`(<-`
和`<-<-`
,如果未定义其中任何一个,则会失败:
(a <- b) <- c
弄乱。
您可以执行此类操作,但您可能希望将这些对象分配给新环境以确保安全。
> "^" <- function(x, y) `-`(x, y) ## subtract y from x
> 5 ^ 3
# [1] 2
> "?" <- function(x, y) sum(x, y) ## add x and y
> 5 ? 5
# [1] 10