आर कार्यों में वैकल्पिक तर्कों को निर्दिष्ट करने के लिए "सही" तरीका


165

मुझे दिलचस्पी है कि आर। में वैकल्पिक तर्कों के साथ फ़ंक्शन लिखने के लिए "सही" तरीका क्या है, समय के साथ, मैं कोड के कुछ टुकड़ों पर ठोकर खाई, जो यहां एक अलग मार्ग लेते हैं, और मुझे एक उचित (आधिकारिक) स्थिति नहीं मिली इस विषय पर।

अब तक, मैंने इस तरह के वैकल्पिक तर्क लिखे हैं:

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

व्यक्तिगत रूप से मैं पहला संस्करण पसंद करता हूं। हालांकि, मैं अच्छे और बुरे दोनों को देख सकता हूं। पहला संस्करण त्रुटि के लिए थोड़ा कम प्रवण है, लेकिन दूसरे को वैकल्पिक की एक मनमानी संख्या को शामिल करने के लिए इस्तेमाल किया जा सकता है।

क्या आर में वैकल्पिक तर्कों को निर्दिष्ट करने का एक "सही" तरीका है? अब तक, मैं पहले दृष्टिकोण पर बस गया हूं, लेकिन दोनों कभी-कभार "हैकी" महसूस कर सकते हैं।


xy.coordsआमतौर पर उपयोग किए जाने वाले दृष्टिकोण को देखने के लिए स्रोत कोड देखें।
कार्ल विटथॉफ्ट

5
कार्ल विटथॉफ्ट एल xy.coordsद्वारा उल्लिखित स्रोत कोड xy.coords
RubenLaguna

जवाबों:


129

आप यह भी missing()परीक्षण करने के लिए उपयोग कर सकते हैं कि क्या तर्क yकी आपूर्ति की गई थी या नहीं :

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

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

5
मुझे बेहतर याद आ रहा है। खासकर यदि आप शून्य मूलभूत मूल्यों का एक बहुत है, तो आप अपने पैकेज दस्तावेज में एक्स = शून्य, y = शून्य, z = शून्य नहीं होगा
rawr

5
@ क्रॉल missing()इस अर्थ में भी अधिक अभिव्यंजक है कि यह "कहता है कि इसका क्या अर्थ है"। साथ ही यह उपयोगकर्ताओं को उन जगहों पर NULL के मान से गुजरने की अनुमति देता है, जहां यह समझ में आता है!
जोश ओ'ब्रायन

31
मेरे लिए, इस तरह से लापता का उपयोग करने के लिए बड़ा नकारात्मक है: फ़ंक्शन तर्कों को स्किम करते समय आप अब नहीं देख सकते हैं कि कौन से तर्क आवश्यक हैं और कौन से विकल्प हैं।
हैडली

3
@param x numeric; something something; @param y numeric; **optional** something something; @param z logical; **optional** something something
कच्ची

4
missing()भयानक है जब आप एक फ़ंक्शन से दूसरे में तर्क पारित करना चाहते हैं।
जॉन स्मिथ

55

ईमानदार होने के लिए मुझे ओपी का पहला तरीका पसंद है, वास्तव में इसे एक NULLमूल्य के साथ शुरू करना और फिर इसे जांचना is.null(मुख्यतः क्योंकि यह बहुत सरल और समझने में आसान है)। यह शायद उस तरह से निर्भर करता है जिस तरह से लोगों को कोडिंग के लिए उपयोग किया जाता है लेकिन हैडली इस is.nullतरह से भी समर्थन करती है:

हैडली की पुस्तक "एडवांस्ड-आर" अध्याय 6, कार्य, p.84 (ऑनलाइन संस्करण की जांच के लिए यहां ):

आप यह निर्धारित कर सकते हैं कि लापता () फ़ंक्शन के साथ एक तर्क की आपूर्ति की गई थी या नहीं।

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

कभी-कभी आप एक गैर-तुच्छ डिफ़ॉल्ट मान जोड़ना चाहते हैं, जिसे गणना करने के लिए कोड की कई लाइनें लग सकती हैं। फ़ंक्शन की परिभाषा में उस कोड को सम्मिलित करने के बजाय, यदि आवश्यक हो तो आप लापता () को सशर्त रूप से गणना करने के लिए उपयोग कर सकते हैं। हालाँकि, इससे यह जानना मुश्किल हो जाता है कि कौन-कौन से तर्कों की आवश्यकता है और जो दस्तावेज़ीकरण को ध्यान से पढ़े बिना वैकल्पिक हैं। इसके बजाय, मैं आमतौर पर NULL को डिफ़ॉल्ट मान सेट करता हूं और तर्क की आपूर्ति होने पर जांचने के लिए is.null () का उपयोग करता हूं।


2
दिलचस्प। यह उचित लगता है, लेकिन क्या आप कभी भी अपने आप को इस बारे में हैरान पाते हैं कि किसी फ़ंक्शन के लिए कौन सी दलीलें आवश्यक हैं और कौन सी वैकल्पिक हैं? मैं सुनिश्चित नहीं हूं कि मैं गया है कभी वास्तव में था अनुभव ... कि
जोश ओ ब्रायन

