在R语言中创建一个字符串连接运算符

47
我在想如何在R中编写字符串连接运算符,类似于SAS中的||,Java/C#中的+或Visual Basic中的&。最简单的方法是使用%,创建一个特殊的运算符,例如:
`%+%` <- function(a, b) paste(a, b, sep="")

但这会导致代码中出现很多丑陋的%符号。

我注意到+在Ops组中已经定义了,你可以为该组编写S4方法,因此也许这是解决问题的方法。但是,我完全没有使用S4语言特性的经验。如何修改上述函数以使用S4?


1
可能重复:https://dev59.com/vnM_5IYBdhLWcg3wlEFH - Eduardo Leoni
3
https://stat.ethz.ch/pipermail/r-help/2005-February/066719.html - mdsumner
1
注意:\%+%` = paste0`与OP的实现方式相同。 - Jet
5个回答

47

正如其他人所提到的,您无法覆盖密封的S4方法“+”。但是,您不需要定义一个新类来定义字符串的加法函数;这并不理想,因为它强制您转换字符串的类别,从而导致更丑陋的代码。相反,可以简单地重写“+”函数:

"+" = function(x,y) {
    if(is.character(x) || is.character(y)) {
        return(paste(x , y, sep=""))
    } else {
        .Primitive("+")(x,y)
    }
}

接下来的所有内容都应该按预期工作:

1 + 4
1:10 + 4 
"Help" + "Me"

这个解决方案感觉有点像 hack,因为你不再使用正式的方法,但这是实现你想要的确切行为的唯一方法。


3
我不太了解 S3/4 - 这个方案有哪些 hacky 的地方?似乎很好用。 - eddi
1
这是一篇旧帖子,但我有预感很多人仍然会看它。我想提出一个建议,以使此函数的使用更受限制。将if语句中的逻辑运算符更改为&。如果您只确定其中一个对象是字符串,我想不出为什么要连接两个对象。与原始+相比,产生的错误消息不会像那样直观。 - Josh Bradley
1
@JoshBradley:我撤销了你的编辑,因为如果连接不同的对象类型,就不会出现错误。非字符参数被提升为字符,这与许多其他R函数一致。R不是强类型语言,Primitive("+")允许混合类型(例如TRUE+1L1L+1.0)。我建议你添加自己的答案,而不是更改已接受的答案。 - Joshua Ulrich
1
这个答案会破坏其他定义了它们自己的加法运算符方法的包,最显著的是ggplot2。我建议不要使用它。 - OganM
6
对我来说,它不会破坏 ggplot2。ggplot2 将它的 "+" 定义为类 "gg" 的 S3 方法。请参阅 ?ggplot2::%+%``。 - CoderGuy123
显示剩余3条评论

29

我会尝试这个(相对更干净的S3解决方案)

`+` <- function (e1, e2) UseMethod("+")
`+.default` <- function (e1, e2) .Primitive("+")(e1, e2)
`+.character` <- function(e1, e2) 
    if(length(e1) == length(e2)) {
           paste(e1, e2, sep = '')
    } else stop('String Vectors of Different Lengths')

上面的代码将会把+替换成通用符号,并将+.default设置为原来的+,然后给+添加一个新的方法+.character


抱歉问一下,为什么要求这两个字符串具有相同的长度? - Hilton Fernandes
1
测试并不是检查字符串是否具有相同的长度,而是检查向量是否具有相同的长度。例如,"baz" + "foobar" 是可以的,但 "baz" + c("foo", "bar") 不行。 - alan ocallaghan

25

您也可以使用S3类来实现此功能:

String <- function(x) {
  class(x) <- c("String", class(x))
  x
}

"+.String" <- function(x,...) {
  x <- paste(x, paste(..., sep="", collapse=""), sep="", collapse="")
  String(x)
}


print.String <- function(x, ...) cat(x)

x <- "The quick brown "
y <- "fox jumped over "
z <- "the lazy dog"

String(x) + y + z

13

如果 R 完全遵守 S4,以下内容就足够了:

setMethod("+",
          signature(e1 = "character", e2 = "character"),
          function (e1, e2) {
              paste(e1, e2, sep = "")
      })

但是这会产生一个错误,提示该方法被封闭 :((。希望在未来的R版本中会有所改变。

你能做的最好的事情是定义一个新类“string”,它的行为与“character”类完全相同:

setClass("string", contains="character")
string <- function(obj) new("string", as.character(obj))

并定义R允许的最通用方法:

setMethod("+", signature(e1 = "character", e2 = "ANY"),
          function (e1, e2) string(paste(e1, as.character(e2), sep = "")))

现在尝试:

tt <- string(44444)

tt
#An object of class "string"
#[1] "44444"
tt + 3434
#[1] "444443434"
"sfds" + tt
#[1] "sfds44444"
tt +  tt
#[1] "4444444444"
343 + tt
#Error in 343 + tt : non-numeric argument to binary operator
"sdfs" + tt + "dfsd"
#An object of class "string"
#[1] "sdfs44444dfsd"

9

你已经给出了正确的答案——在R中,所有东西都是函数,你不能定义新的运算符。所以%+%就是最好的选择。


3
但是您可以重新定义现有运算符的行为。不过在这种情况下不能重新定义"+"方法,因为它已被封闭为签名c(“character”,“character”)。 - VitoshKa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接