함수에서 명시 적으로 return 호출


199

얼마 전에 R 핵심 팀의 Simon Urbanek에 의해 책망받았습니다 (사용자가 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과 어떤 관련이 있는지에 대한 자세한 내용을 포함하여 의견을 답변으로 확장 할 수 있습니까?
Paul Hiemstra

2
return로컬이 아닌 점프를 유도하고, 로컬이 아닌 로컬 점프는 FP에서 일반적이지 않습니다. 실제로, 예를 들어 scheme에는이 없습니다 return. 내 의견은 답변으로 너무 짧거나 (아마도 틀릴 수 있음) 생각합니다.
kohske

2
F #은없는 return, break, continue지루한 때때로 인 중 하나.
colinfang

답변:


128

질문 : 왜 (명시 적으로) 호출이 더 빠르거나 더 나은 호출을하지 않는가?

R 문서에는 그러한 가정을하는 진술이 없습니다.
메인 페이지 '기능'은 다음과 같이 말합니다.

function( arglist ) expr
return(value)

return을 호출하지 않고 더 빠릅니까?

모두 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을 호출하지 않고 더 낫습니까?

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 ()을 사용하거나 사용하지 않습니다.

많은 경우 return () 만 사용되며 (인수 없음) 경우에 따라 함수를 중지하기 위해 NULL을 반환합니다.

R을 사용하는 표준 사용자 또는 분석가가 실제 차이를 볼 수 없기 때문에 더 나은지 확실하지 않습니다.

내 의견은 질문은 다음과 같아야한다는 것입니다 : R 구현에서 오는 명시 적 수익을 사용하는 데 위험이 있습니까?

또는, 어쩌면 더 나은 사용자 쓰기 기능 코드를 항상 확인해야합니다 의 효과가 무엇입니까 하지 명시 적으로 반환을 사용하여 (또는 코드 브랜치의 마지막 잎으로 반환 될 객체를 배치) 기능 코드는?


4
좋은 답변 주셔서 감사합니다. 나는 사용하는 데 위험이 없다고 생각하며, 사용 return여부에 관계없이 프로그래머의 선호에 달려 있습니다.
Paul Hiemstra

38
의 속도 return는 실제로 당신이 걱정해야 할 마지막 것입니다.
hadley

2
나는 이것이 나쁜 대답이라고 생각합니다. 그 이유는 불필요한 return함수 호출 사용의 가치에 대한 근본적인 의견 불일치 가 있습니다. 당신 물어봐야 질문은 당신이 마지막에 제안하는 것이 아닙니다. 대신,이다 : "왜 해야 내가 중복을 사용 return? 어떤 이점이 있습니까?” 결과적으로“답지 않은 것”또는“아무것도없는 것”입니다. 귀하의 답장이이 점에 감사하지 않습니다.
Konrad Rudolph

@KonradRudolph ... 당신은 실제로 Paul이 원래 요청한 것을 반복했습니다 (왜 명시적인 반환이 나쁜지). 그리고 나는 올바른 사람 (아무에게도 적합한 것)도 알고 싶습니다 :). 이 사이트의 사용자에게 설명을 제공 하시겠습니까?
Petr Matousu

1
@Dason 나는 다른 곳에서 Reddit 게시물을 연결 했는데이 맥락 에서이 논쟁에 결함이있는 이유를 설명했습니다. 불행히도 주석은 매번 자동으로 제거되는 것으로 보입니다. 짧은 버전은 return코드 한 부분 옆에 "increment x by 1"이라고하는 명시적인 주석과 같습니다 x = x + 2. 다시 말해, 그 명시 성은 (a) 전혀 관련이 없으며 (b) 잘못된 정보를 전달합니다 . 때문에 returnR에서의 의미는 단순히 "이 기능 중지"이다. 다른 언어 와 같은 의미 는 아닙니다return .
Konrad Rudolph

102

