R işlevlerinde isteğe bağlı bağımsız değişkenleri belirtmek için "doğru" yol


165

R'de isteğe bağlı bağımsız değişkenlerle işlevleri yazmanın "doğru" yolunun ne olduğuyla ilgileniyorum. Zamanla, burada farklı bir yol izleyen birkaç kod parçasına rastladım ve uygun (resmi) bir konum bulamadım Bu konuda.

Şimdiye kadar, böyle isteğe bağlı argümanlar yazdım:

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

İşlev yalnızca xsağlanmışsa argümanını döndürür . NULLİkinci bağımsız değişken için varsayılan bir değer kullanır ve bu bağımsız değişken olmazsa NULL, işlev iki sayıyı ekler.

Alternatif olarak, işlev şu şekilde yazılabilir (ikinci bağımsız değişkenin adıyla belirtilmesi gerekir, ancak bunun yerine de unlist(z)tanımlanabilir veya tanımlanabilir 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

Şahsen ben ilk versiyonu tercih ederim. Ancak ikisinde de iyi ve kötü görebiliyorum. İlk sürüm hataya daha az eğilimlidir, ancak ikincisi isteğe bağlı sayıda isteğe bağlı seçeneği dahil etmek için kullanılabilir.

R'de isteğe bağlı bağımsız değişkenler belirtmenin "doğru" bir yolu var mı? Şimdiye kadar, ilk yaklaşıma karar verdim, ancak her ikisi de zaman zaman biraz "hacky" hissedebilir.


xy.coordsYaygın olarak kullanılan bir yaklaşımı görmek için kaynak koduna bakın.
Carl Witthoft

5
Carl Witthoft l xy.coordstarafından belirtilen kaynak kodu xy.coords adresinde bulunabilir
RubenLaguna

Yanıtlar:


129

missing()Argümanın sağlanıp sağlanmadığını test etmek için de kullanabilirsiniz y:

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

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

5
Daha iyi özlemeyi seviyorum. özellikle çok sayıda NULL varsayılan değeriniz varsa, paket belgelerinizde x = NULL, y = NULL, z = NULL olmaz
rawr

5
@rawr missing(), "ne anlama geldiğini" anlamında da daha etkileyici. Ayrıca, kullanıcıların mantıklı olduğu yerlerde NULL değerinde geçmesine izin verir!
Josh O'Brien

31
Bana göre, bu şekilde eksik kullanmanın büyük bir dezavantajı var: fonksiyon argümanlarını gözden geçirirken, hangi argümanların gerekli olduğunu ve hangilerinin seçenek olduğunu artık göremezsiniz.
hadley

3
@param x numeric; something something; @param y numeric; **optional** something something; @param z logical; **optional** something something
rawr

4
missing()bir işlevden diğerine argümanlar iletmek istediğinizde korkunçtur.
John Smith

55

Dürüst olmak gerekirse, OP'nin aslında bir NULLdeğerle başlatmanın ve sonra kontrol etmenin ilk yolunu seviyorum is.null(öncelikle çok basit ve anlaşılması kolay olduğu için). Belki de insanların kodlamaya alışma şekline bağlıdır, ancak Hadley de is.nullyolu destekliyor gibi görünüyor :

Hadley'in "Advanced-R" kitabından Bölüm 6, İşlevler, s.84 (çevrimiçi sürüm için buraya bakın ):

Missing () işleviyle bir bağımsız değişken sağlanmış olup olmadığını belirleyebilirsiniz.

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

Bazen, önemsiz olmayan bir varsayılan değer eklemek isteyebilirsiniz ve bu hesaplamak için birkaç kod satırı alabilir. Bu kodu işlev tanımına eklemek yerine, gerekirse koşullu olarak hesaplamak için missing () kullanabilirsiniz. Ancak, bu, hangi argümanların gerekli ve hangilerinin isteğe bağlı olduğunu, belgeleri dikkatlice okumadan bilmeyi zorlaştırır. Bunun yerine, genellikle varsayılan değeri NULL olarak ayarladım ve bağımsız değişkenin sağlandığını kontrol etmek için is.null () kullanın.


2
İlginç. Kulağa mantıklı geliyor, ancak kendinizi bir işleve hangi argümanların gerekli ve hangilerinin isteğe bağlı olduğu konusunda şaşkın buldunuz mu? Ben ettik emin değilim şimdiye aslında vardı deneyim ... o
Josh O'Brien

2
@ JoshO'Brien Dürüst olmak gerekirse, kodlama stiliyle ilgili herhangi bir sorun yaşamadığımı düşünüyorum, en azından büyük olasılıkla dokümantasyon veya kaynak kodunu okumaktan dolayı büyük bir sorun olmadı. Bu yüzden öncelikle bunun gerçekten alışık olduğunuz kodlama stili meselesi olduğunu söylüyorum. Ben kullanıyorum NULLuzunca bir süre yol ve ben kaynak kodlarını görünce daha buna alışık değilim neden muhtemelen olmasıdır. Benim için daha doğal görünüyor. Bununla birlikte, R tabanının her iki yaklaşımı da aldığını söylediğiniz gibi, gerçekten bireysel tercihlere iniyor.
LyzandeR

2
Artık, gerçekten ben gerçekten ikisini de kullanarak geldi çünkü ne ben doğru olarak iki cevap işaretlemek isterdim is.nullve missingbağlama göre ve hangi argüman için kullanılmaktadır.
SimonG

5
Sorun yok @SimonG ve teşekkürler :). Her iki cevabın da çok iyi olduğunu ve bazen bağlama bağlı olduğunu kabul ediyorum. Bu çok iyi bir soru ve cevapların yine de burada birincil hedef olan çok iyi bilgi ve bilgi sağladığına inanıyorum.
LyzandeR

