Меня интересует, каков «правильный» способ написания функций с необязательными аргументами в 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? До сих пор я остановился на первом подходе, но оба могут иногда чувствовать себя немного "хакерскими".
xy.coords
чтобы увидеть часто используемый подход.xy.coords
упомянутого Carl Witthoft l можно найти по адресу xy.coordsОтветы:
Вы также можете использовать
missing()
для проверки, был ли предоставлен аргументy
:источник
missing()
также более выразителен в том смысле, что он «говорит, что это значит». Кроме того, он позволяет пользователям передавать значение NULL в местах, где это имеет смысл!@param x numeric; something something; @param y numeric; **optional** something something; @param z logical; **optional** something something
missing()
ужасно, когда вы хотите передавать аргументы от одной функции к другой.Честно говоря, мне нравится, когда OP сначала начинает со
NULL
значения, а затем проверяет егоis.null
(в первую очередь потому, что это очень просто и легко понять). Возможно, это зависит от того, как люди привыкли к кодированию, но Хэдли, кажется,is.null
тоже поддерживает этот способ:Из книги Хэдли «Advanced-R», глава 6, «Функции», стр.84 (онлайн-версию смотрите здесь ):
источник
NULL
способ довольно давно, и, вероятно, именно поэтому я более привык к нему, когда вижу исходные коды. Это кажется более естественным для меня. Тем не менее, как вы говорите, база R использует оба подхода, так что все сводится к индивидуальным предпочтениям.is.null
и вmissing
зависимости от контекста и того, для чего используется аргумент.Вот мои эмпирические правила:
Если значения по умолчанию можно рассчитать из других параметров, используйте выражения по умолчанию, как в:
если в противном случае отсутствует
В том редком случае, когда пользователь может указать значение по умолчанию, которое длится весь сеанс R, используйте
getOption
Если некоторые параметры применяются в зависимости от класса первого аргумента, используйте универсальный S3:
Используйте
...
только когда вы передаете дополнительные параметры другой функцииИ, наконец, если вы выбираете использование
...
без прохождения точки на другую функцию, предупреждает пользователя , что ваша функция игнорирует все неиспользуемые параметры , так как это может быть очень запутанным иначе:источник
NULL
в сигнатуре функции, так как она более удобна для создания функций , которые цепные красиво.Есть несколько вариантов, и ни один из них не является официальным правильным способом, и ни один из них не является действительно неправильным, хотя они могут передавать различную информацию на компьютер и другим, читающим ваш код.
Для данного примера я думаю, что наиболее ясным вариантом будет предоставить значение по умолчанию для идентификатора, в этом случае сделайте что-то вроде:
Это самый короткий из представленных вариантов, и краткость может помочь читаемости (а иногда даже скорости выполнения). Понятно, что возвращается сумма x и y, и вы можете видеть, что y не задано значение, которое будет равно 0, что при добавлении к x приведет только к x. Очевидно, что если используется нечто более сложное, чем сложение, тогда потребуется другое значение идентификатора (если оно существует).
Одна вещь, которая мне действительно нравится в этом подходе, это то, что ясно, каково значение по умолчанию при использовании
args
функции или даже при просмотре файла справки (вам не нужно прокручивать детали, это прямо в использовании ).Недостаток этого метода в том, что если значение по умолчанию является сложным (требующим нескольких строк кода), то это, вероятно, уменьшит удобочитаемость, пытаясь поместить все это в значение по умолчанию, и подходы
missing
илиNULL
станут более разумными.Некоторые другие различия между методами будут появляться, когда параметр передается другой функции или при использовании функций
match.call
илиsys.call
.Поэтому я полагаю, что «правильный» метод зависит от того, что вы планируете делать с этим конкретным аргументом, и какую информацию вы хотите донести до читателей вашего кода.
источник
Я бы предпочел использовать NULL для ясности того, что требуется, а что необязательно. Одно предупреждение об использовании значений по умолчанию, которые зависят от других аргументов, как предлагает Jthorpe. Значение не устанавливается при вызове функции, но при первом обращении к аргументу! Например:
С другой стороны, если вы ссылаетесь на y перед изменением x:
Это немного опасно, потому что трудно следить за тем, что "y" инициализируется, как если бы оно не вызывалось в начале функции.
источник
Просто хотел бы отметить, что встроенная
sink
функция имеет хорошие примеры различных способов установки аргументов в функции:источник
как насчет этого?
Тогда попробуйте:
источник