모두가 동의하면

  1. return 함수 본문 끝에 필요하지 않습니다
  2. 사용하지 않는 return것이 조금 더 빠릅니다 (@Alan의 테스트에 따르면 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있습니다. 전파하기 위해서는 여러 개의 변수와 복잡한 부울 변수 시스템 이 필요 합니다. 이 모든 것이 단일 반환 규칙이 R과 잘 어울리지 않는다고 말하면 return함수 본문의 일부 위치에서 사용해야 할 경우 일관성이 있고 어디에서나 사용해야합니까?

속도 인수가 유효한 것으로 생각하지 않습니다. 실제로 무언가를 수행하는 함수를 볼 때 0.8 마이크로 초의 차이는 아무 것도 아닙니다. 내가 볼 수있는 마지막 것은 타이핑이 적지 만 게으르지 않다는 것입니다.


7
+1, return@flodel이 보여 주듯이, 어떤 경우 에는 진술 이 분명히 필요합니다 . 또는 리턴 문이 가장 잘 생략되는 상황이 있습니다 (예 : 많은 작은 함수 호출). 다른 경우에는 95 %라고 말하면 실제로 사용하는지 여부는 중요하지 않으며 return선호도에 따라 다릅니다. 나는 당신이 의미하는 바에 더 명시 적이므로 더 읽기 쉽기 때문에 return을 사용하는 것을 좋아합니다. 아마도이 토론은 <-vs =?
Paul Hiemstra

7
이것은 R을 명령형 프로그래밍 언어로 취급하고 있습니다. 기능 프로그래밍 언어는 아닙니다. 함수형 프로그래밍은 단순히 다르게 작동 return하며 값을 반환하는 데는 의미가 없습니다 . if (x == TRUE)대신 쓰기와 동등 if (x)합니다.
Konrad Rudolph

4
당신은 또한 다시 foofoo <- function(x) if (a) a else b(필요에 따라 줄 바꿈 포함). 명시적인 반환 또는 중간 값이 필요하지 않습니다.
hadley

26

이것은 흥미로운 토론입니다. @flodel의 예가 훌륭하다고 생각합니다. 그러나 기능적 코딩 스타일 대신 명령return 을 사용할 때 의미 가있는 내 요점 (@koshke가 주석에서 언급 함)을 보여 줍니다 .

요점은 아니지만 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. 순전히 기능적인 스타일에서 최종 호출은 거의 항상 원하는 반환 값입니다.

파이썬에서 함수에는 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입니다하지만 당신은 다시 썼다 경우 fooifelse

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

이제 foo모든 길이에서 작동합니다 a. 사실, 그것은 a행렬 일 때도 작동 합니다. test문제가 아닌 벡터화에 도움이되는 기능 과 동일한 모양을 반환합니다 .


return기능적인 프로그래밍 스타일에 맞지 않는 이유 는 분명 하지 않습니다. 어느 단계에서 함수 또는 서브 루틴이 무언가를 반환해야하는 것은 필수 또는 기능적으로 프로그래밍하는 것입니다. 예를 들어, 파이썬의 함수형 프로그래밍에는 여전히 return명령문이 필요합니다 . 이 시점에서 더 자세히 설명해 주시겠습니까?
Paul Hiemstra

이 상황에서 사용 ifelse(a,a,b)은 내 애완 동물의 애완 동물입니다. 모든 라인 ?ifelse이 비명을 지르는 것처럼 보입니다 if (a) {a} else b. " 대신에 나를 사용하지 마십시오 ." 예 : "...와 같은 형상의 값을 반환하는 test"경우 " yes또는 no너무 짧아서, 그 요소는 재활용된다.", "결과의 모드의 값에 의존 할 수있다 test"결과의 class 속성 " 에서 가져 test로부터 선택 값에 부적합 할 수있다 yesno"
GSEE

두 번째로 보면 foo말이되지 않습니다. 항상 TRUE 또는를 반환합니다 b. ifelse이것을 사용 하면 1 또는 여러 개의 TRUE 및 / 또는 1 또는 여러 개의을 반환 b합니다. 처음에는 함수의 의도가 "일부 진술이 참이면 무언가를 돌려 주거나 그렇지 않으면 무언가를 돌려주십시오"라고하는 것이라고 생각했습니다. 나는 이것이 벡터화되어야한다고 생각하지 않습니다. 왜냐하면 그것이 참인 어떤 물체의 요소를 돌려주고 참이 아닌 모든 요소에 b
대해을

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)) 로 진행 하고 결과가 반전됩니다 ... 서버에서 시도 할 것입니다.


