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
(주로 매우 간단하고 이해하기 쉽기 때문에). 사람들이 코딩에 익숙한 방식에 따라 다르지만 Hadley is.null
도 그 방식 을 지원하는 것으로 보입니다 .
Hadley의 저서 "Advanced-R"6 장, 기능, 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
}
그렇지 않으면 누락을 사용하는 경우
fun <- function(x,levels){
if(missing(levels)){
[calculate levels here]
}
blah blah blah
}
In the rare case that you thing a user may want to specify a default value that lasts an entire R session, use getOption
fun <- function(x,y=getOption('fun.y','initialDefault')){# or getOption('pkg.fun.y',defaultValue)
blah blah blah
}
If some parameters apply depending on the class of the first argument, use an S3 generic:
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
}
Use ...
only when you are passing additional parameters on to another function
cat0 <- function(...)
cat(...,sep = '')
Finally, if you do choose the use ...
without passing the dots onto another function, warn the user that your function is ignoring any unused parameters since it can be very confusing otherwise:
fun <- (x,...){
params <- list(...)
optionalParamNames <- letters
unusedParams <- setdiff(names(params),optionalParamNames)
if(length(unusedParams))
stop('unused parameters',paste(unusedParams,collapse = ', '))
blah blah blah
}
There are several options and none of them are the official correct way and none of them are really incorrect, though they can convey different information to the computer and to others reading your code.
For the given example I think the clearest option would be to supply an identity default value, in this case do something like:
fooBar <- function(x, y=0) {
x + y
}
This is the shortest of the options shown so far and shortness can help readability (and sometimes even speed in execution). It is clear that what is being returned is the sum of x and y and you can see that y is not given a value that it will be 0 which when added to x will just result in x. Obviously if something more complicated than addition is used then a different identity value will be needed (if one exists).
One thing I really like about this approach is that it is clear what the default value is when using the args
function, or even looking at the help file (you don't need to scroll down to the details, it is right there in the usage).
The drawback to this method is when the default value is complex (requiring multiple lines of code), then it would probably reduce readability to try to put all that into the default value and the missing
or NULL
approaches become much more reasonable.
Some of the other differences between the methods will appear when the parameter is being passed down to another function, or when using the match.call
or sys.call
functions.
So I guess the "correct" method depends on what you plan to do with that particular argument and what information you want to convey to readers of your code.
I would tend to prefer using NULL for the clarity of what is required and what is optional. One word of warning about using default values that depend on other arguments, as suggested by Jthorpe. The value is not set when the function is called, but when the argument is first referenced! For instance:
foo <- function(x,y=length(x)){
x <- x[1:10]
print(y)
}
foo(1:20)
#[1] 10
On the other hand, if you reference y before changing x:
foo <- function(x,y=length(x)){
print(y)
x <- x[1:10]
}
foo(1:20)
#[1] 20
This is a bit dangerous, because it makes it hard to keep track of what "y" is being initialized as if it's not called early on in the function.
Just wanted to point out that the built-in sink
function has good examples of different ways to set arguments in a function:
> 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))
}
}
how about this?
fun <- function(x, ...){
y=NULL
parms=list(...)
for (name in names(parms) ) {
assign(name, parms[[name]])
}
print(is.null(y))
}
Then try:
> fun(1,y=4)
[1] FALSE
> fun(1)
[1] TRUE
'IT story' 카테고리의 다른 글
C #에서 동등한 Java Map (0) | 2020.06.15 |
---|---|
Windows에서 makefile을 실행하는 방법은 무엇입니까? (0) | 2020.06.15 |
익명 유형을 가진 LINQ Select Distinct (0) | 2020.06.15 |
두 개의 동일한 목록에 다른 메모리 공간이있는 이유는 무엇입니까? (0) | 2020.06.15 |
HTML5에서 RTSP 또는 RTP를 통한 스트리밍 (0) | 2020.06.15 |