Basic

要旨: 1. function factory 所制造的函数(闭包, closure)的封装环境(the enclosing environment, 定义时的环境)是函数工厂的一个执行环境(an execution environment)。 2. 闭包(Closure)内含一些实例化时接收的工厂参数信息。闭包是带有数据的函数。

#' @title 函数工厂
power1 <- function(exp) {
  function(x) {
    x^exp
  }
}

square <- power1(2)
pryr::unenclose(square) # 查看生产出来的函数里面参数值
#> function (x) 
#> {
#>     x^2
#> }
cube <- power1(3)
pryr::unenclose(cube)
#> function (x) 
#> {
#>     x^3
#> }
square(3)
#> [1] 9
cube(3)
#> [1] 27

Enviroment

# square() 和 cube() 有相同的父环境,即全局环境,也是 power1 的封装环境
rlang::env_print(square)
#> <environment: 0x00000266ac8be958>
#> Parent: <environment: global>
#> Bindings:
#> • exp: <dbl>
rlang::env_print(cube)
#> <environment: 0x00000266ad017480>
#> Parent: <environment: global>
#> Bindings:
#> • exp: <dbl>
rlang::fn_env(square) # square()的封装环境,也是power1 的一个执行环境
#> <environment: 0x00000266ac8be958>
rlang::fn_env(square) %>% ls() # 其中只有 exp 一个对象
#> [1] "exp"
rlang::fn_env(square)$exp # exp = 2 储存在这个封装环境中
#> [1] 2
rlang::fn_env(cube) # cube()的封装环境,power1 的另一个执行环境
#> <environment: 0x00000266ad017480>
rlang::fn_env(cube)$exp
#> [1] 3

Forcing evaluation

t <- 2
square <- power1(t)
pryr::unenclose(square) # 封装环境中 exp 指向 t
#> function (x) 
#> {
#>     x^t
#> }
t <- 3 # t 值已变
rlang::fn_env(square)$exp
#> [1] 3
square(x = 3) # 运行时计算 t,但 t 已指向 3
#> [1] 27

惰性求值的机制,使 power1() 运行时,exp 指向 t;直到 square() 运行时,t 才被计算,但此时 t 指向的值(在全局环境中)已经变为 3.

强制求值可以避免产生这种错误。

power2 <- function(exp) {
  force(exp) # 强制求值
  function(x) {
    x^exp
  }
}

t <- 2
square <- power2(t) # 由于强制求值,exp 已经被赋值为 2
t <- 3
rlang::fn_env(square)$exp
#> [1] 2
square(3) # 3^2
#> [1] 9

例: 计数器

new_counter <- function() {
  i <- 0

  #' @title 匿名函数,由于使用了 <<- 赋值运算符,
  #'   可以修改其父环境/封装环境(new_counter() 的某一个执行环境)中的数据
  function() {
    i <<- i + 1
    i
  }
}

counter_one <- new_counter()
counter_two <- new_counter()
rlang::fn_env(counter_one)$i
#> [1] 0
rlang::fn_env(counter_two)$i
#> [1] 0
counter_one()
#> [1] 1
rlang::fn_env(counter_one)$i # counter_one() 封装环境中的数据 i,只能被 counter_one() 修改
#> [1] 1
rlang::fn_env(counter_two)$i # counter_two() 封装环境中的数据 i,只能被 counter_two() 修改
#> [1] 0
counter_one()
#> [1] 2
rlang::fn_env(counter_two)$i
#> [1] 0
counter_two()
#> [1] 1
rlang::fn_env(counter_one)$i
#> [1] 2

Application: 图形工厂

鉴于绘图的灵活性,绘图函数通常需要提供许多参数。如果大多数情况下,只使用部分参数,就可以创建一个专用的简版函数,这样可以使代码更容易编写和阅读。

Labelling

label_number()/label_comma()/label_scientific() 返回闭包,可以对数字进行格式化

y <- c(12345, 123456, 1234567)
scales::label_comma()(y)
#> [1] "12,345"    "123,456"   "1,234,567"
scales::label_number(scale = 1e-3, suffix = " K")(y)
#> [1] "12 K"    "123 K"   "1 235 K"
scales::label_scientific()(y)
#> [1] "1.23e+04" "1.23e+05" "1.23e+06"

