programing

R 함수에서 선택적 인수를 지정하는 "올바른" 방법

padding 2023. 6. 22. 21:36
반응형

R 함수에서 선택적 인수를 지정하는 "올바른" 방법

저는 R에서 선택적 인수로 함수를 작성하는 "정확한" 방법이 무엇인지 관심이 있습니다.시간이 지나면서, 저는 여기서 다른 경로를 사용하는 몇 개의 코드를 우연히 발견했고, 이 주제에 대한 적절한 (공식적인) 입장을 찾을 수 없었습니다.

지금까지 저는 다음과 같은 선택적 인수를 작성했습니다.

fooBar <- function(x,y=NULL){
  if(!is.null(y)) x <- x+y
  return(x)
}
fooBar(3) # 3
fooBar(3,1.5) # 4.5

함수는 다음과 같은 경우 인수를 반환합니다.x공급됩니다.기본값을 사용합니다.NULL두 번째 인수에 대한 값 및 해당 인수가 아닐 경우NULL그런 다음 함수는 두 숫자를 추가합니다.

또는 함수를 다음과 같이 쓸 수 있습니다(여기서 두 번째 인수는 이름으로 지정해야 하지만 하나는 이름으로 지정할 수도 있습니다).unlist(z)또는 정의z <- sum(...)대신):

fooBar <- function(x,...){
  z <- list(...)
  if(!is.null(z$y)) x <- x+z$y
  return(x)
}
fooBar(3) # 3
fooBar(3,y=1.5) # 4.5

개인적으로 저는 첫 번째 버전을 선호합니다.하지만, 저는 둘 다 좋은 점과 나쁜 점을 볼 수 있습니다.첫 번째 버전은 오류 발생 가능성이 약간 낮지만 두 번째 버전은 임의의 수의 옵션을 통합하는 데 사용될 수 있습니다.

R에서 선택적 인수를 지정하는 "올바른" 방법이 있습니까?지금까지 저는 첫 번째 접근법을 결정했지만, 둘 다 가끔 약간 "해키"하다고 느낄 수 있습니다.

사용할 수도 있습니다.missing()주장의 여부를 시험해 보다y제공됨:

fooBar <- function(x,y){
    if(missing(y)) {
        x
    } else {
        x + y
    }
}

fooBar(3,1.5)
# [1] 4.5
fooBar(3)
# [1] 3

