装饰器R:改变输入输出

4

我正在尝试重构这个内容。在Python中,我会使用装饰器。那么,在'Restful'方面,应该如何做到这一点呢?比如说,我们有这样一个模式:

good_input <- format_input( bad_input )
bad_output <- use_this_func( good_input )
good_output <- format_output( bad_output )

再说一遍,

good_input <- format_input( bad_input )
bad_output <- use_this_other_func( good_input )
good_output <- format_output( bad_output )

正如您所想象的那样,这种情况像野生蘑菇一样迅速扩散。我希望有一个类似于这个解决方案。

use_this_robust_func <- wrapper( use_this_func ) # Or wrapper( use_this_other_func )
good_output <- use_this_robust_func( bad_input )

我试图通过将对use_this_funcuse_this_other_func(以及相关函数)的调用与format_inputformat_output包装起来。 在一定程度上使用了这个问题,到目前为止我有:

wrapper <- function( func_not_robust ){
  func_robust <- function( ... ){
   # This is the bit I haven't figured out
   ... format_input( ) ... # supposed to convert bad input - the function argument - to good
   bad_output <- func_not_robust( ... ) # supposed to take good input as argument
   good_output <- format_output( bad_output )
   return( good_output )
   }
  return( func_robust )
}

对于伪代码表示抱歉。请注意我不确定这是否是在 R 中所采用的方法。我没有固定于上述解决方案的概念,因为它是从 Python 翻译到 R 的,并且还做得很差。R 本地人会如何处理呢?谢谢提前。


你能给一个实际的使用案例吗? - user3471268
1个回答

7

我认为你已经非常接近了。以下是一个示例,其中清理的第一阶段是用NA替换负输入值,输出清理很简单,只需将所有内容取反:

format_input <- function(x){
    x[x<0] <- NA
    return(x)
}

format_output <- function(x){
    return(-x)
}

wrapper <- function(f){
    force(f)
    g = function(bad_input){
        good_input = format_input(bad_input)
        bad_output = f(good_input)
        good_output = format_output(bad_output)
        return(good_output)
    }
    g
}

然后:

> wrapper(sqrt)(c(-2,2))
[1]        NA -1.414214
wrapper(sqrt) 返回一个“闭包”,这是一个带有封闭数据的函数。函数 f 的值作为封闭数据的一部分包含了函数 sqrt
由于 R 的懒惰求值或“promises”等原因,在创建 g 时不会评估 f,在某些情况下,如果没有进行 force 调用,则运行包装版本时将找不到 f。对于闭包生成器的未评估参数添加 force 调用是零开销的。这有点像蒟蒻编程,但从未出现过问题。
一个更灵活的解决方案可能是将输入和输出清理函数作为闭包生成器的函数进行指定,并设有默认值:
wrapper <- function(f, fi=format_input, fo=format_output){
    force(f) ; force(fi); force(fo)
    g = function(bad_input){
        good_input = fi(bad_input)
        bad_output = f(good_input)
        good_output = fo(bad_output)
        return(good_output)
    }
    g
}

然后我可以使用不同的输入和输出格式对sqrt进行包装。例如,将负函数更改为正函数:

> make_pos = function(x){abs(x)}
> wrapper(sqrt,fo=make_pos)(c(-2,2))
[1]       NA 1.414214

更加灵活的解决方案是意识到您正在生成函数链。您的输出为format_output(sqrt(format_output(bad_input)))。这是函数合成,在functional包中有一个函数可以实现这个功能:
> require(functional)
> w = Compose(format_input, sqrt, format_output)
> w(c(-2,2))
[1]        NA -1.414214

当您在组合中有三个以上的函数时,这可能会变得更加有用,例如,您可以使用do.call将函数列表组合在一起。

一旦在函数式编程中看到模式,就容易上瘾。我现在停下来。


对于functional包,点个赞。非常感谢,我在这里学到了一些东西。 - vathymut

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接