函数操作符和函数包装

时间:2021-09-18 22:03:55

Hadley's "Advanced R" gives us a brief insight into functional programming and possible applications of "function operators" . However, my concern is that I either do not fully understand how these simplify the code, or unwillingly follow some bad practices by using other solutions.

Hadley的“高级R”让我们对函数式编程和“函数操作符”的可能应用有了一个简要的了解。然而,我担心的是,我要么不完全理解这些如何简化代码,要么不情愿地使用其他解决方案来遵循一些糟糕的实践。

Instead of creating operators that expect an input to be a function, I create a wrapper, that takes an expression as its argument, and returns a complete output (instead of a function, that still needs to be called). See example:

我没有创建期望输入是函数的操作符,而是创建一个包装器,将表达式作为参数,并返回完整的输出(而不是仍然需要调用的函数)。看到的例子:

# function operator
delay_by <- function(delay, f) {
  function(...) {
    Sys.sleep(delay)
    f(...)
  }
}

# simple wrapper
delay_by2 <- function(expr, delay) { 
  Sys.sleep(delay)
  eval(expr)
}

delay_by(1, sqrt)(2)
delay_by2(sqrt(2), 1)

operator <- delay_by(1, sqrt)
wrapper <- function(x) delay_by2(sqrt(x), 1)

operator(2)
wrapper(2)

This approach still allows for feeding into functionals (sapply etc.) and applying memoisation. In addition, this supports piping with %>%.

这种方法仍然允许将功能(sapply等)和memoisation合并。此外,这支持管道%>% %。

lapply(1:10, function(x) sqrt(x) %>% delay_by2(1))

The potential drawback of this solution is that it does not create persistent execution environments to store variables maintaining function's state, but these may be created explicitly with <<- new.env().

这个解决方案的潜在缺点是它没有创建持久的执行环境来存储维护函数状态的变量,但是可以使用<<- new.env()显式地创建这些变量。

Is there any reason why I should consider my approach inferior? I am trying to avoid an unnecessary nesting.

我有什么理由认为我的方法不够好吗?我试图避免不必要的嵌套。

1 个解决方案

#1


3  

  • One important software engineering aspect is that functions are easy to test and package, expressions not so much. So if you use a function approach you can write unit tests, and put it in a package. With the eval approach you are limited to your own REPL testing. Obviously we should all be doing "Test Driven Development", at least on our production code, and nothing should go out the door without a set of unit tests that cover as much of the code as economically possible. This makes life much easier for people (including you) who might need to make changes someday.

    一个重要的软件工程方面是,函数易于测试和打包,而表达式则不那么容易。如果你使用函数方法,你可以写单元测试,并把它放在一个包里。使用eval方法,您只能使用自己的REPL测试。显然,我们都应该进行“测试驱动的开发”,至少在我们的产品代码上是这样的,没有一组单元测试,在经济上尽可能多地覆盖代码,任何东西都不应该出门。这让那些可能有一天需要做出改变的人(包括你自己)的生活更加轻松。

  • Also note that there may be compiler optimizations that can be done function-wide, but not on expressions. That would be true in other languages, but I don't know enough about how R's compiler works to say anything about that.

    还需要注意的是,可以在函数范围内执行编译器优化,但不能在表达式上执行。这在其他语言中也是成立的,但是我不太了解R的编译器是如何工作的。

  • Another point that occurs to me is that the eval route is not really scalable, i.e. only practical for expressions up to a certain complexity. Using functions on the other hand allows your logic to grow to any practical size.

    我想到的另一点是,eval路由并不是真正可伸缩的,也就是说,它只适用于具有一定复杂性的表达式。另一方面,使用函数可以使逻辑扩展到任何实际的大小。

  • Finally, eval as a construct is a very powerful one, but one that can get you in trouble, kind of like using a high-powered laser to cut paper. Since it is often easy to implement, it exists in many languages, and there are a lot of articles on the net about why it is dangerous - you should look at them. So like that high-powered laser, you shouldn't really get in the habit of using it except where it is necessary.

    最后,eval是个非常强大的构造,但它会给你带来麻烦,有点像使用高能激光切割纸张。因为它通常很容易实现,所以它存在于许多语言中,而且网上有很多关于为什么它是危险的文章——你应该看看它们。就像那个高能激光,你不应该养成使用它的习惯,除非它是必要的。

#1


3  

  • One important software engineering aspect is that functions are easy to test and package, expressions not so much. So if you use a function approach you can write unit tests, and put it in a package. With the eval approach you are limited to your own REPL testing. Obviously we should all be doing "Test Driven Development", at least on our production code, and nothing should go out the door without a set of unit tests that cover as much of the code as economically possible. This makes life much easier for people (including you) who might need to make changes someday.

    一个重要的软件工程方面是,函数易于测试和打包,而表达式则不那么容易。如果你使用函数方法,你可以写单元测试,并把它放在一个包里。使用eval方法,您只能使用自己的REPL测试。显然,我们都应该进行“测试驱动的开发”,至少在我们的产品代码上是这样的,没有一组单元测试,在经济上尽可能多地覆盖代码,任何东西都不应该出门。这让那些可能有一天需要做出改变的人(包括你自己)的生活更加轻松。

  • Also note that there may be compiler optimizations that can be done function-wide, but not on expressions. That would be true in other languages, but I don't know enough about how R's compiler works to say anything about that.

    还需要注意的是,可以在函数范围内执行编译器优化,但不能在表达式上执行。这在其他语言中也是成立的,但是我不太了解R的编译器是如何工作的。

  • Another point that occurs to me is that the eval route is not really scalable, i.e. only practical for expressions up to a certain complexity. Using functions on the other hand allows your logic to grow to any practical size.

    我想到的另一点是,eval路由并不是真正可伸缩的,也就是说,它只适用于具有一定复杂性的表达式。另一方面,使用函数可以使逻辑扩展到任何实际的大小。

  • Finally, eval as a construct is a very powerful one, but one that can get you in trouble, kind of like using a high-powered laser to cut paper. Since it is often easy to implement, it exists in many languages, and there are a lot of articles on the net about why it is dangerous - you should look at them. So like that high-powered laser, you shouldn't really get in the habit of using it except where it is necessary.

    最后,eval是个非常强大的构造,但它会给你带来麻烦,有点像使用高能激光切割纸张。因为它通常很容易实现,所以它存在于许多语言中,而且网上有很多关于为什么它是危险的文章——你应该看看它们。就像那个高能激光,你不应该养成使用它的习惯,除非它是必要的。