Interesuje mnie jaki jest „poprawny” sposób pisania funkcji z opcjonalnymi argumentami w R. Z biegiem czasu natknąłem się na kilka fragmentów kodu, które obierają tutaj inną drogę i nie mogłem znaleźć właściwej (oficjalnej) pozycji w tym temacie.
Do tej pory pisałem opcjonalne argumenty, takie jak:
fooBar <- function(x,y=NULL){
if(!is.null(y)) x <- x+y
return(x)
}
fooBar(3) # 3
fooBar(3,1.5) # 4.5
Funkcja po prostu zwraca swój argument, jeśli tylko x
zostanie podany. Używa domyślnej NULL
wartości dla drugiego argumentu, a jeśli ten argument nie jest NULL
, funkcja dodaje dwie liczby.
Alternatywnie, można by napisać funkcję w ten sposób (gdzie drugi argument musi być określony przez nazwę, ale można też zamiast tego unlist(z)
zdefiniować 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
Osobiście wolę pierwszą wersję. Jednak w obu przypadkach widzę dobre i złe strony. Pierwsza wersja jest trochę mniej podatna na błędy, ale druga może być użyta do włączenia dowolnej liczby opcji.
Czy istnieje „poprawny” sposób określenia opcjonalnych argumentów w języku R? Jak dotąd zdecydowałem się na pierwsze podejście, ale oba mogą czasami wydawać się nieco „zepsute”.
xy.coords
aby zobaczyć powszechnie stosowane podejście.xy.coords
wspomnianego przez Carla Witthofta l można znaleźć na stronie xy.coordsOdpowiedzi:
Możesz również użyć
missing()
do sprawdzenia, czy argumenty
został podany:źródło
missing()
jest również bardziej wyrazisty w tym sensie, że „mówi, co oznacza”. Ponadto umożliwia użytkownikom przekazywanie wartości NULL w miejscach, w których ma to sens!@param x numeric; something something; @param y numeric; **optional** something something; @param z logical; **optional** something something
missing()
jest okropne, gdy chcesz przekazywać argumenty z jednej funkcji do drugiej.Szczerze mówiąc, podoba mi się pierwszy sposób, w jaki OP zaczyna się od
NULL
wartości, a następnie sprawdza jąis.null
(przede wszystkim dlatego, że jest bardzo prosty i łatwy do zrozumienia). Może to zależy od sposobu, w jaki ludzie są przyzwyczajeni do kodowania, ale Hadley wydaje się również wspierać tenis.null
sposób:Z książki Hadleya „Advanced-R” Rozdział 6, Funkcje, str. 84 (wersję online można sprawdzić tutaj ):
źródło
NULL
drogi już od jakiegoś czasu i prawdopodobnie dlatego jestem do niej bardziej przyzwyczajona, gdy widzę kody źródłowe. Wydaje mi się to bardziej naturalne. To powiedziawszy, jak mówisz, że podstawa R przyjmuje oba podejścia, tak naprawdę sprowadza się to do indywidualnych preferencji.is.null
imissing
w zależności od kontekstu i co argument stosuje.Oto moje praktyczne zasady:
Jeśli wartości domyślne można obliczyć z innych parametrów, użyj domyślnych wyrażeń, jak w:
jeśli w przeciwnym razie użyj missing
W rzadkich przypadkach, gdy wydaje Ci się, że użytkownik może chcieć określić domyślną wartość, która będzie obowiązywała przez całą sesję języka R, użyj
getOption
Jeśli niektóre parametry mają zastosowanie w zależności od klasy pierwszego argumentu, użyj generycznego S3:
Używaj
...
tylko wtedy, gdy przekazujesz dodatkowe parametry do innej funkcjiNa koniec, jeśli wybierzesz użycie
...
bez przekazywania kropek do innej funkcji, ostrzeż użytkownika, że twoja funkcja ignoruje wszelkie nieużywane parametry, ponieważ w przeciwnym razie może to być bardzo mylące:źródło
NULL
funkcji w OP , ponieważ jest to wygodniejsze do tworzenia ładnych łańcuchów funkcji .Istnieje kilka opcji i żadna z nich nie jest oficjalnie poprawna i żadna z nich nie jest naprawdę błędna, chociaż mogą przekazywać różne informacje komputerowi i innym osobom czytającym twój kod.
W podanym przykładzie myślę, że najwyraźniejszą opcją byłoby podanie domyślnej wartości tożsamości, w tym przypadku wykonaj coś takiego:
Jest to najkrótsza z dotychczas wyświetlonych opcji, a skrócenie może pomóc w czytelności (a czasem nawet w przyspieszeniu wykonania). Oczywiste jest, że zwracana jest suma x i y i widać, że y nie otrzymuje wartości, która będzie równa 0, co po dodaniu do x da po prostu x. Oczywiście, jeśli zostanie użyte coś bardziej skomplikowanego niż dodawanie, potrzebna będzie inna wartość tożsamości (jeśli taka istnieje).
Jedną z rzeczy, które naprawdę podoba mi się w tym podejściu, jest to, że jest jasne, jaka jest wartość domyślna podczas korzystania z
args
funkcji, a nawet patrząc na plik pomocy (nie musisz przewijać w dół do szczegółów, jest dokładnie w użyciu ).Wadą tej metody jest to, że gdy wartość domyślna jest złożona (wymagająca wielu wierszy kodu), wówczas próba umieszczenia tego wszystkiego w wartości domyślnej prawdopodobnie zmniejszyłaby czytelność, a podejścia
missing
lubNULL
stałyby się znacznie bardziej rozsądne.Niektóre inne różnice między metodami pojawią się, gdy parametr jest przekazywany do innej funkcji lub podczas korzystania z funkcji
match.call
lubsys.call
.Więc myślę, że „poprawna” metoda zależy od tego, co planujesz zrobić z tym konkretnym argumentem i jakie informacje chcesz przekazać czytelnikom twojego kodu.
źródło
Wolałbym używać NULL ze względu na jasność tego, co jest wymagane, a co opcjonalne. Jedno słowo ostrzeżenia o używaniu wartości domyślnych, które zależą od innych argumentów, zgodnie z sugestią Jthorpe. Wartość nie jest ustawiana, gdy funkcja jest wywoływana, ale gdy najpierw następuje odwołanie do argumentu! Na przykład:
Z drugiej strony, jeśli odniesiesz się do y przed zmianą x:
Jest to trochę niebezpieczne, ponieważ utrudnia śledzenie inicjalizacji „y”, tak jakby nie było wywoływane we wczesnej fazie funkcji.
źródło
Po prostu chciał podkreślić, że wbudowana
sink
funkcja ma dobre przykłady różnych sposobów, aby ustawić argumentów funkcji:źródło
co powiesz na to?
Więc spróbuj:
źródło