솔직히 저는 OP의 첫 번째 방법을 좋아합니다.NULL값을 매긴 다음 확인합니다.is.null(매우 간단하고 이해하기 쉽기 때문에 가능합니다.사람들이 코딩에 익숙한 방식에 따라 다를 수 있지만 해들리는 그것을 지지하는 것 같습니다.is.null방법도:

Hadley의 책 "Advanced-R" 6장, Functions, p.84 (여기서 온라인 버전 확인)에서:

인수가 missing() 함수와 함께 제공되었는지 여부를 확인할 수 있습니다.

i <- function(a, b) {
  c(missing(a), missing(b))
}
i()
#> [1] TRUE TRUE
i(a = 1)
#> [1] FALSE  TRUE
i(b = 2)
#> [1]  TRUE FALSE
i(1, 2)
#> [1] FALSE FALSE

계산하는 데 여러 줄의 코드가 필요할 수 있는 사소한 기본값을 추가하려는 경우가 있습니다.함수 정의에 코드를 삽입하는 대신 missing()을 사용하여 필요한 경우 조건부로 코드를 계산할 수 있습니다.그러나 문서를 주의 깊게 읽지 않으면 어떤 인수가 필요하고 어떤 인수가 선택적인지 알 수 없습니다. 대신, 저는 보통 기본값을 NULL로 설정하고 is.null()을 사용하여 인수가 제공되었는지 확인합니다.

제 경험칙은 다음과 같습니다.

다른 파라미터에서 기본값을 계산할 수 있는 경우 다음과 같이 기본 식을 사용합니다.

fun <- function(x,levels=levels(x)){
    blah blah blah
}

달리 missing을 사용하는 경우

fun <- function(x,levels){
    if(missing(levels)){
        [calculate levels here]
    }
    blah blah blah
}

드물게 사용자가 전체 R 세션을 지속하는 기본값을 지정할 수도 있습니다.getOption

fun <- function(x,y=getOption('fun.y','initialDefault')){# or getOption('pkg.fun.y',defaultValue)
    blah blah blah
}

첫 번째 인수의 클래스에 따라 일부 매개 변수가 적용되는 경우 S3 제네릭을 사용합니다.

fun <- function(...)
    UseMethod(...)


fun.character <- function(x,y,z){# y and z only apply when x is character
   blah blah blah 
}

fun.numeric <- function(x,a,b){# a and b only apply when x is numeric
   blah blah blah 
}

fun.default <- function(x,m,n){# otherwise arguments m and n apply
   blah blah blah 
}

사용하다... 합니다.

cat0 <- function(...)
    cat(...,sep = '')

한다면, 사막선택할경, 을우로으용지마....점을 다른 함수에 전달하지 않고 사용자에게 사용하지 않는 매개 변수를 무시하고 있음을 경고합니다. 그렇지 않으면 매우 혼란스러울 수 있습니다.

fun <- (x,...){
    params <- list(...)
    optionalParamNames <- letters
    unusedParams <- setdiff(names(params),optionalParamNames)
    if(length(unusedParams))
        stop('unused parameters',paste(unusedParams,collapse = ', '))
   blah blah blah 
}

여러 가지 옵션이 있지만 공식적인 올바른 방법은 없으며, 컴퓨터와 코드를 읽는 다른 사람에게 다른 정보를 전달할 수 있지만 실제로는 잘못된 옵션은 없습니다.

주어진 예에서 가장 명확한 옵션은 ID 기본값을 제공하는 것입니다. 이 경우 다음과 같은 작업을 수행하십시오.

fooBar <- function(x, y=0) {
  x + y
}

이는 지금까지 표시된 옵션 중 가장 짧은 것이며 짧은 것이 가독성(때로는 실행 속도)에 도움이 될 수 있습니다.반환되는 것이 x와 y의 합이라는 것은 분명하며 y에 0이 되는 값이 주어지지 않는다는 것을 알 수 있습니다. x에 더하면 x가 됩니다. 분명히 덧셈보다 더 복잡한 것이 사용되면 (존재하는 경우) 다른 ID 값이 필요합니다.

이 접근 방식에 대해 제가 정말 좋아하는 한 가지는 사용할 때 기본값이 무엇인지 명확하다는 것입니다.args기능 또는 도움말 파일을 볼 수도 있습니다(세부 정보로 스크롤할 필요가 없습니다. 바로 사용법에 있습니다).

때 (코드가 할 때) 수 입니다.missing또는NULL접근 방식이 훨씬 더 합리적이 됩니다.

함수로 될 때 할 때 의 다른 중 .match.call또는sys.call기능들.

그래서 저는 "정확한" 방법은 당신이 그 특정한 주장으로 무엇을 할 계획인지 그리고 당신의 코드의 독자들에게 어떤 정보를 전달하고 싶은지에 달려있다고 생각합니다.

단지 그 내장된 것이sink함수에는 함수에서 인수를 설정하는 다양한 방법의 좋은 예가 있습니다.

> sink
function (file = NULL, append = FALSE, type = c("output", "message"),
    split = FALSE)
{
    type <- match.arg(type)
    if (type == "message") {
        if (is.null(file))
            file <- stderr()
        else if (!inherits(file, "connection") || !isOpen(file))
            stop("'file' must be NULL or an already open connection")
        if (split)
            stop("cannot split the message connection")
        .Internal(sink(file, FALSE, TRUE, FALSE))
    }
    else {
        closeOnExit <- FALSE
        if (is.null(file))
            file <- -1L
        else if (is.character(file)) {
            file <- file(file, ifelse(append, "a", "w"))
            closeOnExit <- TRUE
        }
        else if (!inherits(file, "connection"))
            stop("'file' must be NULL, a connection or a character string")
        .Internal(sink(file, closeOnExit, FALSE, split))
    }
}

저는 무엇이 필요하고 무엇이 선택 사항인지 명확하게 하기 위해 NULL을 사용하는 것을 선호합니다.Jthorpe가 제안한 것처럼 다른 인수에 의존하는 기본값을 사용하는 것에 대한 경고의 한 마디.함수가 호출될 때 값이 설정되지 않고 인수가 처음 참조될 때 값이 설정됩니다!예를 들어:

foo <- function(x,y=length(x)){
    x <- x[1:10]
    print(y)
}
foo(1:20) 
#[1] 10

반면 x를 변경하기 전에 y를 참조하는 경우:

foo <- function(x,y=length(x)){
    print(y)
    x <- x[1:10]
}
foo(1:20) 
#[1] 20

함수의 초기에 호출되지 않은 것처럼 초기화되는 "y"를 추적하는 것이 어렵기 때문에 이것은 약간 위험합니다.

이건 어때?

fun <- function(x, ...){
  y=NULL
  parms=list(...)
  for (name in names(parms) ) {
    assign(name, parms[[name]])
  }
  print(is.null(y))
}

그런 다음 시도:

> fun(1,y=4)
[1] FALSE
> fun(1)
[1] TRUE

언급URL : https://stackoverflow.com/questions/28370249/correct-way-to-specifiy-optional-arguments-in-r-functions

반응형