Enum-like arguments in R

前端 未结 5 1502
我在风中等你
我在风中等你 2021-01-04 04:47

I\'m new to R and I\'m currently trying to supply the enumeration-like argument to the R function (or the RC/R6 class method), I currently use character vector plus ma

5条回答
  •  醉酒成梦
    2021-01-04 05:11

    How about using a function that defines the enum by returning list(a= "a", ...)? You can then either assign the returned vector to a variable and use it in context, or use the function directly. Either a name or an integer reference will work as an index, although you have to use the unlist version of the index lookup, [[, otherwise you get a list with one element.

    colorEnum <- function() {
        list(BLUE = "BLUE", RED = "RED", BLACK = "BLACK")
    }
    
    colorEnum()$BLUE
    #> [1] "BLUE"
    colorEnum()[[1]]
    #> [1] "BLUE"
    colorEnum()[1]
    #> $BLUE
    #> [1] "BLUE"
    
    col <- colorEnum()
    col$BLUE
    #> [1] "BLUE"
    col[[1]]
    #> [1] "BLUE"
    col$BAD_COLOR
    #> NULL
    col[[5]]
    #> Error in col[[5]] : subscript out of bounds
    

    You can get the list of names for use in a match, i.e. your function parameter could be

    EnumTest = function( enum = names(colorEnum()) { ...
    

    You can actually abbreviate too, but it must be unique. (If you use RStudio, since col is a list, it will suggest completions!)

    col$BLA
    #> [1] "BLACK"
    col$BL
    #> NULL
    

    If you want more sophisticated enum handling, you could assign S3 classes to the thing returned by your enum constructor function and write a small collection of functions to dispatch on class "enum" and allow case-insensitive indexing. You could also add special functions to work with a specific class, e.g. "colorEnum"; I have not done that here. Inheritance means the list access methods all still work.

    colorEnum2 <- function() {
        structure(
            list(BLUE = "BLUE", RED = "RED", BLACK = "BLACK"),
            class= c("colorEnum2", "enum", "list")
        )
    }
    
    # Note, changed example to allow multiple returned values.
    `[.enum` <- function(x, i) {
        if ( is.character( i ))
            i <- toupper(i)
        class(x) <- "list"
        names(as.list(x)[i])
    }
    
    `[[.enum` <- function(x, i, exact= FALSE) {
        if ( is.character( i ))
            i <- toupper(i)
        class(x) <- "list"
        as.list(x)[[i, exact=exact]]
    }
    
    `$.enum` <- function(x, name) {
        x[[name]]
    }
    
    col <- colorEnum2()
    # All these return [1] "RED"
    col$red
    col$r
    col[["red"]]
    col[["r"]]
    col["red"]
    
    col[c("red", "BLUE")]
    #> [1] "RED" "BLUE"
    
    col["r"]
    [1] NA   # R does not matches partial strings with "["
    

    These override the built in [, [[ and $ functions when the thing being indexed is of class "enum", for any "enum" classed objects. If you need another one, you just need to define it.

     directionEnum <- function() {
        structure(
            list(LEFT = "LEFT", RIGHT = "RIGHT"),
            class= c("directionEnum", "enum", "list")
        )
    }
    
    directionEnum()$l
    #> [1] "LEFT"
    

    If you need several enum objects, you could add a factory function enum that takes a vector of strings and a name and returns an enum object. Most of this is just validation.

    enum <- function(enums, name= NULL) {
        if (length(enums) < 1)
            stop ("Enums may not be empty." )
        enums <- toupper(as.character(enums))
        uniqueEnums <- unique(enums)
        if ( ! identical( enums, uniqueEnums ))
            stop ("Enums must be unique (ignoring case)." )
        validNames <- make.names(enums)
        if ( ! identical( enums, validNames ))
           stop( "Enums must be valid R identifiers." )
    
        enumClass <- c(name, "enum", "list")
        obj <- as.list(enums)
        names(obj) <- enums
        structure( obj, class= enumClass)
    }
    
    col <- enum(c("BLUE", "red", "Black"), name = "TheColors")
    col$R
    #> [1] "RED"
    class(col)
    #> [1] "TheColors" "enum"      "list"
    
    side <- enum(c("left", "right"))
    side$L
    #> [1] "LEFT"
    class(side)
    #> [1] "enum" "list"
    

    But now this is starting to look like a package...

提交回复
热议问题