7. 函数式编程

作者: kkkkkkang | 来源:发表于2020-04-20 10:46 被阅读0次

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

  • 废话不多说,下面这个例子综合了列表中存储函数(后面还有更详细的例子)、隐函数和lapply()的用法
  • 目的:对mtcars的每一列计算mean、median、sd、mad和IQR
#首先上面五个需求函数存储在列表中,然后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数据集中所有列的变异系数(标准差/平均值)
> 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 = x^2 -x 在[0,10]的积分
> 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函数,它根据给出的索引i值,返回带有参数x的函数,这个函数可以根据i值对x进行自己选取
> 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())

积分案例略
下课~

相关文章

  • 7. 函数式编程

    习题是我自己写的,没有参考答案,不对请指出 废话不多说,下面这个例子综合了列表中存储函数(后面还有更详细的例子)、...

  • RxSwift初探(1)

    一、前提:函数响应式编程思想 简单来说 函数响应式编程 = 函数式编程 + 响应式编程 (1)函数式 函数式编程是...

  • iOS 函数编程 & 链式编程

    函数式(链式)编程 函数式编程概念 函数式编程是种编程范式 函数式编程 Functional Programmin...

  • 函数式编程(一)—— 前置知识

    为什么要学函数式编程? 什么是函数式编程?函数式编程和面向对象编程的不同对于函数式编程思维方式的理解: 函数式编程...

  • 二十二、函数式编程、面向协议式编程

    函数式编程(Functional Prigramming) 函数式编程 函数式编程(Functional Prig...

  • Python中的函数式编程

    函数式编程 说到函数式编程,我们的重点在于为什么要采用函数式编程,而不是什么是函数式编程。 函数式编程的优点:一、...

  • RxSwift-初探

    函数响应式编程 一:函数式 函数式编程简称FP(Functional Programming),函数式编程就是一种...

  • 《Kotlin入门实战》CH5 | 函数与函数式编程

    函数与函数式编程 函数式编程与命令式编程最大的不同是:函数式编程的焦点在于数据的映射,命令式编程(imperati...

  • 函数响应式编程思想 & RxSwift 核心逻辑(一)

    函数响应式编程思想 函数响应式编程思想即是将函数式编程和响应式编程相结合。 函数式编程 顾名思义,就是像函数一样的...

  • RxJava系列|RxJava简介(一)

    函数响应式编程 函数式编程是一种编程范式。 常见的编程范式有:命令式编程、函数式编程和逻辑式编程。 面向对象就是一...

网友评论

    本文标题:7. 函数式编程

    本文链接:https://www.haomeiwen.com/subject/svruvhtx.html