后面有一个,而我被指责西蒙Urbanek从R核心团队(我相信)用于推荐用户显式调用return的函数(他的评论被删除,虽然)的结尾:
foo = function() {
return(value)
}
Run Code Online (Sandbox Code Playgroud)
相反,他建议:
foo = function() {
value
}
Run Code Online (Sandbox Code Playgroud)
可能在这种情况下需要:
foo = function() {
if(a) {
return(a)
} else {
return(b)
}
}
Run Code Online (Sandbox Code Playgroud)
他的评论揭示了为什么不打电话,return除非严格要求是好事,但这被删除了.
我的问题是:为什么不打电话return更快或更好,因此更可取?
Pet*_*usu 121
问题是:为什么不(明确地)调用返回更快或更好,因此更可取?
在R文档中没有做出这样的假设.
Tha man page?'function'说:
function( arglist ) expr
return(value)
Run Code Online (Sandbox Code Playgroud)
没有回电话会更快吗?
这两个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
Run Code Online (Sandbox Code Playgroud)

上图可能在您的平台上略有不同.根据测量数据,返回对象的大小不会造成任何差异,重复次数(即使按比例放大)只会产生非常小的差异,实际数据和真实算法的实际单词无法计算或使您的脚本运行得更快.
没有回电话会更好吗?
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
>
Run Code Online (Sandbox Code Playgroud)
这取决于程序员的策略和编程风格,他使用什么样的风格,他可以不使用return(),因为它不是必需的.
R核心程序员使用两种方法即.有和没有显式return(),因为它可以在'基础'函数的源中找到.
很多时候只使用return()(没有参数)在条件下返回NULL来条件地停止该函数.
目前尚不清楚它是否更好,因为使用R的标准用户或分析师无法看到真正的差异.
我的观点是,问题应该是:使用来自R实施的明确回报是否有任何危险?
或者,更好的是,用户编写函数代码应该总是问:在函数代码中不使用显式返回(或将对象作为代码分支的最后一个叶子返回)会产生什么影响?
flo*_*del 93
如果每个人都同意这一点
return 在函数体的末尾没有必要return的速度略快(根据@Alan的测试,4.3微秒与5.1相比)我们是否应该return在功能结束时停止使用?我当然不会,我想解释原因.我希望听到其他人是否赞同我的意见.如果它不是OP的直接答案,我会道歉,但更像是一个长期的主观评论.
我没有使用的主要问题return是,正如Paul所指出的那样,在函数体中还有其他地方可能需要它.如果你被迫return在函数中间某处使用,为什么不将所有return语句都显式化呢?我讨厌不一致.我认为代码读得更好; 可以扫描功能并轻松查看所有出口点和值.
保罗使用了这个例子:
foo = function() {
if(a) {
return(a)
} else {
return(b)
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,人们可以指出它很容易被重写为:
foo = function() {
if(a) {
output <- a
} else {
output <- b
}
output
}
Run Code Online (Sandbox Code Playgroud)
后一版本甚至符合一些编程编码标准,这些标准主张每个函数有一个return语句.我认为一个更好的例子可能是:
bar <- function() {
while (a) {
do_stuff
for (b) {
do_stuff
if (c) return(1)
for (d) {
do_stuff
if (e) return(2)
}
}
}
return(3)
}
Run Code Online (Sandbox Code Playgroud)
使用单个return语句重写将更加困难:它需要多个breaks和一个复杂的布尔变量系统来传播它们.所有这一切都说单一返回规则与R不能很好地协作.所以如果你需要return在函数体的某些地方使用,为什么不能保持一致并在任何地方使用它?
我不认为速度论证是有效的.当您开始查看实际执行某些操作的函数时,0.8微秒的差异就不算什么了.我能看到的最后一件事就是打字不多但是嘿,我并不懒惰.
Ala*_*lan 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
Run Code Online (Sandbox Code Playgroud)
____ 编辑__ _ __ _ __ _ __ _ __ _ ___
我继续其他基准测试(benchmark(fuu(x),foo(x),replications=1e7)),结果反转...我会尝试服务器.
nog*_*pes 22
这是一个有趣的讨论.我认为@ flodel的例子很棒.但是,我认为它说明了我的观点(并且@koshke在评论中提到了这一点),return当你使用命令而不是功能编码风格时这是有意义的.
不要强调这一点,但我会改写foo如下:
foo = function() ifelse(a,a,b)
Run Code Online (Sandbox Code Playgroud)
功能样式可以避免状态更改,例如存储值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.
}
Run Code Online (Sandbox Code Playgroud)
该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)
Run Code Online (Sandbox Code Playgroud)
在函数式中,要返回的值自然落在函数的末尾.再次,它看起来更像是一个数学函数.
@GSee中列出的警告?ifelse肯定很有意思,但我不认为他们试图劝阻使用该功能.实际上,ifelse具有自动矢量化功能的优点.例如,考虑稍微修改过的版本foo:
foo = function(a) { # Note that it now has an argument
if(a) {
return(a)
} else {
return(b)
}
}
Run Code Online (Sandbox Code Playgroud)
这个函数在length(a)1 时工作正常.但是如果你foo用一个改写了ifelse
foo = function (a) ifelse(a,a,b)
Run Code Online (Sandbox Code Playgroud)
现在foo适用于任何长度a.事实上,它甚至可以在a矩阵时使用.返回一个与形状相同的值,test这个特征有助于矢量化,而不是问题.
Hug*_*ins 13
没有在结尾显式地放置'return'的问题是如果在方法结束时添加了额外的语句,突然返回值是错误的:
foo <- function() {
dosomething()
}
Run Code Online (Sandbox Code Playgroud)
这将返回值dosomething().
现在我们来到第二天并添加一个新行:
foo <- function() {
dosomething()
dosomething2()
}
Run Code Online (Sandbox Code Playgroud)
我们希望我们的代码返回值dosomething(),但不再如此.
通过明确的回报,这变得非常明显:
foo <- function() {
return( dosomething() )
dosomething2()
}
Run Code Online (Sandbox Code Playgroud)
我们可以看到这段代码有些奇怪,并修复它:
foo <- function() {
dosomething2()
return( dosomething() )
}
Run Code Online (Sandbox Code Playgroud)
Kon*_*lph 13
我的问题是:为什么不打电话
return更快
它更快,因为return是 R 中的(原始)函数,这意味着在代码中使用它会产生函数调用的成本。将此与大多数其他编程语言进行比较,其中return是关键字,而不是函数调用:它不会转换为任何运行时代码执行。
也就是说,以这种方式调用原始函数在 R 中非常快,并且调用return会产生极小的开销。这不是省略return.
或更好,因此更可取?
因为没有任何理由来使用它。
因为它是多余的,并且没有添加有用的冗余。
需要明确的是:冗余可有时是有用的。但大多数冗余不是这种类型。相反,它是一种在不添加信息的情况下增加视觉混乱的类型:它是填充词或图表垃圾的编程等价物)。
考虑以下解释性注释的示例,它被普遍认为是错误的冗余,因为该注释只是解释了代码已经表达的内容:
# Add one to the result
result = x + 1
Run Code Online (Sandbox Code Playgroud)
return在 R 中使用属于同一类别,因为 R 是一种函数式编程语言,并且在 R 中每个函数调用都有一个值。这是R 的一个基本属性。一旦你从每个表达式(包括每个函数调用)都有一个值的角度来看 R 代码,那么问题就变成了:“我为什么要使用return?” 需要有一个积极的理由,因为默认是不使用它。
一个这样的积极原因是发出提前退出函数的信号,例如在保护子句中:
f = function (a, b) {
if (! precondition(a)) return() # same as `return(NULL)`!
calculation(b)
}
Run Code Online (Sandbox Code Playgroud)
这是对 的有效、非冗余使用return。但是,与其他语言相比,这种保护子句在 R 中很少见,并且由于每个表达式都有一个值,因此正if则不需要return:
sign = function (num) {
if (num > 0) {
1
} else if (num < 0) {
-1
} else {
0
}
}
Run Code Online (Sandbox Code Playgroud)
我们甚至可以这样重写f:
f = function (a, b) {
if (precondition(a)) calculation(b)
}
Run Code Online (Sandbox Code Playgroud)
...if (cond) expr与if (cond) expr else NULL.相同的地方。
最后,我想避免三种常见的反对意见:
有些人认为 usingreturn增加了清晰度,因为它表示“此函数返回一个值”。但如上所述,R 中的每个函数都会返回一些内容。将其return视为返回值的标记不仅是多余的,而且是一种积极的误导。
相关地,Python之禅有一个奇妙的指导方针,应该始终遵循:
显式优于隐式。
删除冗余如何不return违反这一点?因为函数式语言中函数的返回值总是显式的:它是它的最后一个表达式。这又是关于显性与冗余的相同论点。
事实上,如果您想要明确性,请使用它来突出规则的例外:标记不返回有意义值的函数,这些函数仅因其副作用(例如cat)而被调用。除了 R 有一个比return这种情况更好的标记:invisible。例如,我会写
save_results = function (results, file) {
# … code that writes the results to a file …
invisible()
}
Run Code Online (Sandbox Code Playgroud)但是长函数呢?忘记返回的内容会不会很容易?
两个答案:首先,不是真的。规则很明确:函数的最后一个表达式是它的值。没有什么可跟踪的。
但更重要的是,长函数的问题不在于缺乏明确的return标记。这是函数的长度。长函数几乎 (?) 总是违反单一职责原则,即使他们不这样做,他们也会从可读性拆分中受益。
我认为这return是一个技巧.作为一般规则,函数中计算的最后一个表达式的值将成为函数的值 - 并且在许多地方都可以找到此常规模式.以下所有评估为3:
local({
1
2
3
})
eval(expression({
1
2
3
}))
(function() {
1
2
3
})()
Run Code Online (Sandbox Code Playgroud)
什么return不是真正返回一个值(无论是否有它),但以不规则的方式"突破"函数.从这个意义上说,它是R中最接近的GOTO语句(也有中断和下一个).我return很少使用,也从不使用函数.
if(a) {
return(a)
} else {
return(b)
}
Run Code Online (Sandbox Code Playgroud)
...这可以重写,因为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")
}
Run Code Online (Sandbox Code Playgroud)
一般来说,需要许多回报表明问题要么是丑陋的,要么结构不合理
<>
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)
Run Code Online (Sandbox Code Playgroud)