이 차이가 발생하는 이유에 대해 말씀해 주시겠습니까?
Paul Hiemstra 5

4
@PaulHiemstra Petr 's 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

그러나 이것은 실제 코드에서는 실제로 문제가되지 않으며 순전히 이론적 인 것입니다. 이로 인해 어려움을 겪을 수있는 코드 는 훨씬 더 큰 문제가 있습니다. 명확하지 않은 취 성적 인 코드 흐름은 명백하지 않으므로 간단한 추가로 코드가 깨집니다.
Konrad Rudolph

@KonradRudolph 난 당신이 그것에 트루 스콧 노먼을하고 있다고 생각합니다 ;-) "이것이 당신의 코드에 문제가 있다면, 당신은 나쁜 프로그래머입니다!". 정말 동의하지 않습니다. 나는 당신이 모든 줄을 알고있는 작은 코드 조각을 지름길로 벗어날 수는 있지만 코드가 커지면 다시 물릴 것이라고 생각합니다.
휴 퍼킨스

2
@HughPerkins 그것은 진실한 스코틀랜드 사람이 아니다 ; 오히려 수십 년에 걸친 소프트웨어 엔지니어링 모범 사례로 뒷받침되는 코드 복잡성에 대한 실증적 인 관찰입니다. 개별 기능을 짧게 유지하고 코드 흐름을 명확하게 유지하십시오. 생략 return은 지름길이 아니며 함수형 프로그래밍에서 올바른 스타일입니다. 불필요한 return함수 호출을 사용 하는 것은 화물 컬트 프로그래밍 의 한 예입니다 .
Konrad Rudolph

글쎄 ... 나는 이것이 당신이 당신의 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빨리 전화하지 않는

returnR의 (기본) 함수 이기 때문에 더 빠릅니다. 즉, 코드에서 함수를 사용하면 함수 호출 비용이 발생합니다. 이것을 대부분의 다른 프로그래밍 언어와 비교하십시오.return 키워드이지만 함수 호출이 아닌 . 런타임 코드 실행으로 변환되지 않습니다.

즉, 이러한 방식으로 원시 함수를 호출하는 것은 R에서 매우 빠르며 호출 return은 약간의 오버 헤드가 발생합니다. 이것은 생략에 대한 주장이 아닙니다 return.

또는 더 나은, 따라서 바람직합니까?

그것을 사용할 이유 없기 때문에 .

중복되어 유용 하지 않기 때문에 중복을 입니다.

명확하게하기 위해 : 중복성 때때로 유용 할 수 있습니다 . 그러나 대부분의 중복성은 이런 종류가 아닙니다. 대신 정보를 추가하지 않고 시각적 혼란 을 더하는 종류 입니다. 필러 단어 또는 차트 정크 와 같은 프로그래밍입니다. ).

주석은 코드가 이미 표현한 것을 역설하기 때문에 일반적으로 잘못된 중복으로 인식되는 설명 주석의 다음 예를 고려하십시오.

# Add one to the result
result = x + 1

사용 returnR이 있기 때문에 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. 관련하여, Zen of Python 에는 항상 따라야 할 놀라운 지침이 있습니다.

    암시적인 것보다 명시적인 것이 좋습니다.

    중복 제거가 어떻게 return이를 위반 하지 않습니까? 함수형 언어에서 함수의 반환 값은 항상 명시 적이므로 마지막 식입니다. 이것은 다시 명시 성에 대한 동일한 주장입니다. 중복성 입니다.

    사실, 명확성을 원한다면 예외를 규칙에 강조 표시하십시오 . 의미있는 값을 반환 하지 않는 부수 함수 (예 :)는 부작용으로 인해 호출됩니다 cat. R을 제외하고는 return이 경우 보다 마커가 더 좋습니다 invisible. 예를 들어

    save_results = function (results, file) {
        # … code that writes the results to a file …
        invisible()
    }
  3. 그러나 긴 기능은 어떻습니까? 반품 내용을 잃어 버리기 쉽지 않습니까?

    두 가지 대답 : 첫째, 실제로는 아닙니다. 규칙은 분명하다 : 마지막 표현 합니다. 함수 은 그 값입니다. 추적 할 내용이 없습니다.

    그러나 더 중요한 것은 긴 함수의 문제는 명확한 return마커 가 없다는 것입니다 . 함수길이입니다 . 긴 기능은 거의 (?) 거의 단일 책임 원칙을 위반하며 , 그렇지 않은 경우에도 가독성을 위해 분리되는 이점을 누릴 수 있습니다.


