数据科学与R语言

7. 函数式编程

2020-04-20  本文已影响0人  kkkkkkang

习题是我自己写的,没有参考答案,不对请指出

#首先上面五个需求函数存储在列表中,然后lapply把它们交给隐函数(没有名字的函数),分别变成mean(x,na.rm = TRUE)、median(x,na.rm = TRUE)......
summary <- function(x) {
  funs <- c(mean, median, sd, mad, IQR)
  lapply(funs, function(f) f(x, na.rm = TRUE))
}
a <- lapply(mtcars,summary)
str(a)
List of 11
 $ mpg :List of 5
  ..$ : num 20.1
  ..$ : num 19.2
  ..$ : num 6.03
  ..$ : num 5.41
  ..$ : num 7.38
 $ cyl :List of 5
  ..$ : num 6.19
  ..$ : num 6
  ..$ : num 1.79
  ..$ : num 2.97
  ..$ : num 4
 $ disp:List of 5
  ..$ : num 231
  ..$ : num 196
  ..$ : num 124
  ..$ : num 140
  ..$ : num 205
 $ hp  :List of 5
  ..$ : num 147
  ..$ : num 123
  ..$ : num 68.6
  ..$ : num 77.1
  ..$ : num 83.5
 $ drat:List of 5
  ..$ : num 3.6
  ..$ : num 3.7
  ..$ : num 0.535
  ..$ : num 0.704
  ..$ : num 0.84
 $ wt  :List of 5
  ..$ : num 3.22
  ..$ : num 3.33
  ..$ : num 0.978
  ..$ : num 0.767
  ..$ : num 1.03
 $ qsec:List of 5
  ..$ : num 17.8
  ..$ : num 17.7
  ..$ : num 1.79
  ..$ : num 1.42
  ..$ : num 2.01
 $ vs  :List of 5
  ..$ : num 0.438
  ..$ : num 0
  ..$ : num 0.504
  ..$ : num 0
  ..$ : num 1
 $ am  :List of 5
  ..$ : num 0.406
  ..$ : num 0
  ..$ : num 0.499
  ..$ : num 0
  ..$ : num 1
 $ gear:List of 5
  ..$ : num 3.69
  ..$ : num 4
  ..$ : num 0.738
  ..$ : num 1.48
  ..$ : num 1
 $ carb:List of 5
  ..$ : num 2.81
  ..$ : num 2
  ..$ : num 1.62
  ..$ : num 1.48
  ..$ : num 2
> lapply(mtcars,function(x) sd(x)/mean(x))
$mpg
[1] 0.2999881

$cyl
[1] 0.2886338

$disp
[1] 0.5371779

$hp
[1] 0.4674077

$drat
[1] 0.1486638

$wt
[1] 0.3041285

$qsec
[1] 0.1001159

$vs
[1] 1.152037

$am
[1] 1.228285

$gear
[1] 0.2000825

$carb
[1] 0.5742933
> y <- function(x) x ^ 2 - x
> integrate(y,0,10)
283.3333 with absolute error < 3.1e-12

闭包:它可以将父函数的环境封装,并可以访问它的所有变量。

对象是带有函数的数据,闭包是带有数据的函数 ——John D.cook

#闭包就是函数编写的函数,常用到隐函数来实现
#下面利用父层次控制运算,子层次进行工作的思想创建一组幂函数
power <- function(exponent) {
  function(x) {
    x ^ exponent
  }
}

square <- power(2)
square(2)
#> [1] 4
square(4)
#> [1] 16

cube <- power(3)
cube(2)
#> [1] 8
cube(4)
#> [1] 64
#查看闭包的不同环境
library(pryr)
unenclose(square)
#> function (x) 
#> {
#>     x^2
#> }
unenclose(cube)
#> function (x) 
#> {
#>     x^3
#> }
#通常情况下自己写的函数的环境就是全局环境,别人写的函数就是添加包环境,元函数(primitive function),直接调用C代码,且没有相关联的环境
#一般来说,函数的执行环境是临时的,但是闭包可以一直访问它创建的环境
#下面的例子中,counter_one和counter_two在执行是都可以获取它们自己的封闭环境,所以各自计数互不影响
> new_counter <- function() {
+     i <- 0
+     function() {
+         i <<- i + 1
+         i
+     }
+ }
> counter_one <- new_counter()
> counter_two <- new_counter()
> 
> counter_one()
[1] 1
> counter_one()
[1] 2
> counter_two()
[1] 1
> counter_two()
[1] 2
> counter_two()
[1] 3
#不用全局赋值,就是下面这样
> i <- 0
> new_counter2 <- function() {
+   i <<- i + 1
+   i
+ }
> new_counter2()
[1] 1
> new_counter3 <- function() {
+   i <- 0
+   function() {
+     i <- i + 1
+     i
+   }
+ }
> new_counter3()
function() {
    i <- i + 1
    i
  }
