委托函数调用的便捷方法

时间:2019-07-11 08:46:57

标签: r function function-declaration

我有一系列类似的功能,所有这些功能都需要从数据帧中提取一些值。像这样:

function [] = q56982381()
hF = uifigure(); hAx = uiaxes(hF);
hAx.XScale = 'log';
hP = plot( hAx, 1 : 10 );

% Invoke the datacursor manager:
dcm_obj = datacursormode(hF);
hTip = dcm_obj.createDatatip(hP); % Don't specify any further inputs at this stage

% Wait until the figure loaded:
drawnow; pause(0.1); % or mlapptools.waitForFigureReady(hF); (see note at the bottom)

% Modify the datatip position:
hTip.Position = [2, 2, 0];

然后将这些函数作为其他函数的参数提供(让我们将其称为“主函数”。我无法修改主函数)。

我不想使用foo_1 <- function(data, ...) { x <- data$x y <- data$y # .. do some boring stuff with x and y # pack and process the result into 'ret' return(ret) } 而不是将其分配给data$x并使用x,因为它使无聊的东西难以阅读。但是,我现在需要在所有xx <- data$x ...函数中编写foo_1(等等)。这很烦人并且使代码混乱。此外,打包和处理对于所有foo_2函数都是通用的。

什么是优雅而简洁的方式?

一种可能性是foo_N数据框(或使用attach(),如Hong在下面的答案中所建议的那样),但是我不知道我的名字空间中还有其他变量:附加数据可以掩盖我在with()中使用的其他变量。另外,最好使用显式参数调用fun_1函数,这样可以更轻松地查看它们的需求和作用。

我想到的下一个可能性是这样的构造:

foo_N

然后我可以使用foo_generator <- function(number) { tocall <- switch(1=foo_1, 2=foo_2, 3=foo_3) # etc. function(data, ...) { x <- data$x y <- data$y tocall(x, y, ...) # process and pack into ret return(ret) } foo_1 <- function(x, y, ...) { # do some boring stuff } 代替foo_generator(1)作为主函数的参数。

有更好或更优雅的方式吗?我觉得我正在忽略这里很明显的东西。

7 个答案:

答案 0 :(得分:4)

您可能想得太多。您说关于准备和打包的代码是所有foo_n函数所共有的。那么,我假设# .. do some boring stuff with x and y是每个函数都不同的地方。如果是这种情况,则只需创建一个以函数名称作为参数的prep_and_pack函数,然后传入foo_1foo_2等。例如:

prep_and_pack <- function(data, func){
    x <- data$x
    y <- data$y

    # preparatory code here

    xy_output <- func(x, y) # do stuff with x and y

    # code to pack and process into "ret"

    return(ret)
}

现在,您可以创建自己的foo_n函数,用xy做不同的事情:

foo_1 <- function(x, y) {
    # .. do some boring stuff with x and y
}

foo_2 <- function(x, y) {
    # .. do some boring stuff with x and y
}

foo_3 <- function(x, y) {
    # .. do some boring stuff with x and y
}

最后,您可以将对prep_and_pack的多个调用传递到主函数中,其中foo_1等通过func参数传递:

master_func(prep_and_pack(data = df, func = foo_1),
            prep_and_pack(data = df, func = foo_2),
            prep_and_pack(data = df, func = foo_3)
            )

您也可以在switch中使用prep_and_pack和/或完全放弃foo_n函数,而转而使用if-else条件来处理各种情况,但我认为以上内容一直存在一切都很干净。

答案 1 :(得分:2)

我不确定以下是一个好主意。它使我想起了一些使用宏进行编程的过程。我不认为我会这样做。您需要仔细记录文档,因为它出乎意料,令人困惑且不言自明。

如果要在不同的函数中重用相同的代码,可以选择将其创建为未评估的调用,然后在不同的函数中评估该调用:

prepcode <- quote({
  x <- data$x
  y <- data$y
  }
)

foo_1 <- function(data, ...) {
  eval(prepcode)
  # some preparatory code common to all foo_X functions

  # .. do some boring stuff with x and y

  # pack and process the result into 'ret'
  return(list(x, y))
}

L <- list(x = 1, y = "a")
foo_1(L)
#[[1]]
#[1] 1
#
#[[2]]
#[1] "a"

最好将prepcode作为foo_1的参数,以确保不存在任何范围界定问题。

答案 2 :(得分:1)

对我来说,要求仍然有些含糊, 但是如果您的代码是如此相似,以至于您可以在示例中将其包装在诸如tocall之类的辅助函数中, 并且您的输入采用类似列表的结构 (就像只是一列列表的数据框一样), 然后只需编写您所有的foo_*函数,以采用“拟议”解决方案中的“拼接”参数, 然后使用do.call

foo_1 <- function(x, y) {
  x + y
}

foo_2 <- function(x, y) {
  x - y
}

data <- list(x = 1:2, y = 3:4)

do.call(foo_1, data)
# [1] 4 6

do.call(foo_2, data)
# [1] -2 -2

答案 3 :(得分:1)

我不确定我是否完全理解,但是您不能简单地对所有常见的东西使用一个函数,然后使用foo_N将其解压缩到list2env函数中吗?例如:

prepFun <- function(data, ...) {
    x <- data$x
    y <- data$y
    tocall(x, y, ...)
    # process and pack into a named list called ret
    # so you know specifically what the elements of ret are called
    return(ret)
}

foo_1 <- function(data, ...) {

  # Get prepFun to do the prepping, then use list2env to get the result in foo_1
  # You know which are the named elements, so there should be no confusion about
  # what does or does not get masked.
  prepResult <- prepFun(data, ...)
  list2env(prepResult)

  # .. do some boring stuff with x and y

  # pack and process the result into 'ret'
  return(ret)

}

希望这就是您想要的!

答案 4 :(得分:1)

我认为为此任务定义一个函数工厂有点矫kill过正和令人困惑。您可以定义一个通用函数,并将其传递给主函数时在其上使用purrr::partial()

类似的东西:

foo <- function(data, ..., number, foo_funs) {
  tocall <- foo_funs[[number]])
  with(data[c("x", "y")], tocall(x, y, ...))
    # process and pack into ret
   return(ret)
}

foo_1 <- function(x, y, ...) {
  # do some boring stuff
}

foo_funs <- list(foo_1, foo_2, ...)

然后致电master_fun(fun = purrr::partial(foo, number =1) , ...)

答案 5 :(得分:0)

在函数内使用with

foo_1 <- function(data, ...) {
  with(data, {
    # .. in here, x and y refer to data$x and data$y
  }
}

答案 6 :(得分:0)

另一种可能性是使用list2env将列表的组件保存到指定的环境中:

foo_1 <- function(data){
  list2env(data, envir = environment())
  x + y
}

foo_1(data.frame(x = 1:2, y = 3:4))

另请参阅this question