24

Bunlar benim kurallarım:

Varsayılan değerler diğer parametrelerden hesaplanabilirse, varsayılan ifadeleri aşağıdaki gibi kullanın:

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

aksi takdirde eksik kullanılıyorsa

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

In nadir durumlarda size bir şey, bir kullanıcı bütün bir Ar oturumu kullanımına süren varsayılan bir değer belirtmek isteyebilirsinizgetOption

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

İlk bağımsız değişkenin sınıfına bağlı olarak bazı parametreler geçerliyse, S3 genelini kullanın:

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 
}

...Yalnızca başka bir işleve ek parametreler iletirken kullanın

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

Son olarak, ...noktaları başka bir işleve geçirmeden kullanmayı seçerseniz, kullanıcıyı, aksi takdirde çok kafa karıştırıcı olabileceğinden , işlevinizin kullanılmayan parametreleri yok saydığı konusunda uyarın :

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

s3 yöntemi seçeneği de benim için ilk akla gelen şeylerden biriydi
rawr

2
Geriye dönüp bakıldığında, OP'nin NULLişlev imzasında atama yöntemine düşkün oldum , çünkü güzel bir şekilde zincirlenen işlevleri yapmak için daha uygun .
Jthorpe

10

Birkaç seçenek vardır ve bunların hiçbiri resmi doğru yoldur ve hiçbiri gerçekten yanlış değildir, ancak bilgisayara ve kodunuzu okuyan diğer kişilere farklı bilgiler aktarabilirler.

Verilen örnek için en net seçenek bir kimlik varsayılan değeri sağlamak olacağını düşünüyorum, bu durumda şöyle bir şey yapın:

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

Bu, şimdiye kadar gösterilen seçeneklerden en kısa olanıdır ve kısalık okunabilirliğe (ve hatta bazen yürütme hızına) yardımcı olabilir. Döndürülen şeyin x ve y'nin toplamı olduğu açıktır ve x'e eklendiğinde x'in 0 ile sonuçlanacağı y değerine 0 verilmeyeceğini görebilirsiniz. Açıkçası, toplamadan daha karmaşık bir şey kullanılırsa, farklı bir kimlik değerine ihtiyaç duyulacaktır (eğer varsa).

Bu yaklaşımla ilgili gerçekten sevdiğim bir şey, argsişlevi kullanırken veya hatta yardım dosyasına bakarken varsayılan değerin ne olduğu açıktır (ayrıntılara kaydırmanıza gerek yoktur, kullanımda tam oradadır. ).

Bu yöntemin dezavantajı, varsayılan değer karmaşık olduğunda (birden çok kod satırı gerektirir), o zaman muhtemelen tüm bunları varsayılan değere koymaya çalışmak okunabilirliği azaltır ve missingveya NULLyaklaşımlar çok daha makul hale gelir.

Yöntemler arasındaki diğer farklılıklardan bazıları, parametre başka bir işleve aktarılırken veya match.callveya sys.callişlevleri kullanılırken görünecektir .

Bu yüzden "doğru" yöntem, söz konusu argümanla ne yapmayı planladığınıza ve kodunuzun okuyucularına hangi bilgileri iletmek istediğinize bağlıdır.


7

Neyin gerekli ve neyin isteğe bağlı olduğu konusunda net olmak için NULL kullanmayı tercih ederim. Jthorpe tarafından önerildiği gibi, diğer bağımsız değişkenlere bağımlı varsayılan değerleri kullanma hakkında bir uyarı sözcüğü. İşlev çağrıldığında değil, bağımsız değişkene ilk başvuru yapıldığında değer ayarlanmaz! Örneğin:

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

Diğer taraftan, x'i değiştirmeden önce y'ye başvurursanız:

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

Bu biraz tehlikelidir, çünkü fonksiyonda erken çağrılmamış gibi "y" nin başlatıldığını takip etmeyi zorlaştırır.


7

Yerleşik sinkişlevin, bir işlevde bağımsız değişkenler ayarlamak için farklı yollara ilişkin iyi örneklere sahip olduğunu belirtmek istedim :

> 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))
    }
}

1

buna ne dersin?

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

O zaman dene:

> fun(1,y=4)
[1] FALSE
> fun(1)
[1] TRUE
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.