2014-09-25 5 views
9

Solo per divertimento Sto cercando di sovraccaricare il - in R in modo che l'esecuzioneR: Come sovraccaricare l'operatore `-` se vuoi usare` -` nel tuo codice 'overload'?

some.string - n 

mi darà la some.string con gli ultimi n caratteri rimossi.

Questo è il mio codice in R

`-` <- function(x,y) { 
    minus <- force(`-`) 
    if(typeof(x) == "character" & y >0) { 
    return(substr(x,1,minus(nchar(x), as.integer(y)))) 
    } else { 
    return(minus(x,y)) 
    } 
} 

"abc" - 2 

Tuttavia questo sembra causare un una ricorsione infinitamente nidificato. Questo perché ogni volta meno viene eseguito tenta di eseguire se stesso. Ma questo è esattamente quello che sto cercando di prevenire assegnando meno = - in primo luogo?

Come evitare questa ricorsione senza fine?

+0

Certo che cosa eravate dopo, ma è possibile ottenere lo stesso, sostituendo il vostro due 'meno (a, b)' con qualcosa come 'sum (c (a, sign (log (0)) * b))'. – jbaums

risposta

12

È possibile utilizzare .Primitive("-") per evitare un ciclo infinito. Questo dovrebbe funzionare:

`-` <- function(x,y) { 
    if(typeof(x) == "character" & y >0) { 
    return(substr(x, 1, nchar(x) - as.integer(y))) 
    } else { 
    .Primitive("-")(x, y) 
    } 
} 

Più vicino alla tua idea originale è di definire minus <- get("-", envir=baseenv()):

`-` <- function(x,y) { 
    minus <- get("-", envir=baseenv()) 
    if(typeof(x) == "character" & y >0) { 
    return(substr(x,1,minus(nchar(x), as.integer(y)))) 
    } else { 
    return(minus(x,y)) 
    } 
} 
2

ombre risposta è del tutto corretto e vi ha chiesto "solo per divertimento", quindi questo va bene. Ma per qualsiasi programma serio sconsiglio vivamente da questi approcci. Il sovraccarico di - in questo modo rallenta ogni operazione meno nel tuo programma e questo tipo di hack può causare molti altri problemi problematici.

Il modo semplice per eseguire tali overload è introdurre la propria classe wrapper, in cui è possibile salvare gli operatori. Per esempio:

specialstring <- 
    setRefClass("specialstring", 
       fields = list(str = "character"), 
       methods = list(
       initialize = function(str = character(0)) { 
       .self$str <- str 
       }, 
       show = function() { print(.self$str) } 
       ) 
) 

`-.specialstring` <- function(x, y) { 
    if (class(x) == "specialstring" && is.numeric(y)) 
    return(substr(x$str, 1, nchar(x$str) - as.integer(y))) 
    else 
    stop("Incompatible types for -.specialstring") 
} 

Ora specialstring è una classe wrapper stringa che ha la funzionalità desiderata:

x <- specialstring("abc") 
x - 1 

viene eseguita correttamente, mentre x-x conduce al messaggio di errore previsto.

Se si combina un tale overloading degli operatori (che è una delle idee di programmazione orientata agli oggetti in R, pacchetti come igraph davvero fanno uso di queste idee!) Con la funzione di ombra, poi x-x non porta al messaggio di errore previsto in precedenza (Incompatible types for -.specialstring), ma porta al messaggio fuorviante Error in y > 0 : comparison (6) is possible.... Ovviamente, questo potrebbe essere risolto nuovamente con un numero ancora maggiore di controlli nell'overloading -, ma tutti questi controlli sono una specie di "cattiva progettazione", in cui una soluzione OOP accurata è il modo in cui si dovrebbe fare.

3

Ecco un'altra soluzione che utilizza l'invio del metodo S4. La spedizione sarà più elegante rispetto all'utilizzo delle strutture if-else e non è necessario mascherare la funzione primitiva -. Tuttavia, per le funzioni primitive sembra che non sia consentito definire metodi per tipi di dati atomici. Quindi, avevo bisogno di definire una nuova classe "lastCharacters". In realtà questo rende l'ultima riga un po 'più readible come l'intenzione del codice è chiaro ...

lastCharacters <- setClass("lastCharacters", contains = "numeric") 

setMethod("-", 
      signature(e1 = "character", e2 = "lastCharacters"), 
      function(e1, e2) { 
      substr(e1, 1, nchar(e1) - as.integer(e2)) 
      }) 

"abc" - lastCharacters(2) 
2

Lungo le linee di suggestione da @PatrickRoocks, rendere il proprio (S3 o S4) di classe e spedizione (piuttosto di maschera) su quello.

`-.my` = function(e1, e2) substr(e1, 1, nchar(e1) - e2) 

x = c('all', 'the', 'world') 
class(x) = 'my' 
x - 1 
## [1] "al" "th" "worl" 
## attr(,"class") 
## [1] "my" 

Vedi anche? Operazioni nella base (per S3) o metodi (S4) pacchetto, che definisce un 'gruppo generico'

Ops.my = function(e1, e2) { 
    FUN = get(.Generic, envir = parent.frame(), mode = "function") 
    substr(e1, 1, FUN(nchar(e1), e2)) 
} 

per implementare (in modo assurdo, per lo specifico funzione sopra - substr(x, 1, nchar(x) + 2) non ha senso), x - 1, x + 1, x/2, ecc. in una singola definizione. Versione

La S4 (un po 'come @Sebastian, e da here) è

.My = setClass("My", contains="character") 
setMethod("Arith", c("My", "numeric"), function(e1, e2) { 
    substr(e1, 1, callGeneric(nchar(e1), e2)) 
}) 

con

> .My(c("all", "the", "world")) - 1 
An object of class "My" 
[1] "al" "th" "worl" 
non