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


199

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

foo = function() {
  return(value)
}

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

foo = function() {
  value
}

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

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

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

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


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

4
@kohske Не могли бы вы расширить свой комментарий в ответ, включая более подробную информацию о том, почему он быстрее, и как это связано с тем, что R является функциональным языком программирования?
Пол Химстра

2
returnвызывает нелокальный скачок, и явный нелокальный скачок необычен для FP. Собственно, например, схемы нет return. Я думаю, что мои комментарии слишком короткие (и, возможно, неправильные) в качестве ответа.
Кохске

2
F # не имеет return, break, continueлибо, что утомительные иногда.
colinfang

Ответы:


129

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

В документации на R нет такого предположения.
Главная страница? 'Функция' говорит:

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
> 

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

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

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

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

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

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


4
Спасибо за очень хороший ответ. Я полагаю, что в использовании нет никакой опасности return, и все зависит от предпочтения программиста, использовать его или нет.
Пол Химстра

38
Скорость returnдействительно последняя вещь, о которой вы должны беспокоиться.
хэдли

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

@KonradRudolph ... вы на самом деле повторили то, о чем первоначально спрашивал Пол (почему явное возвращение плохо). И я хотел бы знать правильный (единственный, подходящий для всех) ответ, а также :). Считаете ли вы, чтобы предоставить свое объяснение для пользователей этого сайта?
Петр Матоусу

1
@Dason Я в другом месте связывал пост Reddit, объясняющий, почему этот аргумент некорректен в этом контексте. К сожалению, комментарий автоматически удаляется каждый раз. Короче говоря, returnэто похоже на явный комментарий с надписью «увеличение х на 1» рядом с выполнением фрагмента кода x = x + 2. Другими словами, его явность (а) совершенно не имеет значения, и (б) он передает неверную информацию. Потому returnчто семантика в R - это просто «прервать эту функцию». Это не означает то же самое, что и returnв других языках.
Конрад Рудольф

103

Если все согласны с этим

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

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

Моя главная проблема, связанная с неиспользованием, 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 микросекунды - ничто, когда вы начинаете смотреть на функции, которые действительно что-то делают. Последнее, что я вижу, это то, что он печатает меньше, но я не ленивый.


7
+1, returnв некоторых случаях явно требуется утверждение, как показало @flodel. В качестве альтернативы, есть ситуации, когда оператор return лучше всего опускать, например, много-много мелких вызовов функций. Во всех других, скажем, 95%, случаях не имеет значения, используете ли вы его returnили нет, и все сводится к предпочтениям. Мне нравится использовать return, так как он более понятен в том, что вы имеете в виду, и, следовательно, более читабелен. Может быть, это обсуждение сродни <-против =?
Пол Химстра

7
Это рассматривает R как императивный язык программирования, а это не так: это функциональный язык программирования. Функциональное программирование просто работает по-другому, и использование returnдля возврата значения бессмысленно, наравне с записью if (x == TRUE)вместо if (x).
Конрад Рудольф

4
Вы также переписываете fooкак foo <- function(x) if (a) a else b(с необходимыми разрывами строки). Нет необходимости явного возврата или промежуточного значения.
хэдли

26

Это интересная дискуссия. Я думаю, что пример @ 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матрица. Возврат значения той же формы, что testи у функции, которая помогает с векторизацией, не проблема.


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

В этой ситуации я использую ifelse(a,a,b)любимую мозоль. Кажется, что каждая строка ?ifelseкричит: «Не используйте меня вместо if (a) {a} else b». Например, «... возвращает значение с той же формой, что и test», «если yesили noслишком короткие, их элементы перерабатываются.», «режим результата может зависеть от значения test», «атрибут класса результата взято из testи может быть неподходящим для значений, выбранных из yesи no"
GSee

На второй взгляд, fooне имеет особого смысла; он всегда вернет TRUE или b. ifelseЕго использование вернет 1 или несколько ИСТИНА и / или 1 или несколько bс. Первоначально я думал, что целью функции было сказать «если какое-то утверждение истинно, вернуть что-то, иначе вернуть что-то другое». Я не думаю, что это должно быть векторизовано, потому что тогда это станет «возвращать элементы некоторого объекта, которые ИСТИНА, и для всех элементов, которые не ИСТИНА, возвращать b.
GSee

22

Кажется, без 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)), и результат меняется на противоположный ... Я попробую на сервере.


Не могли бы вы прокомментировать причину, по которой возникает эта разница?
Пол Химстра

4
@PaulHiemstra Petr Answer охватывает одну из основных причин этого; два звонка при использовании return(), один, если нет. Он полностью избыточен в конце функции, поскольку function()возвращает ее последнее значение. Вы заметите это только во многих повторах функции, где мало что делается внутри, так что стоимость return()становится большой частью общего времени вычисления функции.
Гэвин Симпсон

13

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

foo <- function() {
    dosomething()
}

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

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

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

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

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

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

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

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