ggplot 图形设定比例尺时,labels 参数可以接收函数

df <- data.frame(x = 1, y = y)
core <- ggplot(df, aes(x, y)) +
  geom_point() +
  scale_x_continuous(breaks = 1, labels = NULL) +
  labs(x = NULL, y = NULL)

core

core + scale_y_continuous(
  labels = scales::label_comma()
)

core + scale_y_continuous(
  labels = scales::label_number(scale = 1e-3, suffix = " K")
)

core + scale_y_continuous(
  labels = scales::label_scientific()
)

Application: 统计工厂

MLE estimator

Maximum Likelihood Estimation, MLE

以泊松分布为例,对于数据 \(\boldsymbol{x} = x_1, x_2, \cdots, x_n\),其 likelihood probability 为

\[ P(\lambda, \boldsymbol{x})=\prod_{i=1}^n \frac{\lambda^{x_i}e^{-\lambda}}{x_i!} \]

则对数似然函数可以简化为

\[ \ln P(\lambda, \boldsymbol{x}) = \ln \lambda \cdot \sum_{i=1}^nx_i-n\cdot\lambda-\sum_{i=1}^n\ln x_i! \]

求参数 \(\lambda\),使对数似然函数取得极大值

#' @title log likelihood poisson 函数工厂
ll_poisson <- function(x) {
  n <- length(x)
  sum_x <- sum(x)

  #' @return 闭包,接收lambda,返回对数似然函数的值
  function(lambda) {
    # lfactorial() 为向量化函数
    log(lambda) * sum_x - n * lambda - sum(lfactorial(x))
  }
}

x1 <- c(41, 30, 31, 38, 29, 24, 30, 29, 31, 38)
ll_1 <- ll_poisson(x1) # 闭包

