类似于R的枚举式论证 [英] Enum-like arguments in R

查看:123
本文介绍了类似于R的枚举式论证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是R的新手,目前我正在尝试向R函数(或RC / R6类方法)提供类似枚举的参数,我目前使用字符向量加$ code匹配。 arg 类似于以下内容:

  EnumTest = function(enum = c(BLUE RED,BLACK)){
enumArg < -
switch(
match.arg(enum),BLUE= 0L,RED= 1L,BLACK= 2L

switch(enumArg,
#do something

}

是否有更好/更简洁的方式来模仿R中的类似枚举的行为?例如。一个大的问题,用户必须知道参数的一组可能的值,并手动键入它作为一个字符串 - 没有任何建议或自动完成...



如果没有其他更好的方法,有一件事情可以改进上述方法 - 通过在全球范围内预先定义枚举或者说R6类的私人成员来说,这样做可以更简洁:

 颜色<  -  c(BLUE,RED,BLACK)

然后可以(重新)在一个或多个函数定义中使用它,例如:

  EnumTest = function(enum = Color){
...

m不知道如何在 match.arg 函数中使用这个颜色矢量。如果我可以将颜色定义为一个地图,那么实际的颜色值和值就是整数表示,那么这是很好的 - 但我不知道这是多么明智。无论如何,也许存在更常见的整齐方法。



主要目标是为我的包和功能的用户提供一个易于使用的直观界面(例如,轻松找到可能的值集,tab-completion,自动建议等),然后使用枚举类的参数来定义这些函数的开发

解决方案

如何使用定义枚举的函数返回 list(a =a,...)?然后,您可以将返回的向量分配给变量,并在上下文中使用它,或直接使用该函数。名称或整数引用将作为索引使用,尽管您必须使用索引查找的未列出版本 [[,否则您将获得一个包含一个元素的列表。

  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]]
#> col [[5]]中的错误:下标超出限制

您可以获取名称列表在一个匹配中使用,即你的函数参数可以是

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

你也可以缩写,但它必须是唯一的(如果使用RStudio, code> col 是一个列表,它会建议完成!)

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

如果你想要更复杂的枚举处理,你可以将S3类分配给你的枚举构造函数返回的东西,并在类enum上写一个小的函数集,并允许不区分大小写的索引。您还可以添加特殊功能来处理特定的类,例如colorEnum;我没有这样做。继承意味着列表访问方法仍然可以工作。

  colorEnum2<  -  functio n(){
结构(
list(BLUE =BLUE,RED =RED,BLACK =BLACK),
class = c(colorEnum2 ,list)

}

#注意,更改示例允许多个返回值。
`[.enum`< - function(x,i){
if(is.character(i))
i< - toupper(i)
class )< - list
names(as.list(x)[i])
}

`[[.enum`< - function(x,i ,exact = FALSE){
if(is.character(i))
i < - toupper(i)
class(x)< - list
as。列表(x)[[i,exact = exact]]
}

`$ .enum`< - function(x,name){
x [[name]
}

col< - colorEnum2()
#所有这些返回[1]RED
col $ red
col $ r
col [[red]]
col [[r]]
col [red]

col [c(red,BLUE )]
#> [1]REDBLUE

col [r]
[1] NA#R不匹配部分字符串与[

这些覆盖内置的 [ [[ / code>和 $ 函数,当索引的东西是类枚举,对于任何枚举类对象。如果你需要另外一个,你只需要定义它。

  directionEnum<  -  function(){
结构(
列表(LEFT =LEFT RIGHT =RIGHT),
class = c(directionEnum,enum,list)

}

directionEnum()$ l
#> [1]LEFT

如果您需要几个枚举对象,可以添加工厂函数枚举,它需要一个字符串和名称的向量,并返回一个枚举对象。大部分只是验证。

 枚举<  -  function(enums,name = NULL){
if长度(枚举)< 1)
stop(枚举可能不为空)
枚举< - toupper(as.character(enums))
uniqueEnums< - unique枚举)
如果(!same(enums,uniqueEnums))
stop(枚举必须是唯一的(忽略大小写))
validNames< - make.names(enums)
if(!identical(enums,validNames))
stop(枚举必须是有效的R标识符)

enumClass< - c(name,枚举,列表)
obj< - as.list(枚举)
名称(obj)< - 枚举
结构(obj,class = enumClass)
}

col< - enum(c(BLUE,red,Black),name =TheColors)
col $ R
#> [1]RED
class(col)
#> [1]TheColorsenumlist

side< - enum(c(left,right))
side $ L
#> ; [1]LEFT
class(s​​ide)
#> [1]枚举列表

但是现在开始看起来像一个包.. 。


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 match.arg similar to the following:

EnumTest = function(enum = c("BLUE", "RED", "BLACK")) {
  enumArg <-
    switch(
      match.arg(enum), "BLUE" = 0L, "RED" = 1L, "BLACK" = 2L
    )
  switch(enumArg,
         # do something
  )
}

Is there are better/more concise way to imitate enum-like behavior in R? E.g. one big problem that user has to know the set of possible values for the argument and manually type it as a string - without any suggestion or auto-completion...

If there is no other better way, one thing that could improve above approach - it'd be nice to make it more concise by say predefining enums globally or say as private members of R6 class:

Color <- c("BLUE", "RED", "BLACK")

Then one could (re)use it in one or more function definitions, e.g.:

EnumTest = function(enum = Color) { 
...

However, I'm not sure how to use this Color vector in match.arg function. It'd be nice if I could define Color as a map with keys being actual color values and values being integer representation - but I'm not sure how sensible that is.. Anyways, maybe there are more common neat approaches exist.

The main goal would be to provide an easy-to-use intuitive interface to the user of my package and functions (e.g. easy way to find the set of possible values, tab-completion, auto-suggestion, etc..), followed by standardized development of such functions using enum-like arguments

解决方案

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...

这篇关于类似于R的枚举式论证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