Явное вызов return в функции или нет

Некоторое время назад меня упрекнул Саймон Урбанек из основной группы R (я считаю) за рекомендацию пользователю явно вызвать return в конце функции (его комментарий был удален, хотя):

 foo = function() { return(value) } 

вместо этого он рекомендовал:

 foo = function() { value } 

Вероятно, в такой ситуации требуется:

 foo = function() { if(a) { return(a) } else { return(b) } } 

Его комментарий пролил некоторый свет на то, почему не называть return если строго необходимо, это хорошо, но это было удалено.

Мой вопрос: почему не называть return быстрее или лучше, и, следовательно, предпочтительнее?

Вопрос: почему нет (явно) вызов возврата быстрее или лучше и, следовательно, предпочтительнее?

В документации R нет никаких утверждений, что делает такое предположение.
Tha man page? ‘Function’ говорит:

 function( arglist ) expr return(value) 

Это быстрее, не возвращаясь?

Обе function() и return() являются примитивными функциями, и сама function() возвращает последнее оцениваемое значение даже без включения функции return() .

Вызов return() как .Primitive('return') с этим последним значением в качестве аргумента будет выполнять ту же работу, но требует еще одного вызова. Так что этот (часто) ненужный .Primitive('return') вызов может привлечь дополнительные ресурсы. Однако простое измерение показывает, что полученная разница очень мала и, следовательно, не может быть причиной отказа от явного возврата. Следующий график создается из данных, выбранных таким образом:

 bench_nor2 <- function(x,repeats) { system.time(rep( # without explicit return (function(x) vector(length=x,mode="numeric"))(x) ,repeats)) } bench_ret2 <- function(x,repeats) { system.time(rep( # with explicit return (function(x) return(vector(length=x,mode="numeric")))(x) ,repeats)) } maxlen <- 1000 reps <- 10000 along <- seq(from=1,to=maxlen,by=5) ret <- sapply(along,FUN=bench_ret2,repeats=reps) nor <- sapply(along,FUN=bench_nor2,repeats=reps) res <- data.frame(N=along,ELAPSED_RET=ret["elapsed",],ELAPSED_NOR=nor["elapsed",]) # res object is then visualized # R version 2.15 

Сравнение времени прошедшего времени

Изображение выше может немного отличаться на вашей платформе. Исходя из измеренных данных, размер возвращаемого объекта не вызывает никакой разницы, количество повторов (даже при увеличении) делает только очень небольшую разницу, которая в реальном слове с реальными данными и реальным алгоритмом не может быть подсчитана или сделать скрипт работает быстрее.

Это лучше, не возвращаясь?

Return - хороший инструмент для четкого проектирования «листьев» кода, где должна заканчиваться процедура, выпрыгивать из функции и возвращать значение.

 # here without calling .Primitive('return') > (function() {10;20;30;40})() [1] 40 # here with .Primitive('return') > (function() {10;20;30;40;return(40)})() [1] 40 # here return terminates flow > (function() {10;20;return();30;40})() NULL > (function() {10;20;return(25);30;40})() [1] 25 > 

Это зависит от страtagsи и стиля программирования программиста, какой стиль он использует, он не может использовать return (), поскольку он не требуется.

R основных программистов использует оба подхода, т.е. с явным возвратом () и без него, как можно найти в источниках «базовых» функций.

Многократно используется только return () (без аргумента), возвращающий NULL в случаях, чтобы условно остановить функцию.

Неясно, лучше ли это или нет, поскольку стандартный пользователь или аналитик, использующий R, не может видеть реальную разницу.

Мое мнение таково, что вопрос должен быть: есть ли какая-либо опасность при использовании явного возвращения из реализации R?

Или, может быть, лучше, код функции написания пользователем всегда должен спрашивать: каков эффект от использования явного возврата (или размещения объекта, который будет возвращен как последний лист ветви кода) в коде функции?

Если все согласятся с тем, что

  1. return не требуется в конце тела функции
  2. не используя return немного быстрее (согласно тесту @ Алана, 4,3 микросекунды против 5.1)

Должны ли мы прекратить использование return в конце функции? Я, конечно, не буду, и я хотел бы объяснить, почему. Я надеюсь услышать, что другие люди разделяют мое мнение. И я извиняюсь, если это не прямой ответ OP, а скорее длинный субъективный комментарий.

Моя главная проблема не в том, чтобы использовать return это то, что, как заметил Павел, в теле функции есть другие места, где вам может понадобиться. И если вы вынуждены использовать return где-то посередине вашей функции, почему бы не сделать все операторы return явными? Я ненавижу быть непоследовательным. Также я думаю, что код читается лучше; можно сканировать функцию и легко видеть все точки выхода и значения.

Павел использовал этот пример:

 foo = function() { if(a) { return(a) } else { return(b) } } 

К сожалению, можно отметить, что его можно легко переписать так:

 foo = function() { if(a) { output <- a } else { output <- b } output } 

Последняя версия даже соответствует некоторым стандартам программирования, которые защищают один оператор возврата за каждую функцию. Я думаю, что лучший пример мог бы быть:

 bar <- function() { while (a) { do_stuff for (b) { do_stuff if (c) return(1) for (d) { do_stuff if (e) return(2) } } } return(3) } 

Это было бы намного сложнее переписать с использованием одного оператора return: для его распространения потребуется несколько break s и сложная система булевых переменных. Все это говорит о том, что правило единственного возврата не очень хорошо работает с R. Итак, если вам нужно будет использовать return в некоторых местах тела вашей функции, почему бы не быть последовательным и использовать его повсюду?

Я не думаю, что аргумент скорости является допустимым. Разница в 0,8 микросекунды - это ничто, когда вы начинаете смотреть на функции, которые на самом деле что-то делают. Последнее, что я вижу, это то, что он набирает меньше, но эй, я не ленив.

Кажется, что без return() это быстрее …

 library(rbenchmark) x <- 1 foo <- function(value) { return(value) } fuu <- function(value) { value } benchmark(foo(x),fuu(x),replications=1e7) test replications elapsed relative user.self sys.self user.child sys.child 1 foo(x) 10000000 51.36 1.185322 51.11 0.11 0 0 2 fuu(x) 10000000 43.33 1.000000 42.97 0.05 0 0 

____ ИЗМЕНИТЬ __ _ __ _ __ _ __ _ __ _ ___

Я benchmark(fuu(x),foo(x),replications=1e7) к другим критериям ( benchmark(fuu(x),foo(x),replications=1e7) ), и результат отменяется ... Я постараюсь на сервере.

Это интересная дискуссия. Я думаю, что пример @ flodel превосходный. Однако, я думаю, это иллюстрирует мою точку зрения (и @koshke упоминает это в комментарии), что return имеет смысл, когда вы используете императив вместо функционального стиля кодирования .

Чтобы не допустить этого, но я бы переписал foo следующим образом:

 foo = function() ifelse(a,a,b) 

Функциональный стиль позволяет избежать изменений состояния, таких как сохранение значения output . В этом стиле return неуместно; foo больше похож на математическую функцию.

Я согласен с @flodel: использование сложной системы булевых переменных в bar будет менее ясным и бессмысленным, когда вы return . Что делает bar настолько поддающимся return что он написан в императивном стиле. Действительно, логические переменные представляют собой «состояния», которые избегают в функциональном стиле.

Это действительно сложно переписать bar в функциональном стиле, потому что это просто псевдокод, но идея что-то вроде этого:

 e_func <- function() do_stuff d_func <- function() ifelse(any(sapply(seq(d),e_func)),2,3) b_func <- function() { do_stuff ifelse(c,1,sapply(seq(b),d_func)) } bar <- function () { do_stuff sapply(seq(a),b_func) # Not exactly correct, but illustrates the idea. } 

Цикл while будет сложнее переписать, поскольку он управляется изменениями состояния в a .

Потери скорости, вызванные призывом к return , незначительны, но эффективность, достигаемая путем избежания return и перезаписи в функциональном стиле, часто огромна. Говорить новым пользователям о прекращении использования return вероятно, не поможет, но приведение их в функциональный стиль будет отдачей.


@Paul return необходим в императивном стиле, потому что вы часто хотите выйти из функции в разных точках цикла. Функциональный стиль не использует циклы и, следовательно, не нуждается в return . В чисто функциональном стиле окончательный вызов почти всегда является желаемым возвратным значением.

В Python для функций требуется оператор return . Однако, если вы запрограммировали свою функцию в функциональном стиле, у вас, скорее всего, будет только один оператор return : в конце вашей функции.

Используя пример из другого сообщения StackOverflow, скажем, нам нужна функция, которая вернула TRUE если все значения в заданном x имели нечетную длину. Мы могли бы использовать два стиля:

 # Procedural / Imperative allOdd = function(x) { for (i in x) if (length(i) %% 2 == 0) return (FALSE) return (TRUE) } # Functional allOdd = function(x) all(length(x) %% 2 == 1) 

В функциональном стиле возвращаемое значение естественно попадает в конец функции. Опять же, это больше похоже на математическую функцию.

@GSee Предупреждения, изложенные в ?ifelse , определенно интересны, но я не думаю, что они пытаются отговорить использовать функцию. Фактически, ifelse имеет преимущество функций автоматического векторизации. Например, рассмотрим слегка измененную версию foo :

 foo = function(a) { # Note that it now has an argument if(a) { return(a) } else { return(b) } } 

Эта функция отлично работает, когда length(a) равна 1. Но если вы переписали foo с ifelse

 foo = function (a) ifelse(a,a,b) 

Теперь foo работает на любой длине a . Фактически, это будет даже работать, когда a - matrix. Возrotation значения той же формы, что и test является функцией, которая помогает с векторизации, а не проблемой.

Проблема, заключающаяся в том, что я не добавляю «return» явно в конце, состоит в том, что если добавить конец в конец метода, то внезапное возвращаемое значение неверно:

 foo <- function() { dosomething() } 

Это возвращает значение dosomething() .

Теперь мы приходим на следующий день и добавляем новую строку:

 foo <- function() { dosomething() dosomething2() } 

Мы хотели, чтобы наш код возвращал значение dosomething() , но вместо этого он больше не работает.

С явным возвратом это становится действительно очевидным:

 foo <- function() { return( dosomething() ) dosomething2() } 

Мы можем видеть, что в этом коде есть что-то странное и исправить:

 foo <- function() { dosomething2() return( dosomething() ) } 

Я думаю о return в качестве трюка. Как правило, значение последнего выражения, оцениваемого в функции, становится значением функции, и этот общий шаблон встречается во многих местах. Все нижеследующие оценивают до 3:

 local({ 1 2 3 }) eval(expression({ 1 2 3 })) (function() { 1 2 3 })() 

Какое return действительно не возвращает значение (это делается с ним или без него), но «вырывание» функции нерегулярно. В этом смысле это самый близкий эквивалент оператора GOTO в R (также есть разрыв и следующий). Я использую return очень редко и никогда в конце функции.

  if(a) { return(a) } else { return(b) } 

… это можно переписать так, как if(a) a else b который был бы намного более читабельным и менее вьющимся. Здесь нет необходимости return . Мой прототипный случай использования «возвращения» был бы чем-то вроде …

 ugly <- function(species, x, y){ if(length(species)>1) stop("First argument is too long.") if(species=="Mickey Mouse") return("You're kidding!") ### do some calculations if(grepl("mouse", species)) { ## do some more calculations if(species=="Dormouse") return(paste0("You're sleeping until", x+y)) ## do some more calculations return(paste0("You're a mouse and will be eating for ", x^y, " more minutes.")) } ## some more ugly conditions # ... ### finally return("The end") } 

Как правило, потребность в многих возвратах предполагает, что проблема является либо уродливой, либо плохо структурированной.

<>

return действительно не нужна функция для работы: вы можете использовать ее, чтобы вырваться из набора выражений, подлежащих оценке.

 getout <- TRUE # if getout==TRUE then the value of EXP, LOC, and FUN will be "OUTTA HERE" # .... if getout==FALSE then it will be `3` for all these variables EXP <- eval(expression({ 1 2 if(getout) return("OUTTA HERE") 3 })) LOC <- local({ 1 2 if(getout) return("OUTTA HERE") 3 }) FUN <- (function(){ 1 2 if(getout) return("OUTTA HERE") 3 })() identical(EXP,LOC) identical(EXP,FUN) 

return может повысить читаемость кода:

 foo <- function() { if (a) return(a) b } 
  • конвертировать письма в номера
  • Как вы кодируете функцию R так, чтобы она «знала», чтобы искать «данные» для переменных в других аргументах?
  • Получить «встроенные nul (s), найденные во вводе» при чтении csv, используя read.csv ()
  • Вычисление суммарной суммы для каждой строки
  • Лучший способ конвертировать список в вектор?
  • Найти ближайшее значение в векторе с бинарным поиском
  • Импорт CSV-файла в R-числовые значения, считанные как символы
  • Манекены переменных из строковой переменной
  • Автоматически создавать формулы для всех возможных линейных моделей
  • Как проверить, вызывает ли вызов функции предупреждение?
  • Условно удалить строки Dataframe с R
  • Interesting Posts

    Получите TransactionScope для работы с async / wait

    Msgstr “Ликвидировать CSS-рендеринг с помощью надстрочного содержимого”

    Изменение раскладки клавиатуры на DVORAK из командной строки на Mac

    Приложение Facebook Iframe с несколькими страницами в сеансах сеанса Safari не сохраняется

    GVim не является источником .bashrc, .bash_profile или .profile из неинтерактивной оболочки без входа

    Возможно ли одновременная загрузка двух ОС одновременно?

    Добавьте несколько изображений в тело электронной почты (inline) с помощью приложения windows c #

    .Net DefaultValueAttribute on Properties

    jQuery скрыть элемент при нажатии в любом месте страницы

    Android map v2 zoom, чтобы показать все маркеры

    Сложность с ng-моделью, ng-repeat и входами

    Неужели плохая практика заставит сеттера вернуть «это»?

    UILabel – видимая часть текста

    Объединить два текстовых файла по строкам, используя командный скрипт

    Конфликт Ассамблеи Newtonsoft.Json

    Давайте будем гением компьютера.