optimise(f = ll_1, interval = c(0, 100), maximum = TRUE) # 求得 MLE estimator
#> $maximum
#> [1] 32.09999
#> 
#> $objective
#> [1] -30.26755
LS0tDQp0aXRsZTogIkZ1bmN0aW9uIEZhY3RvcnkiDQpzdWJ0aXRsZTogJycNCmF1dGhvcjogIkh1bW9vbiINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogaHRtbF9kb2N1bWVudA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQpzb3VyY2UoIi4uL1JtYXJrZG93bi10ZW1wbGF0ZS9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywNCiAgZXZhbCA9IGNvbmZpZyRldmFsLA0KICB0aWR5ID0gY29uZmlnJHRpZHksDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwNCiAgY29sbGFwc2UgPSBjb25maWckY29sbGFwc2UsDQogIGNhY2hlID0gY29uZmlnJGNhY2hlLA0KICBjYWNoZS5jb21tZW50cyA9IGNvbmZpZyRjYWNoZS5jb21tZW50cywNCiAgYXV0b2RlcCA9IGNvbmZpZyRhdXRvZGVwDQopDQoNCmxpYnJhcnkocmxhbmcpDQpsaWJyYXJ5KHNjYWxlcykNCmBgYA0KDQojIyMgQmFzaWMNCg0K6KaB5peo77yaDQoxLiBmdW5jdGlvbiBmYWN0b3J5IOaJgOWItumAoOeahOWHveaVsO+8iOmXreWMhSwgY2xvc3VyZe+8ieeahOWwgeijheeOr+Wig++8iCoqdGhlKiogZW5jbG9zaW5nIGVudmlyb25tZW50LCDlrprkuYnml7bnmoTnjq/looPvvInmmK/lh73mlbDlt6XljoLnmoTkuIDkuKrmiafooYznjq/looPvvIgqKmFuKiogZXhlY3V0aW9uIGVudmlyb25tZW5077yJ44CCDQoyLiDpl63ljIXvvIhDbG9zdXJl77yJ5YaF5ZCr5LiA5Lqb5a6e5L6L5YyW5pe25o6l5pS255qE5bel5Y6C5Y+C5pWw5L+h5oGv44CC6Zet5YyF5piv5bim5pyJ5pWw5o2u55qE5Ye95pWw44CCDQoNCmBgYHtyfQ0KIycgQHRpdGxlIOWHveaVsOW3peWOgg0KcG93ZXIxIDwtIGZ1bmN0aW9uKGV4cCkgew0KICBmdW5jdGlvbih4KSB7DQogICAgeF5leHANCiAgfQ0KfQ0KDQpzcXVhcmUgPC0gcG93ZXIxKDIpDQpwcnlyOjp1bmVuY2xvc2Uoc3F1YXJlKSAjIOafpeeci+eUn+S6p+WHuuadpeeahOWHveaVsOmHjOmdouWPguaVsOWAvA0KY3ViZSA8LSBwb3dlcjEoMykNCnByeXI6OnVuZW5jbG9zZShjdWJlKQ0KDQpzcXVhcmUoMykNCmN1YmUoMykNCmBgYA0KDQoNCiMjIyMgRW52aXJvbWVudA0KDQpgYGB7cn0NCiMgc3F1YXJlKCkg5ZKMIGN1YmUoKSDmnInnm7jlkIznmoTniLbnjq/looPvvIzljbPlhajlsYDnjq/looPvvIzkuZ/mmK8gcG93ZXIxIOeahOWwgeijheeOr+Wigw0Kcmxhbmc6OmVudl9wcmludChzcXVhcmUpDQpybGFuZzo6ZW52X3ByaW50KGN1YmUpDQoNCnJsYW5nOjpmbl9lbnYoc3F1YXJlKSAjIHNxdWFyZSgp55qE5bCB6KOF546v5aKD77yM5Lmf5pivcG93ZXIxIOeahOS4gOS4quaJp+ihjOeOr+Wigw0Kcmxhbmc6OmZuX2VudihzcXVhcmUpICU+JSBscygpICMg5YW25Lit5Y+q5pyJIGV4cCDkuIDkuKrlr7nosaENCnJsYW5nOjpmbl9lbnYoc3F1YXJlKSRleHAgIyBleHAgPSAyIOWCqOWtmOWcqOi/meS4quWwgeijheeOr+Wig+S4rQ0Kcmxhbmc6OmZuX2VudihjdWJlKSAjIGN1YmUoKeeahOWwgeijheeOr+Wig++8jHBvd2VyMSDnmoTlj6bkuIDkuKrmiafooYznjq/looMNCnJsYW5nOjpmbl9lbnYoY3ViZSkkZXhwDQpgYGANCg0KIyMjIyBGb3JjaW5nIGV2YWx1YXRpb24NCg0KYGBge3J9DQp0IDwtIDINCnNxdWFyZSA8LSBwb3dlcjEodCkNCnByeXI6OnVuZW5jbG9zZShzcXVhcmUpICMg5bCB6KOF546v5aKD5LitIGV4cCDmjIflkJEgdA0KdCA8LSAzICMgdCDlgLzlt7Llj5gNCnJsYW5nOjpmbl9lbnYoc3F1YXJlKSRleHANCnNxdWFyZSh4ID0gMykgIyDov5DooYzml7borqHnrpcgdO+8jOS9hiB0IOW3suaMh+WQkSAzDQpgYGANCg0K5oOw5oCn5rGC5YC855qE5py65Yi277yM5L2/IHBvd2VyMSgpIOi/kOihjOaXtu+8jGV4cCDmjIflkJEgdO+8m+ebtOWIsCBzcXVhcmUoKSDov5DooYzml7bvvIx0IOaJjeiiq+iuoeeul++8jOS9huatpOaXtiB0IOaMh+WQkeeahOWAvO+8iOWcqOWFqOWxgOeOr+Wig+S4re+8ieW3sue7j+WPmOS4uiAzLg0KDQrlvLrliLbmsYLlgLzlj6/ku6Xpgb/lhY3kuqfnlJ/ov5nnp43plJnor6/jgIINCg0KYGBge3J9DQpwb3dlcjIgPC0gZnVuY3Rpb24oZXhwKSB7DQogIGZvcmNlKGV4cCkgIyDlvLrliLbmsYLlgLwNCiAgZnVuY3Rpb24oeCkgew0KICAgIHheZXhwDQogIH0NCn0NCg0KdCA8LSAyDQpzcXVhcmUgPC0gcG93ZXIyKHQpICMg55Sx5LqO5by65Yi25rGC5YC877yMZXhwIOW3sue7j+iiq+i1i+WAvOS4uiAyDQp0IDwtIDMNCnJsYW5nOjpmbl9lbnYoc3F1YXJlKSRleHANCnNxdWFyZSgzKSAjIDNeMg0KYGBgDQoNCiMjIyMg5L6LOiDorqHmlbDlmagNCg0KYGBge3J9DQpuZXdfY291bnRlciA8LSBmdW5jdGlvbigpIHsNCiAgaSA8LSAwDQoNCiAgIycgQHRpdGxlIOWMv+WQjeWHveaVsO+8jOeUseS6juS9v+eUqOS6hiA8PC0g6LWL5YC86L+Q566X56ym77yMDQogICMnICAg5Y+v5Lul5L+u5pS55YW254i2546v5aKDL+WwgeijheeOr+Wig++8iG5ld19jb3VudGVyKCkg55qE5p+Q5LiA5Liq5omn6KGM546v5aKD77yJ5Lit55qE5pWw5o2uDQogIGZ1bmN0aW9uKCkgew0KICAgIGkgPDwtIGkgKyAxDQogICAgaQ0KICB9DQp9DQoNCmNvdW50ZXJfb25lIDwtIG5ld19jb3VudGVyKCkNCmNvdW50ZXJfdHdvIDwtIG5ld19jb3VudGVyKCkNCnJsYW5nOjpmbl9lbnYoY291bnRlcl9vbmUpJGkNCnJsYW5nOjpmbl9lbnYoY291bnRlcl90d28pJGkNCg0KY291bnRlcl9vbmUoKQ0Kcmxhbmc6OmZuX2Vudihjb3VudGVyX29uZSkkaSAjIGNvdW50ZXJfb25lKCkg5bCB6KOF546v5aKD5Lit55qE5pWw5o2uIGnvvIzlj6rog73ooqsgY291bnRlcl9vbmUoKSDkv67mlLkNCnJsYW5nOjpmbl9lbnYoY291bnRlcl90d28pJGkgIyBjb3VudGVyX3R3bygpIOWwgeijheeOr+Wig+S4reeahOaVsOaNriBp77yM5Y+q6IO96KKrIGNvdW50ZXJfdHdvKCkg5L+u5pS5DQoNCmNvdW50ZXJfb25lKCkNCnJsYW5nOjpmbl9lbnYoY291bnRlcl90d28pJGkNCg0KY291bnRlcl90d28oKQ0Kcmxhbmc6OmZuX2Vudihjb3VudGVyX29uZSkkaQ0KYGBgDQoNCg0KIyMjIEFwcGxpY2F0aW9uOiDlm77lvaLlt6XljoINCg0K6Ym05LqO57uY5Zu+55qE54G15rS75oCn77yM57uY5Zu+5Ye95pWw6YCa5bi46ZyA6KaB5o+Q5L6b6K645aSa5Y+C5pWw44CC5aaC5p6c5aSn5aSa5pWw5oOF5Ya15LiL77yM5Y+q5L2/55So6YOo5YiG5Y+C5pWw77yM5bCx5Y+v5Lul5Yib5bu65LiA5Liq5LiT55So55qE566A54mI5Ye95pWw77yM6L+Z5qC35Y+v5Lul5L2/5Luj56CB5pu05a655piT57yW5YaZ5ZKM6ZiF6K+744CCDQoNCiMjIyMgTGFiZWxsaW5nDQoNCmBsYWJlbF9udW1iZXIoKWAvYGxhYmVsX2NvbW1hKClgL2BsYWJlbF9zY2llbnRpZmljKClgIOi/lOWbnumXreWMhe+8jOWPr+S7peWvueaVsOWtl+i/m+ihjOagvOW8j+WMlg0KDQpgYGB7cn0NCnkgPC0gYygxMjM0NSwgMTIzNDU2LCAxMjM0NTY3KQ0Kc2NhbGVzOjpsYWJlbF9jb21tYSgpKHkpDQpzY2FsZXM6OmxhYmVsX251bWJlcihzY2FsZSA9IDFlLTMsIHN1ZmZpeCA9ICIgSyIpKHkpDQpzY2FsZXM6OmxhYmVsX3NjaWVudGlmaWMoKSh5KQ0KYGBgDQoNCmdncGxvdCDlm77lvaLorr7lrprmr5TkvovlsLrml7bvvIxsYWJlbHMg5Y+C5pWw5Y+v5Lul5o6l5pS25Ye95pWwDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gMSwgeSA9IHkpDQpjb3JlIDwtIGdncGxvdChkZiwgYWVzKHgsIHkpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxLCBsYWJlbHMgPSBOVUxMKSArDQogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKQ0KDQpjb3JlDQpjb3JlICsgc2NhbGVfeV9jb250aW51b3VzKA0KICBsYWJlbHMgPSBzY2FsZXM6OmxhYmVsX2NvbW1hKCkNCikNCmNvcmUgKyBzY2FsZV95X2NvbnRpbnVvdXMoDQogIGxhYmVscyA9IHNjYWxlczo6bGFiZWxfbnVtYmVyKHNjYWxlID0gMWUtMywgc3VmZml4ID0gIiBLIikNCikNCmNvcmUgKyBzY2FsZV95X2NvbnRpbnVvdXMoDQogIGxhYmVscyA9IHNjYWxlczo6bGFiZWxfc2NpZW50aWZpYygpDQopDQpgYGANCg0KIyMjIEFwcGxpY2F0aW9uOiDnu5/orqHlt6XljoINCg0KIyMjIyBNTEUgZXN0aW1hdG9yDQoNCk1heGltdW0gTGlrZWxpaG9vZCBFc3RpbWF0aW9uLCBNTEUNCg0K5Lul5rOK5p2+5YiG5biD5Li65L6L77yM5a+55LqO5pWw5o2uICRcYm9sZHN5bWJvbHt4fSA9IHhfMSwgeF8yLCBcY2RvdHMsIHhfbiTvvIzlhbYgbGlrZWxpaG9vZCBwcm9iYWJpbGl0eSDkuLogDQoNCiQkDQpQKFxsYW1iZGEsIFxib2xkc3ltYm9se3h9KT1ccHJvZF97aT0xfV5uIFxmcmFje1xsYW1iZGFee3hfaX1lXnstXGxhbWJkYX19e3hfaSF9DQokJA0KDQrliJnlr7nmlbDkvLznhLblh73mlbDlj6/ku6XnroDljJbkuLogDQoNCiQkDQpcbG4gUChcbGFtYmRhLCBcYm9sZHN5bWJvbHt4fSkgPSBcbG4gXGxhbWJkYSBcY2RvdCBcc3VtX3tpPTF9Xm54X2ktblxjZG90XGxhbWJkYS1cc3VtX3tpPTF9Xm5cbG4geF9pIQ0KJCQNCg0K5rGC5Y+C5pWwICRcbGFtYmRhJO+8jOS9v+WvueaVsOS8vOeEtuWHveaVsOWPluW+l+aegeWkp+WAvA0KDQpgYGB7cn0NCiMnIEB0aXRsZSBsb2cgbGlrZWxpaG9vZCBwb2lzc29uIOWHveaVsOW3peWOgg0KbGxfcG9pc3NvbiA8LSBmdW5jdGlvbih4KSB7DQogIG4gPC0gbGVuZ3RoKHgpDQogIHN1bV94IDwtIHN1bSh4KQ0KDQogICMnIEByZXR1cm4g6Zet5YyF77yM5o6l5pS2bGFtYmRh77yM6L+U5Zue5a+55pWw5Ly854S25Ye95pWw55qE5YC8DQogIGZ1bmN0aW9uKGxhbWJkYSkgew0KICAgICMgbGZhY3RvcmlhbCgpIOS4uuWQkemHj+WMluWHveaVsA0KICAgIGxvZyhsYW1iZGEpICogc3VtX3ggLSBuICogbGFtYmRhIC0gc3VtKGxmYWN0b3JpYWwoeCkpDQogIH0NCn0NCg0KeDEgPC0gYyg0MSwgMzAsIDMxLCAzOCwgMjksIDI0LCAzMCwgMjksIDMxLCAzOCkNCmxsXzEgPC0gbGxfcG9pc3Nvbih4MSkgIyDpl63ljIUNCg0Kb3B0aW1pc2UoZiA9IGxsXzEsIGludGVydmFsID0gYygwLCAxMDApLCBtYXhpbXVtID0gVFJVRSkgIyDmsYLlvpcgTUxFIGVzdGltYXRvcg0KYGBgDQo=