代码之家  ›  专栏  ›  技术社区  ›  user63230

向函数传递向量或未定义数量的参数

  •  1
  • user63230  · 技术社区  · 4 年前

    我希望能够通过 ... 而且能够通过 vector 。这里有一个愚蠢的例子:

    library(tidyverse)
    df <- data.frame(gear = as.character(unique(mtcars$gear)),
                     id = 1:3)
    myfun <- function(...) {
      ids_lst <- lst(...)
      df2 <- bind_rows(map(ids_lst, function(x) 
        mtcars %>% 
          filter(gear == x) %>% 
          select(mpg)), .id = "gear") %>% 
        left_join(df)
      df2
    }
    #these all work:
    myfun(3)
    myfun(3, 4)
    myfun(3, 4, 5)
    

    不过,将矢量传递给它是不起作用的:

    myvector <- unique(mtcars$gear)
    myfun(myvector)
    

    问题在于函数收集参数的方式以及返回参数的方式:

    myfun_lst <- function(...) {
      ids_lst <- lst(...)
      ids_lst
    }
    myfun_lst(3, 4, 5)
    # $`3`
    # [1] 3
    
    # $`4`
    # [1] 4
    
    # $`5`
    # [1] 5
    
    myfun_lst(myvector)
    # $myvector
    # [1] 4 3 5
    

    我想一个解决方案是测试输入是否是 矢量 ,类似于:

    myfun_final <- function(...) {
      if(is.vector(...) & !is.list(...)) {
        ids_lst <- as.list(...)
        names(ids_lst) <- (...)
      } else { 
        ids_lst <- lst(...)
      }
      df2 <- bind_rows(map(ids_lst, function(x) 
        mtcars %>% 
          filter(gear == x) %>% 
          select(mpg)), .id = "gear") %>% 
        left_join(df)
      df2
    }
    

    现在,通过向量传递函数是可行的,但收集参数则不行:

    myfun_final(3, 4, 5)
    myfun_final(myvector)
    

    解决这个问题的好方法是什么? 谢谢

    0 回复  |  直到 4 年前
        1
  •  4
  •   TimTeaFan    4 年前

    当然,您可以更改您的函数,使其同时使用这两个正则参数 myfun(3, 4, 5) 和一个矢量 myfun(myvector) ,如上面的答案所示。

    另一种选择是通过使用bang-bang-bang运算符来取消参数拼接 !!! 。此运算符仅在某些{rlang}和{tidyverse}函数中受支持。在你的例子中,你评估了点 ... 在…内 purrr::map 这支持论点拼接。因此,可能不需要重写您的函数:

    library(tidyverse)
    
    # your original function:
    myfun <- function(...) {
            ids_lst <- lst(...)
            df2 <- bind_rows(map(ids_lst, function(x) 
                    mtcars %>% 
                            filter(gear == x) %>% 
                            select(mpg)), .id = "gear") %>% 
                    left_join(df)
            df2
    }
    
    myvector <- unique(mtcars$gear)
    
    myfun(!!! myvector) # works
    
    #> Joining, by = "gear"
    #>    gear  mpg id
    #> 1     4 21.0  1
    #> 2     4 21.0  1
    #> 3     4 22.8  1
    #> 4     4 24.4  1
    #> 5     4 22.8  1
    #> 6     4 19.2  1
    #> 7     4 17.8  1
    #> 8     4 32.4  1
    #> 9     4 30.4  1
    #> 10    4 33.9  1
    #> ...
    
    
    myfun(3, 4, 5) # works
    
    #> Joining, by = "gear"
    #>    gear  mpg id
    #> 1     3 21.4  2
    #> 2     3 18.7  2
    #> 3     3 18.1  2
    #> 4     3 14.3  2
    #> 5     3 16.4  2
    #> 6     3 17.3  2
    #> 7     3 15.2  2
    #> 8     3 10.4  2
    #> 9     3 10.4  2
    #> 10    3 14.7  2
    #> ...
    

    创建于2021-12-30由 reprex package (v0.3.0)

    您可以阅读更多关于使用bang-bang-bang运算符取消查询的信息 here

    最后,您应该考虑功能的用户。如果你是唯一的用户,那么选择适合你的。如果有其他用户,您应该考虑他们期望该功能如何工作。用户可能不希望函数同时使用多个参数,或者在向量中提供这些参数。在琐碎的争论中 !!! 是一个公认的概念。在基数R中,我们通常使用 do.call("myfun", as.list(myvector)) 以实现类似的目标。


    要添加另一个选项,请执行以下操作:

    purrr包装有一个系列 lift 函数,可用于更改函数的参数类型。最突出的是 lift_dl 它将以点为自变量的函数转换为以列表或向量为自变量的功能。这也可以用于解决问题,而无需重写函数:

    lift_dl(myfun)(myvector)
    
    #> Joining, by = "gear"
    #>    gear  mpg id
    #> 1     4 21.0  1
    #> 2     4 21.0  1
    #> 3     4 22.8  1
    #> 4     4 24.4  1
    #> 5     4 22.8  1
    #> 6     4 19.2  1
    #> 7     4 17.8  1
    #> 8     4 32.4  1
    #> 9     4 30.4  1
    #> 10    4 33.9  1
    #> ...
    

    创建于2022-01-01由 reprex包 (v0.3.0)

        2
  •  3
  •   ekoam    4 年前

    如果 ... 长度为1,如果唯一通过的参数是向量?如果不是,则考虑 。。。 缩放器列表,并用捕获它们 lst(...)

    myfun_final <- function(...) {
      if (...length() == 1L && is.vector(..1))
        ids_lst <- `names<-`(..1, ..1)
      else
        ids_lst <- lst(...)
      
      df2 <- bind_rows(map(ids_lst, function(x) 
        mtcars %>% 
          filter(gear == x) %>% 
          select(mpg)), .id = "gear") %>% 
        left_join(df)
      df2
    }
    

    测验

    > myfun_final(3)
    Joining, by = "gear"
       gear  mpg id
    1     3 21.4  2
    2     3 18.7  2
    3     3 18.1  2
    4     3 14.3  2
    5     3 16.4  2
    6     3 17.3  2
    7     3 15.2  2
    8     3 10.4  2
    9     3 10.4  2
    10    3 14.7  2
    11    3 21.5  2
    12    3 15.5  2
    13    3 15.2  2
    14    3 13.3  2
    15    3 19.2  2
    > myfun_final(3,4,5)
    Joining, by = "gear"
       gear  mpg id
    1     3 21.4  2
    2     3 18.7  2
    3     3 18.1  2
    4     3 14.3  2
    5     3 16.4  2
    6     3 17.3  2
    7     3 15.2  2
    8     3 10.4  2
    9     3 10.4  2
    10    3 14.7  2
    11    3 21.5  2
    12    3 15.5  2
    13    3 15.2  2
    14    3 13.3  2
    15    3 19.2  2
    16    4 21.0  1
    17    4 21.0  1
    18    4 22.8  1
    19    4 24.4  1
    20    4 22.8  1
    21    4 19.2  1
    22    4 17.8  1
    23    4 32.4  1
    24    4 30.4  1
    25    4 33.9  1
    26    4 27.3  1
    27    4 21.4  1
    28    5 26.0  3
    29    5 30.4  3
    30    5 15.8  3
    31    5 19.7  3
    32    5 15.0  3
    > myfun_final(c(3,4,5))
    Joining, by = "gear"
       gear  mpg id
    1     3 21.4  2
    2     3 18.7  2
    3     3 18.1  2
    4     3 14.3  2
    5     3 16.4  2
    6     3 17.3  2
    7     3 15.2  2
    8     3 10.4  2
    9     3 10.4  2
    10    3 14.7  2
    11    3 21.5  2
    12    3 15.5  2
    13    3 15.2  2
    14    3 13.3  2
    15    3 19.2  2
    16    4 21.0  1
    17    4 21.0  1
    18    4 22.8  1
    19    4 24.4  1
    20    4 22.8  1
    21    4 19.2  1
    22    4 17.8  1
    23    4 32.4  1
    24    4 30.4  1
    25    4 33.9  1
    26    4 27.3  1
    27    4 21.4  1
    28    5 26.0  3
    29    5 30.4  3
    30    5 15.8  3
    31    5 19.7  3
    32    5 15.0  3
    
    推荐文章