首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >R中的类枚举参数

R中的类枚举参数
EN

Stack Overflow用户
提问于 2015-11-21 08:45:44
回答 5查看 10.4K关注 0票数 15

我是R的新手,目前我正在尝试为R函数(或RC/R6类方法)提供类似枚举的参数,我目前使用的是字符向量加上match.arg,如下所示:

代码语言:javascript
运行
复制
EnumTest = function(enum = c("BLUE", "RED", "BLACK")) {
  enumArg <-
    switch(
      match.arg(enum), "BLUE" = 0L, "RED" = 1L, "BLACK" = 2L
    )
  switch(enumArg,
         # do something
  )
}

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

如果没有其他更好的方法,有一件事可以改进上面的方法-通过全局预定义枚举或作为R6类的私有成员来使其更简洁:

代码语言:javascript
运行
复制
Color <- c("BLUE", "RED", "BLACK")

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

代码语言:javascript
运行
复制
EnumTest = function(enum = Color) { 
...

但是,我不确定如何在match.arg函数中使用这个Color向量。如果我可以将Color定义为一个映射,键是实际的颜色值,值是整数表示,那就太好了--但我不确定这是否合理。无论如何,也许还有更常见的整洁方法。

主要目标是为我的包和函数的用户提供一个易于使用的直观界面(例如,查找可能值集的简单方法、制表符完成、自动建议等),然后使用类似枚举的参数对这些函数进行标准化开发

EN

回答 5

Stack Overflow用户

发布于 2016-12-01 05:06:38

使用一个通过返回list(a= "a", ...)来定义枚举的函数怎么样?然后,您可以将返回的向量赋给一个变量并在上下文中使用它,或者直接使用该函数。名称或整数引用都可以用作索引,尽管您必须使用索引查找的非列表版本[[,否则会得到一个只有一个元素的列表。

代码语言:javascript
运行
复制
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

您可以获取在匹配中使用的名称列表,例如,您的函数参数可以是

代码语言:javascript
运行
复制
EnumTest = function( enum = names(colorEnum()) { ...

实际上你也可以缩写,但它必须是唯一的。(如果您使用RStudio,因为col是一个列表,它将建议完成!)

代码语言:javascript
运行
复制
col$BLA
#> [1] "BLACK"
col$BL
#> NULL

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

代码语言:javascript
运行
复制
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 "["

当被索引的对象属于“枚举”类时,这些函数会覆盖任何“枚举”类对象的内置[[[$函数。如果你需要另一个,你只需要定义它。

代码语言:javascript
运行
复制
 directionEnum <- function() {
    structure(
        list(LEFT = "LEFT", RIGHT = "RIGHT"),
        class= c("directionEnum", "enum", "list")
    )
}

directionEnum()$l
#> [1] "LEFT"

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

代码语言:javascript
运行
复制
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"

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

票数 11
EN

Stack Overflow用户

发布于 2017-05-24 15:58:01

我喜欢使用环境作为枚举的替代,因为您可以锁定它们,以防止在创建后进行任何更改。我这样定义我的创建函数:

代码语言:javascript
运行
复制
Enum <- function(...) {

  ## EDIT: use solution provided in comments to capture the arguments
  values <- sapply(match.call(expand.dots = TRUE)[-1L], deparse)

  stopifnot(identical(unique(values), values))

  res <- setNames(seq_along(values), values)
  res <- as.environment(as.list(res))
  lockEnvironment(res, bindings = TRUE)
  res
}

创建一个新枚举,如下所示:

代码语言:javascript
运行
复制
FRUITS <- Enum(APPLE, BANANA, MELON)

我们可以访问这些值:

代码语言:javascript
运行
复制
FRUITS$APPLE

但我们不能修改它们或创建新的:

代码语言:javascript
运行
复制
FRUITS$APPLE <- 99  # gives error
FRUITS$NEW <- 88  # gives error
票数 10
EN

Stack Overflow用户

发布于 2016-11-02 20:48:15

我刚刚面对了这个问题,只能找到这个问题。Paul提到的objectProperties包似乎被抛弃了(它产生了几个警告),对于这样一个简单的(原则上)问题,它有很多开销。我想出了以下轻量级解决方案(仅依赖于stringi包),它在C语言中重现了枚举的感觉。也许这对某些人有帮助。

代码语言:javascript
运行
复制
EnumTest <- function(colorEnum = ColorEnum$BLUE) {
  enumArg <- as.character(match.call()[2])
  match.arg(enumArg, stringi::stri_c("ColorEnum$", names(ColorEnum)))
  sprintf("%s: %i",enumArg,colorEnum)
}

ColorEnum <- list(BLUE = 0L, RED = 1L, BLACK = 2L)
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33838392

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档