2
@ जोश'ब्रायन मुझे लगता है कि मैं ईमानदार होने के लिए या तो कोडिंग शैली के साथ वह समस्या नहीं थी, कम से कम यह कभी भी प्रलेखन या स्रोत कोड पढ़ने के कारण एक प्रमुख मुद्दा नहीं था। और यही कारण है कि मैं मुख्य रूप से कहता हूं कि यह वास्तव में उस कोडिंग शैली का मामला है जिसका आप उपयोग कर रहे हैं। मैं NULLकाफी समय से रास्ते का उपयोग कर रहा हूं और शायद इसीलिए जब मैं स्रोत कोड देखता हूं तो मुझे इसका अधिक उपयोग होता है। यह मुझे अधिक स्वाभाविक लगता है। जैसा कि आप कहते हैं कि आधार आर दोनों दृष्टिकोणों को लेता है, यह वास्तव में व्यक्तिगत प्राथमिकताओं के लिए नीचे आता है।
लाईजेंड्री

2
अब तक, मैं वास्तव में कामना करता हूं कि मैं दो उत्तरों को सही मान सकूं क्योंकि मैं वास्तव में दोनों का उपयोग करते हुए आया हूं is.nullऔर missingसंदर्भ के आधार पर और तर्क का उपयोग किया गया है।
सिमॉन

5
यह ठीक है @SimonG और धन्यवाद :)। मैं सहमत हूं कि दोनों उत्तर बहुत अच्छे हैं और वे कभी-कभी संदर्भ पर निर्भर करते हैं। यह एक बहुत अच्छा सवाल है और मेरा मानना ​​है कि उत्तर बहुत अच्छी जानकारी और ज्ञान प्रदान करते हैं जो कि वैसे भी यहाँ प्राथमिक लक्ष्य है।
अगस्त को

24

ये हैं मेरे अंगूठे के नियम:

यदि डिफ़ॉल्ट मानों की गणना अन्य मापदंडों से की जा सकती है, तो निम्न में डिफ़ॉल्ट अभिव्यक्तियों का उपयोग करें:

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

यदि अन्यथा लापता का उपयोग कर

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 
}

s3 विधि विकल्प उन चीजों में से एक था जो मेरे लिए पहली बात थी, वह भी
rawr

2
रेट्रोस्पेक्ट में, मैं NULLफ़ंक्शन हस्ताक्षर में ओपी की विधि को पसंद करने का शौकीन बन गया हूं , क्योंकि यह उन कार्यों के लिए अधिक सुविधाजनक है जो अच्छी तरह से श्रृंखला बनाते हैं ।
जेथोरपे

10

कई विकल्प हैं और उनमें से कोई भी आधिकारिक सही तरीका नहीं है और उनमें से कोई भी वास्तव में गलत नहीं है, हालांकि वे कंप्यूटर को और आपके कोड को पढ़ने वाले अन्य लोगों को विभिन्न जानकारी दे सकते हैं।

दिए गए उदाहरण के लिए मुझे लगता है कि स्पष्ट विकल्प एक पहचान डिफ़ॉल्ट मान की आपूर्ति करना होगा, इस मामले में कुछ ऐसा करें:

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

यह अब तक दिखाए गए विकल्पों में सबसे छोटा है और लघुता पठनीयता (और कभी-कभी निष्पादन में गति) को भी मदद कर सकती है। यह स्पष्ट है कि जो लौटाया जा रहा है वह x और y का योग है और आप देख सकते हैं कि y को एक मान नहीं दिया गया है कि यह 0 होगा जो x में जोड़े जाने पर x में परिणाम होगा। जाहिर है कि अगर जोड़ से अधिक जटिल चीज का उपयोग किया जाता है तो एक अलग पहचान मूल्य की आवश्यकता होगी (यदि कोई मौजूद है)।

इस दृष्टिकोण के बारे में मुझे एक बात पसंद है कि यह स्पष्ट है कि argsफ़ंक्शन का उपयोग करते समय डिफ़ॉल्ट मान क्या है , या यहां तक ​​कि मदद फ़ाइल को देखने के लिए (आपको विवरण तक स्क्रॉल करने की आवश्यकता नहीं है, यह उपयोग में वहीं है )।

इस विधि में दोष यह है कि जब डिफ़ॉल्ट मान जटिल होता है (कोड की कई पंक्तियों की आवश्यकता होती है), तो यह संभवतया पठनीयता को कम कर देगा जो कि सभी को डिफ़ॉल्ट मूल्य में डालने की कोशिश करता है और missingया NULLदृष्टिकोण बहुत अधिक उचित हो जाता है।

तरीकों के बीच अन्य अंतर से कुछ जब पैरामीटर एक और कार्य करने के लिए नीचे पारित किया जा रहा है, या का उपयोग करते समय दिखाई देगा match.callया sys.callकार्य करता है।

इसलिए मुझे लगता है कि "सही" विधि इस बात पर निर्भर करती है कि आप उस विशेष तर्क के साथ क्या करने की योजना बना रहे हैं और आप अपने कोड के पाठकों को क्या जानकारी देना चाहते हैं।


7

मैं क्या आवश्यक है और क्या वैकल्पिक है की स्पष्टता के लिए NULL का उपयोग करना पसंद करेंगे। डिफ़ॉल्ट मानों का उपयोग करने के बारे में चेतावनी का एक शब्द जो अन्य तर्कों पर निर्भर करता है, जैसा कि जेथोरपे द्वारा सुझाया गया है। फ़ंक्शन को कॉल करने पर मान सेट नहीं होता है, लेकिन जब तर्क को पहली बार संदर्भित किया जाता है! उदाहरण के लिए:

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" का ट्रैक रखना कठिन बनाता है, क्योंकि इसे फंक्शन में जल्दी नहीं बुलाया जाता है।


7

केवल यह बताना चाहते हैं कि अंतर्निहित 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))
    }
}

1

इस बारे में कैसा है?

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.