为什么当用户定义的R函数没有返回值时会发生这种情况?

时间:2018-03-21 06:13:10

标签: r dplyr r-plotly

在下面显示的函数中,没有return。但是,执行后,我可以确认正常输入d的值。

没有return。任何有关这方面的建议都将受到赞赏。

代码

#installed plotly, dplyr
accumulate_by <- function(dat, var) {
  var <- lazyeval::f_eval(var, dat)
  lvls <- plotly:::getLevels(var)
  dats <- lapply(seq_along(lvls), function(x) {
  cbind(dat[var %in% lvls[seq(1, x)], ], frame = lvls[[x]])
  })
dplyr::bind_rows(dats)
}


d <- txhousing %>%
  filter(year > 2005, city %in% c("Abilene", "Bay Area")) %>%
  accumulate_by(~date)

2 个答案:

答案 0 :(得分:3)

在该功能中,最后一项任务是创建&#39; dats&#39;与bind_rows(dats)一起返回的内容我们不需要明确的return语句。假设,如果要返回两个对象,我们可以将其放在list

python等某些语言中,为了提高内存效率,generators使用yield而不是在内存中创建整个输出,即考虑python <中的两个函数/ p>

def get_square(n):
    result = []
    for x in range(n):
        result.append(x**2)
return result

当我们运行它时

get_square(4)
#[0, 1, 4, 9]

同一个函数可以写成generator。 <而不是return任何东西,

def get_square(n):
    for x in range(n):
        yield(x**2)

运行功能

get_square(4) 
#<generator object get_square at 0x0000015240C2F9E8> 

通过使用list进行投射,我们得到相同的输出

list(get_square(4))
#[0, 1, 4, 9]

答案 1 :(得分:3)

总有一个回归:)你不必明白它。

所有 R表达式都返回一些东西。包括控制结构和用户定义的函数。 (顺便说一句,控制结构只是函数,所以你可以记住所有东西都是值或函数调用,并且所有东西都计算为值)。

对于函数,返回值是在函数执行中计算的最后一个表达式。所以,对于

f <- function(x) 2 + x

当您致电f(3)时,您将使用两个参数+2调用函数x。它们分别评估为23,因此`+`(2, 3)评估为5,这是f(3)的结果。

当您调用return函数时 - 请记住,这是一个函数 - 您只需提前保留函数的控制流。所以,

f <- function(x) {
    if (x < 0) return(0)
    x + 2
}

的工作方式如下:当您调用f时,它将调用if函数来确定在第一个语句中要执行的操作。 if函数将评估x < 0(这意味着使用参数<x调用函数0)。如果x < 0为真,if将评估return(0)。如果为false,则会评估其else部分(因为if在函数方面具有特殊语法,因此未显示,但是NULL)。如果x < 0不成立,f将评估x + 2并返回该值。但是,如果x < 0 为真,if函数将评估return(0)。这是对函数return的调用,参数为0,该调用将终止f的执行并生成结果0

小心return。这是一个功能

f <- function(x) {
    if (x < 0) return;
    x + 2
}

是完全有效的R代码,但在x < 0时不会返回。 if调用只评估函数return,但不会调用它。

return函数也有一点特别之处在于它可以从控制结构的父调用返回。严格地说,return并未在上面示例中f的框架中进行评估,而是在if次调用内部进行评估。它只是处理这个特殊所以它可以f返回。

对于非标准评估,这并不总是案例。

使用此功能

f <- function(df) {
    with(df, if (any(x < 0)) return("foo") else return("bar"))
    "baz"
}
你可能会认为

f(data.frame(x = rnorm(10)))

应返回"foo""bar"。毕竟,我们在if语句中返回。但是,if语句在with内进行评估,并且不会以这种方式工作。该函数将返回baz

对于非本地回报,您需要使用callCC,然后它会变得更具技术性(就好像这还不够技术)。

如果可以的话,尽量避免使用return并依赖函数返回他们评估的最后一个表达式。

更新

只是跟进下面关于循环的评论。当您调用循环时,您很可能会调用其中一个内置的基本函数。而且,是的,他们返回NULL。但是你可以编写自己的,他们将遵循他们返回他们评估的最后一个表达式的规则。例如,您可以按照for这样的方式实施while

`for` <- function(itr_var, seq, body) {
    itr_var <- as.character(substitute(itr_var))
    body <- substitute(body)
    e <- parent.frame()
    j <- 1
    while (j < length(seq)) {
        assign(x = itr_var, value = seq[[j]], envir = e)
        eval(body, envir = e)
        j <- j + 1
    }
    "foo"
}

这个函数肯定会返回"foo",所以这个

for(i in 1:5) { print(i) }

评估"foo"。如果你想要它返回NULL,你必须明确它(或者只是让返回值是while循环的结果 - 如果它是原始的while它返回NULL)。

我想说的是,函数返回它们评估的最后一个表达式与函数定义的方式有关,而不是你如何调用它们。循环使用非标准求值,因此您为它们提供的循环体中的最后一个表达式可能是它们评估的最后一个值,也可能不是。对于原始循环,它不是。

除了他们特殊的语法之外,没有什么神奇的循环。他们遵循所有功能遵循的规则。使用非标准的评估,从函数调用中得出它们将要评估的最后一个表达式可能会有点棘手,因为函数体看起来就像函数评估的那样。在某种程度上,如果函数是合理的,但循环体函数体。这是一个参数。如果它不是特殊语法,并且你必须提供循环体作为正常参数,那么可能会有更少的混淆。