1
да, на самом деле я считаю, что явный return () полезен при отладке; как только код очищен, его потребность становится менее неотразимой, и я предпочитаю элегантность его отсутствия ...
PatrickT

Но на самом деле это не проблема в реальном коде, это чисто теоретический подход. Код, который может пострадать от этого, имеет гораздо большую проблему: непонятный, хрупкий поток кода, который настолько неочевиден, что простые добавления ломают его.
Конрад Рудольф

@KonradRudolph Я думаю, что вы вроде как делаете на ней шотландца «Нет-Истинный» ;-) «Если это проблема в вашем коде, вы плохой программист!». Я действительно не согласен. Я думаю, что в то время как вы можете с легкостью урезать небольшие фрагменты кода, где вы знаете каждую строчку наизусть, это вернется, чтобы укусить вас, когда ваш код станет больше.
Хью Перкинс

2
@HughPerkins Это не настоящий шотландец ; скорее это эмпирическое наблюдение за сложностью кода, подкрепленное десятилетиями передовой практики разработки программного обеспечения: держать отдельные функции короткими и поток кода очевидным. И опускание return- это не ярлык, а правильный стиль в функциональном программировании. Использование ненужных returnвызовов функций является примером программирования культа грузов .
Конрад Рудольф

Ну ... я не понимаю, как это мешает вам добавить что-то после вашего returnзаявления и не заметить, что оно не будет выполнено. Вы также можете добавить комментарий после значения, которое хотите вернуть, например,dosomething() # this is my return value, don't add anything after it unless you know goddam well what you are doing
lebatsnok

10

Мой вопрос: почему не звонит returnбыстрее

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

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

или лучше и при этом предпочтительнее?

Потому что нет никаких причин , чтобы использовать его.

Потому что это избыточно и не добавляет полезной избыточности.

Чтобы было ясно: избыточность иногда может быть полезной . Но большинство избыточности не такого рода. Вместо этого это тот тип, который добавляет визуальный беспорядок без добавления информации: это программный эквивалент слова-заполнителя или чарджанк ).

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

# Add one to the result
result = x + 1

Использование returnв R относится к той же категории, потому что R является функциональным языком программирования. , а в R каждый вызов функции имеет значение . Это фундаментальное свойство R. И как только вы видите код R с точки зрения того, что каждое выражение (включая каждый вызов функции) имеет значение, возникает вопрос: «Почему я должен использовать return?» Должна быть положительная причина, так как по умолчанию она не используется.

Одна из таких положительных причин - сигнализировать о досрочном выходе из функции, скажем, в пункте охраны :

f = function (a, b) {
    if (! precondition(a)) return() # same as `return(NULL)`!
    calculation(b)
}

Это допустимое, не лишнее использование return . Тем не менее, такие защитные положения редки в R по сравнению с другими языками, и так как каждое выражение имеет значение, регулярное выражение ifне требует return:

sign = function (num) {
    if (num > 0) {
        1
    } else if (num < 0) {
        -1
    } else {
        0
    }
}

Мы можем даже переписать fтак:

f = function (a, b) {
    if (precondition(a)) calculation(b)
}

... где так if (cond) exprже, как if (cond) expr else NULL.

Наконец, я хотел бы предупредить три распространенных возражения:

  1. Некоторые люди утверждают, что использование returnдобавляет ясности, потому что это сигнализирует «эта функция возвращает значение». Но, как объяснено выше, каждая функция возвращает что-то в R. Думая о том, returnчто маркер возврата значения не просто избыточен, это активно вводит в заблуждение .

  2. Соответственно, дзен питона есть изумительное руководство, которому всегда следует следовать:

    Явное лучше, чем неявное.

    Как сбрасывание лишнего returnне нарушает это? Потому что возвращаемое значение функции в функциональном языке всегда явное: это его последнее выражение. Это опять тот же аргумент о явности против избыточности.

    Фактически, если вы хотите явной, используйте его, чтобы выделить исключение из правила: помечать функции, которые не возвращают значащее значение, которые вызываются только для их побочных эффектов (таких как cat). За исключением R имеет лучший маркер , чем returnдля этого случая: invisible. Например, я бы написал

    save_results = function (results, file) {
        # … code that writes the results to a file …
        invisible()
    }
  3. Но как насчет длинных функций? Не будет ли легко потерять след того, что возвращается?

    Два ответа: во-первых, не совсем. Правило ясно: последним выражением функции является ее значение. Там нет ничего, чтобы отслеживать.

    Но что более важно, проблема в длинных функциях не в отсутствии явных returnмаркеров. Это длина функции . Длинные функции почти (?) Всегда нарушают принцип единой ответственности, и даже если они этого не делают, им будет полезно, если они будут разбиты на части для удобства чтения.


Может быть, я должен добавить, что некоторые люди рекомендуют использовать его, returnчтобы сделать его более похожим на другие языки. Но это плохой аргумент: другие функциональные языки программирования, как правило, returnтоже не используют . Только императивные языки, где не каждое выражение имеет значение, используют его.
Конрад Рудольф

