Явно викликає повернення у функцію чи ні


199

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

foo = function() {
  return(value)
}

натомість він рекомендував:

foo = function() {
  value
}

Можливо, у такій ситуації потрібно:

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

Його коментар пролив трохи світла на те, чому б не зателефонувати, returnякщо суворо не потрібно - це добре, але це було видалено.

Моє запитання: Чому не телефонувати returnшвидше чи краще, і, таким чином, краще?


12
returnє непотрібним навіть в останньому прикладі. Видалення returnможе зробити його швидше трохи, але, на мій погляд, це тому, що R, як кажуть, є функціональною мовою програмування.
kohske

4
@kohske Чи можете ви розширити свій коментар у відповідь, зокрема детальніше про те, чому це швидше, і як це пов'язано з тим, що R є функціональною мовою програмування?
Пол Хіемстра

2
returnіндукує не локальний стрибок, а явний не локальний стрибок незвичний для FP. Насправді, наприклад, схеми немає return. Я вважаю, що мої коментарі є занадто короткими (а можливо, неправильними) як відповідь.
kohske

2
F # не має return, break, continueабо, що виснажливі іноді.
colinfang

Відповіді:


128

Питання: Чому (явно) виклик не повертається швидше чи краще, і, таким чином, краще?

У документації на 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 (), оскільки це не потрібно.

Основні програмісти використовують обидва підходи, тобто. з явним поверненням () і без нього, як це можливо знайти в джерелах функцій 'base'.

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

Незрозуміло, краще чи ні, оскільки стандартний користувач чи аналітик, що використовує R, не можуть бачити реальної різниці.

На мою думку, питання повинно бути таким: чи існує якась небезпека використання явного повернення, що випливає з реалізації R?

Або, може бути , краще, написання коду користувача функція завжди повинні запитати: Що таке ефект НЕ використовуючи явний повернення (або розміщення об'єкта повинен бути повернутий в якості останнього листа коду філії) в коді функції?


4
Дякую за дуже гарну відповідь. Я вважаю, що небезпеки у використанні немає return, і це зводиться до переваг програміста, використовувати його чи ні.
Пол Хіемстра

38
Швидкість - returnце дійсно останнє, про що ви повинні турбуватися.
хадлі

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

@KonradRudolph ... ти повторив насправді те, що Павло спочатку запитував (чому явне повернення погано). І я хотів би також знати правильну (таку, яка потрібна для когось) відповідь :). Чи вважаєте ви надати пояснення користувачам цього веб-сайту?
Петро Матусу

1
@Dason Я в іншому місці пов’язав пост у Reddit, пояснюючи, чому цей аргумент є недоліком у цьому контексті. На жаль, коментар видаляється автоматично кожного разу. Коротка версія - returnце як явний коментар, який говорить "збільшення x на 1", поруч із фрагментом коду x = x + 2. Іншими словами, його явність (а) абсолютно не має значення, і (б) вона передає неправильну інформацію. Оскільки returnсемантика в R є, суто, «перервати цю функцію». Це не означає те саме, що і returnв інших мовах.
Конрад Рудольф

102

Якщо всі згодні з цим

  1. return не потрібно в кінці тіла функції
  2. використання не returnє незначно швидшим (згідно з тестом @ Алана, 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 та складна система булевих змінних. Все це говорить про те, що правило єдиного повернення не дуже добре відповідає Р. Отже, якщо вам потрібно буде використовувати returnв деяких місцях тіла вашої функції, чому б не бути послідовним і використовувати його скрізь?

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


7
+1, returnв деяких випадках очевидна потреба у заяві, як показало @flodel. Крім того, існують ситуації, коли оператор зворотного зв'язку найкраще опускати, наприклад, багато та безліч викликів невеликих функцій. У всьому іншому, скажімо, 95% випадків, це не має особливого значення, користується він returnчи ні, і це зводиться до переваги. Мені подобається використовувати return, оскільки він більш чіткий у тому, що ви маєте на увазі, таким чином, більш читабельний. Можливо, ця дискусія схожа на <-vs =?
Пол Хіемстра

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не має великого сенсу; вона завжди поверне ІСТИНА або b. Використовуючи ifelseйого, повернеться 1 або кілька ІСТИНИ, та / або 1 або кілька bс. Спочатку я думав, що метою функції було сказати: "якщо якесь твердження є ПРАВИЛЬНИМ, поверніть щось, інакше поверніть щось інше". Я не думаю , що має бути векторизації, тому що тоді він став би «повернути елементи якого - або об'єкта , які є істинними, і для всіх елементів, які не є TRUE, повернення 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

____ EDIT__ _ __ _ __ _ __ _ __ _ ___

Я переходжу до інших бенчмарк ( benchmark(fuu(x),foo(x),replications=1e7)), і результат зворотний ... Я спробую на сервері.


Чи можете ви прокоментувати причину, чому виникає така різниця?
Пол Хіемстра

4
@PaulHiemstra Петрова відповідь охоплює одну з основних причин цього; два дзвінки під час використання 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.

чи краще, і таким чином кращим?

Тому що немає підстав для цього використовувати його.

Тому що це зайве і не додає корисного надмірності.

Щоб було зрозуміло: надмірність іноді може бути корисною . Але більшість надмірностей не такого роду. Натомість він має таку форму, яка додає візуального безладу без додавання інформації: це програмний еквівалент слова наповнювача або charterjunk ).