<environment: 0x0000018dd806dd20>
#这个new_counter3()没有输出,是因为虽然它可以向上查找其父环境中的i,但是执行完函数又被清除了(个人理解)
> pick <- function(i){
+   function(x) x[[i]]
+ }
> pick(5)
function(x) x[[i]]
<environment: 0x0000025bb7088b90>
> head(lapply(mtcars,pick(5)))
$mpg
[1] 18.7
$cyl
[1] 8
$disp
[1] 360
$hp
[1] 175
$drat
[1] 3.15
$wt
[1] 3.44
#检查结果
> head(lapply(mtcars,function(x) x[[5]]))
$mpg
[1] 18.7
$cyl
[1] 8
$disp
[1] 360
$hp
[1] 175
$drat
[1] 3.15
$wt
[1] 3.44
#利用函数列表的思想比较三种不同方法计算平均值所需的时间
compute_mean <- list(
  base = function(x) mean(x),
  sum = function(x) sum(x) / length(x),
  manual = function(x) {
    total <- 0
    n <- length(x)
    for (i in seq_along(x)) {
      total <- total + x[i] / n
    }
    total
  }
)
x <- runif(1e5)
system.time(compute_mean$base(x))
#>    user  system elapsed 
#>   0.001   0.000   0.001
system.time(compute_mean[[2]](x))
#>    user  system elapsed 
#>   0.000   0.000   0.001
system.time(compute_mean[["manual"]](x))
#>    user  system elapsed 
#>   0.023   0.000   0.023
> is.primitive(sum)
[1] TRUE
> is.primitive(mean)
[1] FALSE
#看看,元函数sum的快可见一斑。还有就是尽量避免使用循环,你看这个“manual”就慢的不像话
#lapply,让函数调用更简单
lapply(compute_mean, function(f) f(x))
#> $base
#> [1] 0.4994664
#> 
#> $sum
#> [1] 0.4994664
#> 
#> $manual
#> [1] 0.4994664
#如果需要额外的参数呢?有...在,你尽管放心加。
funs2 <- list(
  sum = function(x, ...) sum(x, ..., na.rm = TRUE),
  mean = function(x, ...) mean(x, ..., na.rm = TRUE),
  median = function(x, ...) median(x, ..., na.rm = TRUE)
)
lapply(funs2, function(f) f(x))
#> $sum
#> [1] 55
#> 
#> $mean
#> [1] 5.5
#> 
#> $median
#> [1] 5.5
#当然还可以更简单
lapply(funs, function(f) f(x, na.rm = TRUE))

将函数列表移到全局环境

simple_tag <- function(tag) {
  force(tag)
  function(...) {
    paste0("<", tag, ">", paste0(...), "</", tag, ">")
  }
}
tags <- c("p", "b", "i")
html <- lapply(setNames(tags, tags), simple_tag)
html$p("This is ", html$b("bold"), " text.")
#> [1] "<p>This is <b>bold</b> text.</p>"
#三种方法终止html$的作用
1. with(),适合临时。最为推荐
with(html, p("This is ", b("bold"), " text."))
#> [1] "<p>This is <b>bold</b> text.</p>"
2. attach(),适合长时间。使用完成后,detach()解除即可
attach(html)
p("This is ", b("bold"), " text.")
#> [1] "<p>This is <b>bold</b> text.</p>"
detach(html)
3. list2env(),复制到全局环境
list2env(html, environment())
#> <environment: R_GlobalEnv>
p("This is ", b("bold"), " text.")
#> [1] "<p>This is <b>bold</b> text.</p>"
rm(list = names(html), envir = environment())

积分案例略
下课~

上一篇下一篇

猜你喜欢

热点阅读