어쩌면 어떤 사람들은 return다른 언어와 더 비슷하게 사용 하기 위해 옹호한다고 덧붙여 야 할 것입니다. 그러나 이것은 잘못된 주장입니다. 다른 함수형 프로그래밍 언어도 사용하지 않는 경향이 return있습니다. 모든 표현에 가치가있는 것은 아니며, 그것을 사용하는 것은 명령형 언어 일뿐 입니다.
Konrad Rudolph

나는이 질문에 return명시 적으로 지원하는 것이 더 좋다는 의견으로 왔으며 완전한 비판으로 대답을 읽습니다. 당신의 대답으로 저의 견해를 반영하게되었습니다. 나는 return(적어도 내 경우에는) 명시 적으로 사용해야 할 필요성이 나중에 내 기능을 더 잘 수정할 수 있어야한다고 생각합니다. 내 함수가 너무 복잡하다는 생각으로 이제는 프로그래밍 스타일을 향상시키는 목표가 코드없이을 명시 적으로 유지하도록 구성하는 데 노력하고 있음을 알 수 있습니다 return. 그 성찰과 통찰에 감사드립니다 !!
Kasper Thystrup Karstensen

6

return트릭 이라고 생각 합니다. 일반적으로 함수에서 평가 된 마지막 표현식의 값은 함수의 값이되며이 일반적인 패턴은 여러 곳에서 찾을 수 있습니다. 다음은 모두 3으로 평가됩니다.

local({
1
2
3
})

eval(expression({
1
2
3
}))

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

어떤 return일은 정말 아닌 반환 (이 함께 또는없이 완료) 값을하지만 불규칙한 방식으로 기능의 "탈옥". 그런 의미에서, R에서 GOTO 문과 가장 비슷합니다 (단, 다음이 있습니다). 나는 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)if 인 경우 x에는 사용할 수 없습니다 NULL.)
lebatsnok

1
그 이유는 |(단순화가 차례로 평가되는 ||단락 회로 OR, 벡터화되지 않음 ) 대신 ( 양쪽이 평가되는 벡터화 된 OR) 을 사용했기 때문 입니다. 고려 if (TRUE | stop()) print(1)if (TRUE || stop()) print(1)
ASAC

2

return 코드 가독성을 높일 수 있습니다.

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

3
아마 그럴 수도 있습니다. 그러나 귀하의 예에서는 그렇지 않습니다. 대신 코드 흐름이 모호해 지거나 복잡해집니다.
Konrad Rudolph

1
당신의 기능을 단순화 할 수 있습니다 : foo <- function() a || b(IMO 더 읽을 수있는, 어떤 경우에, 다른 사람의 의견에는 "순수"가독성하지만 가독성이 없다 : 어셈블리 언어를 완벽하게 읽을 수 말하는 사람들이 있습니다)
lebatsnok

1

여기에서 중복성에 대한 논쟁이 많이 일어났습니다. 내 생각에 그것은 생략하기에 충분한 이유가 아니다 return(). 중복성이 자동으로 나쁜 것은 아닙니다. 전략적으로 사용될 때 중복성은 코드를보다 명확하고 유지 보수 할 수있게합니다.

다음 예제를 고려하십시오. 함수 매개 변수에는 종종 기본값이 있습니다. 따라서 기본값과 동일한 값을 지정하면 중복됩니다. 그것을 제외하고는 내가 기대하는 행동을 분명히합니다. 기본값을 알려주기 위해 함수 맨 페이지를 불러올 필요가 없습니다. 그리고 향후 버전의 기능이 기본값을 변경하는 것에 대해 걱정할 필요가 없습니다.