Розглянемо наступний приклад пояснювального коментаря, який загальновизнаний як погана надмірність, оскільки коментар лише перефразовує те, що вже виражається в коді:

# 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додає ясність, оскільки це сигналізує "ця функція повертає значення". Але як пояснено вище, кожна функція повертає щось у Р. Мислення returnяк маркера повернення значення не просто зайве, воно активно вводить в оману .

  2. Так само, Дзен Пітона є чудовим керівництвом, якого слід завжди дотримуватися:

    Явне краще, ніж неявне.

    Як відкидання зайвого returnне порушує цього? Оскільки повернене значення функції у функціональній мові завжди явне: це останнє вираження. Це знову той самий аргумент щодо явності vs надмірності.

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

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

    Дві відповіді: по-перше, не дуже. Правило зрозуміле: останній вираз функції є її значення. Немає чого слідкувати.

    Але ще важливіше, що проблема довгих функцій - не відсутність явних returnмаркерів. Це тривалість функції . Довгі функції майже (?) Завжди порушують принцип єдиної відповідальності і навіть тоді, коли вони цього не роблять, вони виграють від розбиття за читабельність.


Можливо, варто додати, що деякі люди виступають за використання, returnщоб зробити його більш схожим на інші мови. Але це поганий аргумент: інші функціональні мови програмування зазвичай не використовують returnжодної. Використовують лише імперативні мови, де не кожен вираз має значення.
Конрад Рудольф

Я прийшов до цього питання з думкою, що користуватися returnпідтримками явно краще, і прочитав вашу відповідь з повною критикою. Ваша відповідь змусила мене задуматися над цим поглядом. Я думаю, що потреба використовувати returnявно (принаймні, в моєму випадку) пов'язана з потребою краще переглядати свої функції в більш пізній момент часу. З урахуванням того, що мої функції просто можуть бути занадто складними, я тепер бачу, що метою покращення мого стилю програмування було б прагнення до структуризації кодів для підтримки чіткості без return. Дякую за роздуми та прозріння !!
Каспер Тіструп Карстенсен

6

Я думаю про 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")
   }

Як правило, потреба у багатьох поверненнях говорить про те, що проблема або некрасива, або погано структурована.g

<>

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)
asac

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 бути частиною заяви, яку я не закінчив писати?" - Чим це відрізняється від будь-якого іншого виразу, який ви пишете? Це, я думаю, є стрижнем нашої незгоди. Наявність самостійної змінної позиції може бути властивою імперативній мові програмування, але це абсолютно нормально і очікується у функціональній мові програмування. Я запевняю, проблема полягає лише в тому, що ви не знайомі з функціональним програмуванням (підкріплює це той факт, що ви говорите про "заяви" замість "виразів").
Конрад Рудольф

Це інакше, тому що кожне інше твердження зазвичай робить щось більш очевидним чином: це призначення, порівняння, виклик функції ... Так, перші мої кроки кодування були в імперативних мовах, і я все ще використовую імперативні мови. Наявність рівномірних візуальних підказок на різних мовах (там, де мови дозволяють) полегшує мою роботу. A return()в R нічого не коштує. Це об'єктивно зайве, але бути "марним" - це ваше суб'єктивне судження. Надлишки та марність - це не обов'язково синоніми. ТО, де ми не згодні.
цимон

Крім того, я не є інженером програмного забезпечення та не вченим. Не читайте занадто багато нюансів у моєму використанні термінології.
цимон

Просто для уточнення: «Надлишки та марність - це не обов'язково синоніми. ТОМУ, де ми не згодні ». - Ні, я повністю з цим погоджуюся, і я чітко висловив це у своїй відповіді. Скорочення може бути корисним або навіть вирішальним . Але це потрібно активно показувати, а не припускати. Я розумію ваш аргумент, чому ви вважаєте, що це справедливо return, і хоча я не переконаний, я думаю, що це потенційно справедливо (це, безумовно, є необхідною мовою ... я вважаю, що це не перекладається на функціональні мови).
Конрад Рудольф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.