Я пришел к этому Вопросу с мнением, что использование returnподдерживает явно лучше, и прочитал ваш ответ с полной критикой. Ваш ответ заставил меня задуматься над этим мнением. Я думаю, что необходимость returnявно использовать (по крайней мере, в моем собственном случае) связана с необходимостью лучше пересматривать мои функции в более поздний момент времени. Поняв, что мои функции просто могут быть слишком сложными, теперь я вижу, что целью улучшения моего стиля программирования было бы стремление структурировать коды, чтобы поддерживать простоту без a return. Спасибо за эти размышления и понимание !!
Каспер Тиструп Карстенсен

6

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

local({
1
2
3
})

eval(expression({
1
2
3
}))

(function() {
1
2
3
})()

Что returnна самом деле означает не возвращение значения (это делается с ним или без него), а «прерывание» функции нерегулярным способом. В этом смысле это самый близкий эквивалент оператора GOTO в R (есть также break и next). Я использую 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(мой уродливый пример, приведенный выше, очень искусственный): предположим, вам нужно проверить, является ли значение 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.)
lebatsnok

1
Это потому, что вы использовали |(векторизованное ИЛИ, где оцениваются обе стороны) вместо ||(короткое замыкание ИЛИ, не векторизованное, где предикаты оцениваются по очереди). Рассмотрим в if (TRUE | stop()) print(1)сравненииif (TRUE || stop()) print(1)
ГАГА

2

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

foo <- function() {
    if (a) return(a)       
    b     
}

3
Может быть, это так. Но это не делает это в вашем примере. Вместо этого он затеняет (или, скорее, усложняет) поток кода.
Конрад Рудольф

1
Ваша функция может быть упрощена до: foo <- function() a || b(что является IMO более читабельным; в любом случае нет «чистой» читабельности, но читаемость, по мнению кого-то: есть люди, которые говорят, что язык ассемблера отлично читается)
lebatsnok

1

Аргумент избыточности много выдвигался здесь. На мой взгляд, это не является достаточной причиной, чтобы опустить return(). Избыточность автоматически не является плохой вещью. При стратегическом использовании избыточность делает код более понятным и более понятным.

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

С незначительным return()снижением производительности за вызов (согласно оценкам, опубликованным здесь другими), все сводится к стилю, а не к правильному и неправильному. Для того, чтобы что-то было «неправильно», должен быть явный недостаток, и никто здесь не продемонстрировал удовлетворительно, что включение или исключение return()имеет постоянный недостаток. Кажется, очень зависит от конкретного случая и пользователя.

Так вот, где я стою на этом.

function(){
  #do stuff
  ...
  abcd
}

Мне неудобно с «бесхозными» переменными, как в примере выше. Был abcdбудет частью заявления я не закончил писать? Является ли это остатком соединения / редактирования в моем коде, и его нужно удалить? Я случайно вставил / переместил что-то откуда-то еще?

function(){
  #do stuff
  ...
  return(abdc)
}

Напротив, этот второй пример показывает мне, что это предполагаемое возвращаемое значение, а не какой-то случайный или неполный код. Для меня эта избыточность абсолютно не бесполезна.

Конечно, как только функция закончена и работает, я могу удалить возврат. Но удаление его само по себе является лишним дополнительным шагом и, на мой взгляд, более бесполезным, чем return()в первую очередь.

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


«Будет ли abcd частью заявления, которое я не закончил писать?» - Чем это отличается от любого другого выражения, которое ты пишешь? Это, я думаю, является основой нашего разногласия. Наличие самостоятельной переменной может быть необычным для императивного языка программирования, но это совершенно нормально и ожидаемо для функционального языка программирования. Я утверждаю, что проблема в том, что вы не знакомы с функциональным программированием (тот факт, что вы говорите об «утверждениях» вместо «выражений», подтверждает это).
Конрад Рудольф

Это отличается, потому что каждый другой оператор обычно делает что-то более очевидным образом: это присваивание, сравнение, вызов функции ... Да, мои первые шаги кодирования были на императивных языках, и я все еще использую императивные языки. Наличие одинаковых визуальных подсказок на разных языках (где это позволяют языки) облегчает мою работу. А return()в R ничего не стоит. Это объективно избыточно, но быть «бесполезным» - ваше субъективное суждение. Избыточные и бесполезные не обязательно являются синонимами. Вот где мы не согласны.
Cymon

Кроме того, я не программист и не программист. Не читайте слишком много нюансов в моем использовании терминологии.
Cymon

Просто чтобы уточнить: «Избыточное и бесполезное не обязательно синонимы. Вот где мы не согласны. - Нет, я полностью согласен с этим, и я прямо указал на это в своем ответе. Избыточность может быть полезной или даже критической . Но это нужно активно показывать, а не предполагать. Я понимаю ваш аргумент о том, почему вы считаете, что это справедливо return, и хотя я не уверен, что считаю его потенциально действительным (это определенно на императивном языке… я считаю, что это не переводится на функциональные языки).
Конрад Рудольф
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.