return()(다른 사람들이 게시 한 벤치 마크에 따라) 호출에 대한 무시할만한 성능 페널티 로 인해 옳고 그름이 아닌 스타일이 결정됩니다. 무언가가 "잘못"되기 위해서는 분명한 단점이 있어야하며, 여기에 포함하거나 생략 return()하는 것이 일관성있는 단점을 가지고 있다는 것을 만족스럽게 입증 한 사람은 없습니다 . 대소 문자와 사용자마다 매우 다릅니다.

여기 제가 여기 서 있습니다.

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

위의 예와 같이 "고아"변수가 불편합니다. abcd제가 글을 끝내지 않은 성명서의 일부 가 될 예정입니까? 내 코드에서 스플 라이스 / 편집의 잔재이며 삭제해야합니까? 실수로 다른 곳에서 붙여 넣기 / 이동 했습니까?

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

이와 대조적으로,이 두 번째 예는 사고 나 불완전한 코드가 아니라 의도 된 반환 값임을 분명히 보여줍니다. 나 에게이 중복성은 절대 쓸모가 없습니다.

물론 함수가 완료되고 작동하면 반환을 제거 할 수 있습니다. 그러나 그것을 제거하는 것은 그 자체로 여분의 여분 단계이며, 제 생각 return()에는 처음에 포함 시키는 것보다 더 쓸모가 없습니다 .

말 그대로, 나는 return()이름이없는 단일 라이너 기능을 짧게 사용하지 않습니다 . 거기에서 함수 코드의 상당 부분을 구성하므로 코드를 쉽게 읽을 수 없게 만드는 시각적 혼란이 발생합니다. 그러나 공식적으로 정의되고 명명 된 더 큰 기능의 경우이 기능을 사용하며 계속 사용할 것입니다.


"abcd는 제가 글을 끝내지 않은 성명서의 일부가 되었습니까?" — 이것은 여러분이 쓰는 다른 표현 과 어떻게 다릅니 까? 이것이 우리 의견 불일치의 핵심이라고 생각합니다. 자체적으로 변수 스탠드를 갖는 것은 명령형 프로그래밍 언어에서는 독창적이지만 기능적 프로그래밍 언어에서는 완전히 정상적입니다. 제가 주장하는 문제는 단순히 함수형 프로그래밍에 익숙하지 않다는 것입니다.
Konrad Rudolph

할당, 비교, 함수 호출입니다. 첫 번째 코딩 단계는 명령형 언어로되어 있으며 여전히 명령형 언어를 사용합니다. 여러 언어에 걸쳐 균일 한 시각적 신호를 가짐으로써 (언어가 허용하는 모든 곳에서) 작업이 쉬워집니다. return()R in A 는 비용이 들지 않습니다. 객관적으로 중복되지만 "무용지물"은 주관적인 판단입니다. 중복적이고 쓸모없는 것이 반드시 동의어 일 필요는 없습니다. 그것이 우리가 동의하지 않는 곳입니다.
cymon

또한 저는 소프트웨어 엔지니어 나 컴퓨터 과학자가 아닙니다. 용어 사용에 대해 뉘앙스를 너무 많이 읽지 마십시오.
cymon

“중복적이고 쓸모없는 것이 반드시 동의어 일 필요는 없습니다. 그것이 우리가 동의하지 않는 곳입니다.” — 아니오, 전적으로 동의합니다. 제 답변에서 그 점을 분명히 밝혔습니다. 이중화는 도움이되거나 중요 할 수 있습니다 . 그러나 이것은 가정 된 것이 아니라 적극적으로 보여 져야합니다. 나는 이것이 당신이 생각하는 이유에 대한 당신의 주장을 이해하며 return, 그것이 확실하지 않다고 생각하더라도 잠재적으로 유효하다고 생각합니다 (확실히 명령형 언어로되어 있습니다 ... 내 생각에 그것이 기능적 언어로 번역되지 않는다는 것입니다).
Konrad Rudolph
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.