IT story

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

hot-time 2020. 5. 16. 10:19
반응형

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


얼마 전 나는 R 핵심 팀의 Simon Urbanek에 의해 책망을 받았다. (사용자 return는 함수가 끝날 때 명시 적으로 호출하도록 권장했다 .

foo = function() {
  return(value)
}

대신 그는 추천했다 :

foo = function() {
  value
}

아마도 이와 같은 상황에서는 다음이 필요합니다.

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

그의 의견 return은 꼭 필요한 경우가 아니라면 전화 하지 않는 이유에 대해 약간의 설명을 밝혔 지만 이것은 삭제되었다.

내 질문은 : 왜 return더 빨리 또는 더 나은 전화를하지 않는 이유는 무엇 입니까?


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

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 구현에서 오는 명시 적 수익을 사용하는 데 위험이 있습니까?

또는 함수 코드를 작성하는 사용자는 항상 다음과 같이 질문해야합니다. 함수 코드 에서 명시 적 리턴을 사용 하지 않거나 코드 분기의 마지막 리프로 리턴 될 오브젝트를 배치 하면 어떤 영향이 있습니까?


모두가 동의하면

  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 마이크로 초의 차이는 아무 것도 아닙니다. 내가 볼 수있는 마지막 것은 타이핑이 적지 만 게으르지 않다는 것입니다.


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


이것은 흥미로운 토론입니다. @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이 끝날 때 하나의 명령문 만있을 수 있습니다.

Using an example from another StackOverflow post, let us say we wanted a function that returned TRUE if all the values in a given x had an odd length. We could use two styles:

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

In a functional style, the value to be returned naturally falls at the ends of the function. Again, it looks more like a mathematical function.

@GSee The warnings outlined in ?ifelse are definitely interesting, but I don't think they are trying to dissuade use of the function. In fact, ifelse has the advantage of automatically vectorizing functions. For example, consider a slightly modified version of foo:

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

This function works fine when length(a) is 1. But if you rewrote foo with an ifelse

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

Now foo works on any length of a. In fact, it would even work when a is a matrix. Returning a value the same shape as test is a feature that helps with vectorization, not a problem.


A problem with not putting 'return' explicitly at the end is that if one adds additional statements at the end of the method, suddenly the return value is wrong:

foo <- function() {
    dosomething()
}

This returns the value of dosomething().

Now we come along the next day and add a new line:

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

We wanted our code to return the value of dosomething(), but instead it no longer does.

With an explicit return, this becomes really obvious:

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

We can see that there is something strange about this code, and fix it:

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

I think of return as a trick. As a general rule, the value of the last expression evaluated in a function becomes the function's value -- and this general pattern is found in many places. All of the following evaluate to 3:

local({
1
2
3
})

eval(expression({
1
2
3
}))

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

What return does is not really returning a value (this is done with or without it) but "breaking out" of the function in an irregular way. In that sense, it is the closest equivalent of GOTO statement in R (there are also break and next). I use return very rarely and never at the end of a function.

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

... this can be rewritten as if(a) a else b which is much better readable and less curly-bracketish. No need for return at all here. My prototypical case of use of "return" would be something like ...

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 코드 가독성을 높일 수 있습니다.

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

참고 URL : https://stackoverflow.com/questions/11738823/explicitly-calling-return-in-a-function-or-not

반응형