Некоторое время назад Саймон Урбанек (Simon Urbanek) из основной команды R (я полагаю) получил от меня return
замечания за то, что он рекомендовал пользователю явно вызывать в конце функции (хотя его комментарий был удален):
foo = function() {
return(value)
}
вместо этого он рекомендовал:
foo = function() {
value
}
Вероятно, в такой ситуации требуется:
foo = function() {
if(a) {
return(a)
} else {
return(b)
}
}
Его комментарий пролил некоторый свет на то, почему не звонить, return
если в этом нет особой необходимости, это хорошо, но это было удалено.
Мой вопрос: почему не звонить return
быстрее или лучше, и, следовательно, предпочтительнее?
return
не нужно даже в последнем примере. Удалениеreturn
может сделать это немного быстрее, но, на мой взгляд, это потому, что R называется функциональным языком программирования.return
вызывает нелокальный скачок, и явный нелокальный скачок необычен для FP. Собственно, например, схемы нетreturn
. Я думаю, что мои комментарии слишком короткие (и, возможно, неправильные) в качестве ответа.return
,break
,continue
либо, что утомительные иногда.Ответы:
Вопрос заключался в следующем: почему (явно) вызов return не является более быстрым или лучшим, и поэтому предпочтительным?
В документации на R нет такого предположения.
Главная страница? 'Функция' говорит:
Это быстрее без вызова возврата?
Оба
function()
иreturn()
являются примитивными функциями, аfunction()
сам возвращает последнее оцененное значение, даже не включаяreturn()
функцию.Вызов ,
return()
как.Primitive('return')
с этим последним значением в качестве аргумента будет делать ту же работу , но нужно один вызов больше. Так что этот (часто) ненужный.Primitive('return')
вызов может привлечь дополнительные ресурсы. Простое измерение, однако, показывает, что результирующая разница очень мала и, следовательно, не может быть причиной отказа от явного возврата. Следующий график создается из данных, выбранных таким образом:Изображение выше может немного отличаться на вашей платформе. Основываясь на измеренных данных, размер возвращаемого объекта не вызывает никакой разницы, количество повторений (даже в увеличенном масштабе) имеет очень маленькое различие, которое в реальном слове с реальными данными и реальным алгоритмом не может быть подсчитано или произведено. скрипт работает быстрее.
Лучше без звонка вернуть?
Return
хороший инструмент для четкого проектирования «листьев» кода, где процедура должна заканчиваться, выпрыгивать из функции и возвращать значение.От стратегии и стиля программирования программиста зависит, какой стиль он использует, он не может использовать return (), так как это не требуется.
R основные программисты используют оба подхода, т.е. с и без явного возврата (), как это можно найти в источниках «базовых» функций.
Много раз используется только return () (без аргументов), возвращая NULL в тех случаях, чтобы условно остановить функцию.
Не ясно, лучше это или нет, поскольку обычный пользователь или аналитик, использующий R, не видит реальной разницы.
Мое мнение таково, что вопрос должен быть следующим: существует ли какая-либо опасность в использовании явного возврата из реализации R?
Или, может быть , лучше, написание кода пользователя функция всегда должны спросить: Что такое эффект не используя явный возврат (или размещения объекта должен быть возвращен в качестве последнего листа кода филиала) в коде функции?
источник
return
, и все зависит от предпочтения программиста, использовать его или нет.return
действительно последняя вещь, о которой вы должны беспокоиться.return
вызовов функций. Вопрос, который вы должны задать, не тот, который вы предлагаете в конце. Вместо этого это: «почему я должен использовать избыточныйreturn
? Какую пользу это дает? » Как оказалось, ответ «не много» или даже «вообще ничего». Ваш ответ не может оценить это.return
это похоже на явный комментарий с надписью «увеличение х на 1» рядом с выполнением фрагмента кодаx = x + 2
. Другими словами, его явность (а) совершенно не имеет значения, и (б) он передает неверную информацию. Потомуreturn
что семантика в R - это просто «прервать эту функцию». Это не означает то же самое, что иreturn
в других языках.Если все согласны с этим
return
не требуется в конце тела функцииreturn
незначительно быстрее (согласно тесту @ Alan, 4,3 микросекунды против 5,1)мы все должны прекратить использование
return
в конце функции? Я конечно не буду, и я хотел бы объяснить, почему. Я надеюсь услышать, если другие люди разделяют мое мнение. И я прошу прощения, если это не прямой ответ на ФП, а скорее как длинный субъективный комментарий.Моя главная проблема, связанная с неиспользованием,
return
состоит в том, что, как указал Пол, в теле функции есть другие места, где вам это может понадобиться. И если вы вынуждены использоватьreturn
где-то в середине вашей функции, почему бы не сделать всеreturn
утверждения явными? Я ненавижу быть непоследовательным. Также я думаю, что код читается лучше; Можно отсканировать функцию и легко увидеть все точки выхода и значения.Павел использовал этот пример:
К сожалению, можно отметить, что это может быть легко переписано как:
Последняя версия даже соответствует некоторым стандартам программирования, которые предусматривают один оператор возврата для каждой функции. Я думаю, что лучший пример мог бы быть:
Это было бы гораздо труднее переписать с помощью одного оператора return:
break
для их распространения потребовалось бы несколько s и сложная система булевых переменных. Все это говорит о том, что правило одиночного возврата не очень хорошо работает с R. Итак, если вам нужно использоватьreturn
в некоторых местах тела вашей функции, почему бы не быть последовательным и использовать его везде?Я не думаю, что аргумент скорости является действительным. Разница в 0,8 микросекунды - ничто, когда вы начинаете смотреть на функции, которые действительно что-то делают. Последнее, что я вижу, это то, что он печатает меньше, но я не ленивый.
источник
return
в некоторых случаях явно требуется утверждение, как показало @flodel. В качестве альтернативы, есть ситуации, когда оператор return лучше всего опускать, например, много-много мелких вызовов функций. Во всех других, скажем, 95%, случаях не имеет значения, используете ли вы егоreturn
или нет, и все сводится к предпочтениям. Мне нравится использовать return, так как он более понятен в том, что вы имеете в виду, и, следовательно, более читабелен. Может быть, это обсуждение сродни<-
против=
?return
для возврата значения бессмысленно, наравне с записьюif (x == TRUE)
вместоif (x)
.foo
какfoo <- function(x) if (a) a else b
(с необходимыми разрывами строки). Нет необходимости явного возврата или промежуточного значения.Это интересная дискуссия. Я думаю, что пример @ flodel превосходен. Тем не менее, я думаю, что это иллюстрирует мою точку зрения (и @koshke упоминает об этом в комментарии), что
return
имеет смысл, когда вы используете императив вместо функционального стиля кодирования .Не говоря о сути, но я бы переписал
foo
это так:Функциональный стиль позволяет избежать изменений состояния, например, хранить значение
output
. В этом стилеreturn
неуместно;foo
выглядит больше как математическая функция.Я согласен с @flodel: использование сложной системы логических переменных в
bar
будет менее понятным и бессмысленным, если у вас естьreturn
. Что делает заявленияbar
такими привлекательными, такreturn
это то, что они написаны в императивном стиле. Действительно, логические переменные представляют изменения «состояния», которых избегают в функциональном стиле.Это действительно сложно переписать
bar
в функциональном стиле, потому что это просто псевдокод, но идея примерно такая:while
Цикл будет наиболее трудно переписать, потому что он находится под контролем изменений состоянияa
.Потеря скорости, вызванная вызовом,
return
незначительна, но эффективность, полученная благодаря избеганиюreturn
и переписыванию в функциональном стиле, часто огромна. Сказать новым пользователям прекратить использование,return
вероятно, не поможет, но приведение их к функциональному стилю окупится.@Paul
return
необходим в императивном стиле, потому что вы часто хотите выйти из функции в разных точках цикла. Функциональный стиль не использует циклы, и поэтому не нуждаетсяreturn
. В чисто функциональном стиле последний вызов почти всегда является желаемым возвращаемым значением.В Python для функций требуется
return
оператор. Однако, если вы запрограммировали свою функцию в функциональном стиле, у вас, скорее всего, будет только одноreturn
утверждение: в конце вашей функции.Используя пример из другого поста StackOverflow, допустим, мы хотели получить функцию, которая возвращала
TRUE
бы, если бы все значения в данном случаеx
имели нечетную длину. Мы могли бы использовать два стиля:В функциональном стиле возвращаемое значение естественным образом попадает в конец функции. Опять же, это больше похоже на математическую функцию.
@GSee Предупреждения, изложенные в
?ifelse
, безусловно, интересны, но я не думаю, что они пытаются отговорить использование этой функции. Фактически,ifelse
имеет преимущество автоматической векторизации функций. Например, рассмотрим слегка измененную версиюfoo
:Эта функция прекрасно работает, когда
length(a)
1. Но если вы переписалиfoo
сifelse
Сейчас
foo
работает на любую длинуa
. На самом деле, это будет даже работать, когдаa
матрица. Возврат значения той же формы, чтоtest
и у функции, которая помогает с векторизацией, не проблема.источник
return
не подходит функциональный стиль программирования. Если кто-то программирует императивно или функционально, на каком-то этапе функция или подпрограмма должна что-то вернуть. Например, функциональное программирование на python все еще требуетreturn
утверждения. Не могли бы вы подробнее рассказать об этом?ifelse(a,a,b)
любимую мозоль. Кажется, что каждая строка?ifelse
кричит: «Не используйте меня вместоif (a) {a} else b
». Например, «... возвращает значение с той же формой, что иtest
», «еслиyes
илиno
слишком короткие, их элементы перерабатываются.», «режим результата может зависеть от значенияtest
», «атрибут класса результата взято изtest
и может быть неподходящим для значений, выбранных изyes
иno
"foo
не имеет особого смысла; он всегда вернет TRUE илиb
.ifelse
Его использование вернет 1 или несколько ИСТИНА и / или 1 или несколькоb
с. Первоначально я думал, что целью функции было сказать «если какое-то утверждение истинно, вернуть что-то, иначе вернуть что-то другое». Я не думаю, что это должно быть векторизовано, потому что тогда это станет «возвращать элементы некоторого объекта, которые ИСТИНА, и для всех элементов, которые не ИСТИНА, возвращатьb
.Кажется, без
return()
этого быстрее ...____ РЕДАКТИРОВАТЬ__ _ __ _ __ _ __ _ __ _ ___
Я перехожу к другим тестам (
benchmark(fuu(x),foo(x),replications=1e7)
), и результат меняется на противоположный ... Я попробую на сервере.источник
return()
, один, если нет. Он полностью избыточен в конце функции, посколькуfunction()
возвращает ее последнее значение. Вы заметите это только во многих повторах функции, где мало что делается внутри, так что стоимостьreturn()
становится большой частью общего времени вычисления функции.Проблема, заключающаяся в том, чтобы не ставить «return» явно в конце, заключается в том, что если добавить один оператор в конец метода, внезапно возвращаемое значение будет неправильным:
Это возвращает значение
dosomething()
.Теперь мы идем на следующий день и добавляем новую строку:
Мы хотели, чтобы наш код возвращал значение
dosomething()
, но вместо этого он больше не делает.С явным возвратом это становится действительно очевидным:
Мы можем видеть, что в этом коде есть что-то странное, и исправить это:
источник
return
- это не ярлык, а правильный стиль в функциональном программировании. Использование ненужныхreturn
вызовов функций является примером программирования культа грузов .return
заявления и не заметить, что оно не будет выполнено. Вы также можете добавить комментарий после значения, которое хотите вернуть, например,dosomething() # this is my return value, don't add anything after it unless you know goddam well what you are doing
Это быстрее, потому что
return
это (примитивная) функция в R, что означает, что ее использование в коде влечет за собой затраты на вызов функции. Сравните это с большинством других языков программирования, гдеreturn
ключевое слово, а не вызов функции: это не приводит к выполнению кода во время выполнения.Тем не менее, вызов примитивной функции таким способом довольно быстр в R, а вызов
return
влечет за собой незначительные накладные расходы. Это не аргумент для пропускаreturn
.Потому что нет никаких причин , чтобы использовать его.
Потому что это избыточно и не добавляет полезной избыточности.
Чтобы было ясно: избыточность иногда может быть полезной . Но большинство избыточности не такого рода. Вместо этого это тот тип, который добавляет визуальный беспорядок без добавления информации: это программный эквивалент слова-заполнителя или чарджанк ).
Рассмотрим следующий пример пояснительного комментария, который повсеместно признается плохой избыточностью, потому что комментарий просто перефразирует то, что код уже выражает:
Использование
return
в R относится к той же категории, потому что R является функциональным языком программирования. , а в R каждый вызов функции имеет значение . Это фундаментальное свойство R. И как только вы видите код R с точки зрения того, что каждое выражение (включая каждый вызов функции) имеет значение, возникает вопрос: «Почему я должен использоватьreturn
?» Должна быть положительная причина, так как по умолчанию она не используется.Одна из таких положительных причин - сигнализировать о досрочном выходе из функции, скажем, в пункте охраны :
Это допустимое, не лишнее использование
return
. Тем не менее, такие защитные положения редки в R по сравнению с другими языками, и так как каждое выражение имеет значение, регулярное выражениеif
не требуетreturn
:Мы можем даже переписать
f
так:... где так
if (cond) expr
же, какif (cond) expr else NULL
.Наконец, я хотел бы предупредить три распространенных возражения:
Некоторые люди утверждают, что использование
return
добавляет ясности, потому что это сигнализирует «эта функция возвращает значение». Но, как объяснено выше, каждая функция возвращает что-то в R. Думая о том,return
что маркер возврата значения не просто избыточен, это активно вводит в заблуждение .Соответственно, дзен питона есть изумительное руководство, которому всегда следует следовать:
Как сбрасывание лишнего
return
не нарушает это? Потому что возвращаемое значение функции в функциональном языке всегда явное: это его последнее выражение. Это опять тот же аргумент о явности против избыточности.Фактически, если вы хотите явной, используйте его, чтобы выделить исключение из правила: помечать функции, которые не возвращают значащее значение, которые вызываются только для их побочных эффектов (таких как
cat
). За исключением R имеет лучший маркер , чемreturn
для этого случая:invisible
. Например, я бы написалНо как насчет длинных функций? Не будет ли легко потерять след того, что возвращается?
Два ответа: во-первых, не совсем. Правило ясно: последним выражением функции является ее значение. Там нет ничего, чтобы отслеживать.
Но что более важно, проблема в длинных функциях не в отсутствии явных
return
маркеров. Это длина функции . Длинные функции почти (?) Всегда нарушают принцип единой ответственности, и даже если они этого не делают, им будет полезно, если они будут разбиты на части для удобства чтения.источник
return
чтобы сделать его более похожим на другие языки. Но это плохой аргумент: другие функциональные языки программирования, как правило,return
тоже не используют . Только императивные языки, где не каждое выражение имеет значение, используют его.return
поддерживает явно лучше, и прочитал ваш ответ с полной критикой. Ваш ответ заставил меня задуматься над этим мнением. Я думаю, что необходимостьreturn
явно использовать (по крайней мере, в моем собственном случае) связана с необходимостью лучше пересматривать мои функции в более поздний момент времени. Поняв, что мои функции просто могут быть слишком сложными, теперь я вижу, что целью улучшения моего стиля программирования было бы стремление структурировать коды, чтобы поддерживать простоту без areturn
. Спасибо за эти размышления и понимание !!Я думаю о
return
трюке. Как правило, значение последнего выражения, вычисленного в функции, становится значением функции - и этот общий шаблон встречается во многих местах. Все следующие оценки до 3:Что
return
на самом деле означает не возвращение значения (это делается с ним или без него), а «прерывание» функции нерегулярным способом. В этом смысле это самый близкий эквивалент оператора GOTO в R (есть также break и next). Я используюreturn
очень редко и никогда в конце функции.... это можно переписать так, чтобы
if(a) a else b
оно было намного лучше читаемым и менее фигурным. Нет необходимостиreturn
здесь вообще. Мой опытный случай использования «возврата» будет что-то вроде ...Как правило, потребность во многих доходах предполагает, что проблема либо уродлива, либо плохо структурирована.
<>
return
на самом деле не нужна функция для работы: вы можете использовать ее, чтобы вырваться из набора выражений для оценки.источник
return
(мой уродливый пример, приведенный выше, очень искусственный): предположим, вам нужно проверить, является ли значениеNULL
илиNA
: в этих случаях возвращает пустую строку, в противном случае возвращаетcharacter
значение. Но проверкаis.na(NULL)
дает ошибку, поэтому похоже, что это можно сделать только с помощью,if(is.null(x)) return("")
а затем продолжить сif(is.na(x)) .....
. (Можно использоватьlength(x)==0
вместо,is.null(x)
но, тем не менее, невозможно использовать,length(x)==0 | is.na(x)
еслиx
естьNULL
.)|
(векторизованное ИЛИ, где оцениваются обе стороны) вместо||
(короткое замыкание ИЛИ, не векторизованное, где предикаты оцениваются по очереди). Рассмотрим вif (TRUE | stop()) print(1)
сравненииif (TRUE || stop()) print(1)
return
может повысить читаемость кода:источник
foo <- function() a || b
(что является IMO более читабельным; в любом случае нет «чистой» читабельности, но читаемость, по мнению кого-то: есть люди, которые говорят, что язык ассемблера отлично читается)Аргумент избыточности много выдвигался здесь. На мой взгляд, это не является достаточной причиной, чтобы опустить
return()
. Избыточность автоматически не является плохой вещью. При стратегическом использовании избыточность делает код более понятным и более понятным.Рассмотрим этот пример: параметры функции часто имеют значения по умолчанию. Поэтому указание значения, которое совпадает со значением по умолчанию, является излишним. За исключением того, что это делает очевидным поведение, которое я ожидаю. Нет необходимости открывать справочную страницу функции, чтобы напомнить себе, каковы настройки по умолчанию. И не беспокойтесь о том, что будущая версия функции изменит свои настройки по умолчанию.
С незначительным
return()
снижением производительности за вызов (согласно оценкам, опубликованным здесь другими), все сводится к стилю, а не к правильному и неправильному. Для того, чтобы что-то было «неправильно», должен быть явный недостаток, и никто здесь не продемонстрировал удовлетворительно, что включение или исключениеreturn()
имеет постоянный недостаток. Кажется, очень зависит от конкретного случая и пользователя.Так вот, где я стою на этом.
Мне неудобно с «бесхозными» переменными, как в примере выше. Был
abcd
будет частью заявления я не закончил писать? Является ли это остатком соединения / редактирования в моем коде, и его нужно удалить? Я случайно вставил / переместил что-то откуда-то еще?Напротив, этот второй пример показывает мне, что это предполагаемое возвращаемое значение, а не какой-то случайный или неполный код. Для меня эта избыточность абсолютно не бесполезна.
Конечно, как только функция закончена и работает, я могу удалить возврат. Но удаление его само по себе является лишним дополнительным шагом и, на мой взгляд, более бесполезным, чем
return()
в первую очередь.Все, что сказано, я не использую
return()
в коротких одноименных функциях. Там он составляет большую часть кода функции и поэтому в основном вызывает визуальный беспорядок, который делает код менее разборчивым. Но для больших формально определенных и именованных функций я использую его и, скорее всего, продолжу.источник
return()
в R ничего не стоит. Это объективно избыточно, но быть «бесполезным» - ваше субъективное суждение. Избыточные и бесполезные не обязательно являются синонимами. Вот где мы не согласны.return
, и хотя я не уверен, что считаю его потенциально действительным (это определенно на императивном языке… я считаю, что это не переводится на функциональные языки).