2011-01-19 10 views
36

Me preguntaba cómo se podría escribir un operador de concatenación de cadenas en R, algo así como || en SAS, + en Java/C# o & en Visual Basic.Hacer un operador de concatenación de cadenas en R

La forma más sencilla sería la creación de un operador especial usar%, como

`%+%` <- function(a, b) paste(a, b, sep="") 

pero esto conduce a un montón de feas % 's en el código.

Me di cuenta de que + se define en el grupo Operaciones, y puede escribir métodos S4 para ese grupo, por lo que quizás algo así sería el camino a seguir. Sin embargo, no tengo ninguna experiencia con las características del lenguaje S4 en absoluto. ¿Cómo modificaría la función anterior para usar S4?

+1

posible duplicado: http://stackoverflow.com/questions/1319698/why-doesnt-operate-on-characters-in-r –

+2

https: //stat.ethz.ch/pipermail/r-help/2005-February/066719.html – mdsumner

Respuesta

36

Como otros han mencionado, no se puede anular el método S4 sellado "+". Sin embargo, no necesita definir una nueva clase para definir una función de adición para cadenas; esto no es ideal ya que te obliga a convertir la clase de cadenas y por lo tanto a un código más feo. En su lugar, simplemente se puede sobrescribir la función "+":

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

A continuación, el siguiente debería funcionar como se esperaba:

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

Esta solución se siente un poco como un truco, puesto que ya no utilizar está métodos formales, pero es la única forma de obtener el comportamiento exacto que deseaba.

+2

No estoy tan familiarizado con S3/4, ¿qué tiene de malo esto? Parece que funciona bastante bien. – eddi

+1

Esta es una publicación anterior, pero tengo la sensación de que muchas personas aún la miran. Haría una sugerencia para hacer que el uso de esta función sea más restringido. Cambie el operador lógico en la instrucción if a &. No puedo pensar en una buena razón por la que quieras usar dos objetos si solo estás seguro de que uno de ellos es una cadena. El mensaje de error resultante no sería tan intuitivo como el mensaje de error que obtendría de la primitiva +. –

+1

@JoshBradley: he revertido su edición porque no había ningún error si se concatenan diferentes tipos de objetos. El argumento sin carácter se promueve a carácter, que es coherente con muchas otras funciones R. R no es un lenguaje fuertemente tipado y 'Primitive (" + ")' permite tipos mixtos (por ejemplo, 'TRUE + 1L',' 1L + 1.0'). Sugiero que agregue su propia respuesta, en lugar de cambiar la respuesta aceptada. –

8

Te has dado la respuesta correcta: todo en R es una función, y no puedes definir nuevos operadores. Entonces, %+% es lo mejor posible.

+2

Pero puede redefinir el comportamiento de los operadores existentes. No en este caso, porque los métodos "+" están sellados para la firma c ("personaje", "personaje"). – VitoshKa

20

También puede utilizar las clases S3 para esto:

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 
11

Si R sería thoroghlly cumplir con S4, el siguiente habría sido suficiente:

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

Pero esto da un error que el método está sellado: ((. Esperemos que esto cambie en las versiones de características de R.

Lo mejor que puede hacer es definir una nueva clase "cadena" que se comportaría exactamente como clase de "carácter":

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

y definir el método más general que R permite:

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

ahora intenta:

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" 
+0

Sin necesidad de S4, el equivalente S3 funciona bien. –

16

Voy a tratar esto (solución relativamente más limpia 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') 

Código anterior cambiará + a un genérico, y establecer el +.default al original +, a continuación, añadir nuevo método +.character a +

Cuestiones relacionadas