21.1 函数类型

R 语言中函数主要有内置函数(builtin)特殊函数(special )闭包函数(closure)三种类型

Show the code
typeof(sum)    
#> [1] "builtin"
typeof(`[`)
#> [1] "special"
typeof(`%>%`)
#> [1] "closure"

21.2 函数组件

  1. formals() 参数列表
  2. body() 函数体,包括异常处理、执行程序
  3. environment() 确定函数如何查找与名称相关联的值的数据结构,俗称环境
Show the code
f02 <- function(x, y) {
  # 注释
  x + y
}

formals(f02)
#> $x
#> 
#> 
#> $y
body(f02)
#> {
#>     x + y
#> }
environment(f02)
#> <environment: R_GlobalEnv>

属性 srcref(source reference ),指向函数的源代码

Show the code
attributes(f02)
#> $srcref
#> function(x, y) {
#>   # 注释
#>   x + y
#> }

21.3 原函数

在R中,原函数(primitive functions) 不具备上述三个组件,而是直接通过.Primitive()调用C语言代码实现。

Show the code
formals(sum)
#> NULL
body(sum)
#> NULL
environment(sum)
#> NULL
attributes(sum)
#> NULL

在R语言中,“Primitive functions”(始函数)是使用C语言实现的核心函数,它们是R的基础并且直接映射到底层操作,从而提供了更高的运行效率。原始函数的类型为builtin或special,并且它们通常不接受延迟求值的参数,而是在调用时立即求值 。

原函数有内置函数(builtin)特殊函数(special )两种类型。

Show the code
sum
#> function (..., na.rm = FALSE)  .Primitive("sum")
typeof(sum)
#> [1] "builtin"
.Primitive("sum")
#> function (..., na.rm = FALSE)  .Primitive("sum")
get("sum",envir = baseenv())
#> function (..., na.rm = FALSE)  .Primitive("sum")

`[`
#> .Primitive("[")
typeof(`[`)
#> [1] "special"
.Primitive("[")
#> .Primitive("[")

21.4 First-class functions

在编程语言中,“First-class functions”(一级函数或函数作为一等公民)是函数式编程的一个核心概念,它意味着函数可以像任何其他类型的数据(如数字、字符串和对象)一样被传递和操作。具体来说,一级函数具有以下几个关键特性:

  1. 可赋值:可以给函数分配一个变量。

  2. 可传递:可以作为参数传递给其他函数。

  3. 可返回:可以从其他函数中作为返回值。

  4. 可创建:可以在运行时创建新函数。

这些特性使得一级函数非常灵活,并且允许编程者以声明式的方式构建复杂的逻辑。

R语言是一种支持函数式编程的语言,它自然地支持一级函数。以下是R语言中一级函数的一些应用示例:

  1. 将函数赋值给变量

    Show the code
    f01 <- function(x) {
      x^2
    }
  2. 将函数作为参数传递

    Show the code
    apply_func <- function(x, f) {
      f(x)
    }
    result <- apply_func(10, f01) # 将函数f01()作为参数传递
  3. 从函数中返回函数

    Show the code
    create_multiplier <- function(n) {
      function(x) {
        x * n
      }
    }
    
    double <- create_multiplier(2)
    result <- double(10) # 返回20
  4. 匿名函数 anonymous function:

    Show the code
    sapply(c(1,2,3,4,5), function(x) x*2)
    #> [1]  2  4  6  8 10
  5. Lambda函数:在较新版本的R中,可以使用~来定义Lambda函数,是一种简洁的匿名函数定义方式。

    Show the code
    map_dbl(1:5, ~.x * 2)
    #> [1]  2  4  6  8 10
  6. 函数的高阶特性

    Show the code
    map_values <- function(x, f) {
      vapply(x, f, FUN.VALUE = double(1))
    }
    
    result <- map_values(c(1,2,3,4,5), function(x) x ^ 2)

    在R中,vapplysapplylapplymapply 等函数都是高阶函数的例子,它们可以接受另一个函数作为参数,并对数据集执行操作。

21.4.1 一级函数的优势:

  • 代码复用:通过将函数作为参数,可以编写更通用、更可重用的代码。

  • 模块化:一级函数有助于将程序分解为更小、更易于管理的部分。

  • 抽象:它们允许开发者创建更高级别的抽象,简化复杂逻辑的表达。

  • 灵活性:一级函数提供了编写灵活和动态代码的能力。

函数式编程和一级函数是现代编程范式的重要组成部分,它们在数据处理和统计计算中尤其有用,这也是R语言作为统计计算语言的一个强大特性。

21.4.2 函数列表

Show the code
funs <- list(
  half = function(x) x / 2,
  double = function(x) x * 2
)
funs$double(10)
#> [1] 20

21.4.3 函数闭包

在 R 中,你经常会看到称为 闭包(closure)(内嵌在函数体的函数) 的函数。

函数是对象,使用创建函数闭包(function closure)时的即存在的状态值进行封装的函数。

Show the code
typeof(ggplot)
#> [1] "closure"

21.5 函数调用

Show the code
args <- list(1:10, na.rm = TRUE)  # 参数列表
do.call(mean, args)
#> [1] 5.5

21.6 词法作用域

词法作用域lexical scoping ,即函数运行中需要使用某个变量时,从其定义时的环境向外层逐层查找,而不是在调用时的环境中查找。这种查找找到变量对应的存储空间后,使用其当前值,而不是历史值,这种规则称为动态查找(dynamic lookup)

函数的作用域(scope)在R中称为环境。当一个函数被创建后,R 中存在一个让这个函数发挥作用的环境。

在启动 R 之后,我们就进去了一个全局环境之中(Global Environment),我们创建的各自变量、函数都会处于其中。

对象可以通过参数传递到函数中,但不会被函数改变。传递的是对象的副本而不是对象本身。除非使用 <<- 特殊赋值符。

Show the code
x <- 2
y <- 3
z <- 4
f <- function(w){
    z <- 2
    x <- w*y*z
    return(x)
}
f(x)
#> [1] 12
x
#> [1] 2
y
#> [1] 3
z
#> [1] 4

Lexical scoping:

  1. Name masking
  2. Functions versus variables
  3. A fresh start
  4. Dynamic lookup

21.6.1 Name masking

在函数内部定义的名称会掩盖在函数外部定义的名称。

Show the code
x <- 10
y <- 20
g02 <- function() {
  x <- 1
  y <- 2
  c(x, y)
}
g02()
#> [1] 1 2

如果未在函数中定义名称,则 R 将向上查找。

Show the code
x <- 2
g03 <- function() {
  y <- 1
  c(x, y)
}
g03()
#> [1] 2 1

y
#> [1] 20

21.7 惰性求值

Lazy evaluation 是一种将操作延迟到真正需要它的结果时才执行的优化技术。

惰性求值由称为 promise 或thunk 的数据结构提供支持。

21.7.1 promise

一个表达式,调用函数的环境,一个值,局部变量作用域求值

Show the code
double <- function(x) { 
  message("Calculating...")
  x * 2
}

h03 <- function(x) {
  c(x, x)
}

h03(double(20))
#> [1] 40 40

21.8 传递参数

关键字传参,位置传参

21.8.1 默认参数

Show the code
h04 <- function(x = 1, y = x * 2, z = a + b) {
  a <- 10
  b <- 100
  
  c(x, y, z)
}
h04()
#> [1]   1   2 110

# 查看参数的名称和默认值
args(h04)
#> function (x = 1, y = x * 2, z = a + b) 
#> NULL

21.8.2 缺失参数

Show the code
h06 <- function(x = 10) {
  list(missing(x), x)
}
str(h06())
#> List of 2
#>  $ : logi TRUE
#>  $ : num 10

str(h06(10))
#> List of 2
#>  $ : logi FALSE
#>  $ : num 10

21.9 特殊参数...

variable arguments,有了它,函数可以接受任意数量的附加参数。还可以将附加参数传递给另一个函数

Show the code
addAll <- function(x, ...) {
  args <- list(...)
  for (a in args) {
    x <- x + a
  }
  return(x)
}
addAll(3, 4, 5, 6, 7, 8)
#> [1] 33

使用list() 将传入的 ... 转换为列表,然后再进行处理

Show the code
i01 <- function(y, z) {
  list(y = y, z = z)
}

i02 <- function(x, ...) {
  i01(...)
}
str(i02(x = 1, y = 2, z = 3))
#> List of 2
#>  $ y: num 2
#>  $ z: num 3
i02(x = 1, y = 2, z = 3)
#> $y
#> [1] 2
#> 
#> $z
#> [1] 3

特殊形式 ..N,按位置引用参数,使用 ..1、..2 等直接引用 … 对象中的第 1 个元素、第 2 个元素。

Show the code
i03 <- function(...) {
  list(first = ..1, third = ..3)
}
str(i03(1, 2, 3))
#> List of 2
#>  $ first: num 1
#>  $ third: num 3

list(...)计算参数并将它们存储在列表中

Show the code
i04 <- function(...) {
  list(...)
}
str(i04(a = 1, b = 2))
#> List of 2
#>  $ a: num 1
#>  $ b: num 2

21.10 退出函数

返回一个对象,或者,错误

21.10.1 隐式返回值

最后计算的表达式是返回值

Show the code
j01 <- function(x) {
  if (x < 10) {
    0
  } else {
    10
  }
}
j01(5)
#> [1] 0

j01(15)
#> [1] 10

21.10.2 显示返回值

调用return()

Show the code
j02 <- function(x) {
  if (x < 10) {
    return(0)
  } else {
    return(10)
  }
}

21.10.3 不可见值

大多数函数都以可见方式返回:在交互式上下文中调用函数会自动打印结果

Show the code
j03 <- function() 1
j03()
#> [1] 1

通过应用invisible()于最后一个值来阻止自动打印:

Show the code
j04 <- function() invisible(1)
j04()

若要验证此值是否确实存在,可以显式打印它或将其括在括号中,或者使用返回值和可见性标志:withVisible()

Show the code
print(j04())
#> [1] 1


(j04())
#> [1] 1

withVisible(j04())
#> $value
#> [1] 1
#> 
#> $visible
#> [1] FALSE

不可见返回的最常见函数是:<-

Show the code
a <- 2
(a <- 2)
#> [1] 2

a <- b <- c <- d <- 2

21.10.4 Errors

如果函数无法完成其分配的任务,则应抛出错误,并立即终止函数的执行。

Show the code
j05 <- function() {
  stop("I'm an error")
  return(10)
}
j05()
#> Error in j05(): I'm an error

21.10.5 退出处理程序

Show the code
j06 <- function(x) {
  cat("Hello\n")
  on.exit(cat("Goodbye!\n"), add = TRUE) # 使用  add = TRUE 时始终设置
  
  if (x) {
    return(10)
  } else {
    stop("Error")
  }
}

j06(TRUE)
#> Hello
#> Goodbye!
#> [1] 10


j06(FALSE)
#> Hello
#> Error in j06(FALSE): Error
#> Goodbye!

21.11 函数形式

  • prefix: foofy(a, b, c)
  • infix: x + y
  • replacement:names(df) <- c("a", "b", "c")
  • special:[[, if ,for
Show the code
x <- 2
y <- 1
x + y
#> [1] 3
`+`(x, y)
#> [1] 3


for(i in 1:10) print(i)
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> [1] 5
#> [1] 6
#> [1] 7
#> [1] 8
#> [1] 9
#> [1] 10
`for`(i, 1:10, print(i))
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> [1] 5
#> [1] 6
#> [1] 7
#> [1] 8
#> [1] 9
#> [1] 10

21.11.1 前缀形式

是 R 代码中最常见的形式,参数按确切名称匹配,然后与唯一前缀匹配,最后按位置匹配。

Show the code
k01 <- function(abcdef, bcde1, bcde2) {
  list(a = abcdef, b1 = bcde1, b2 = bcde2)
}
str(k01(2, 3, abcdef = 1))
#> List of 3
#>  $ a : num 1
#>  $ b1: num 2
#>  $ b2: num 3
str(k01(2, 3, a = 1))  # 部分匹配
#> List of 3
#>  $ a : num 1
#>  $ b1: num 2
#>  $ b2: num 3
options(warnPartialMatchArgs = TRUE)
x <- k01(a = 1, 2, 3)

21.11.2 中缀形式

Show the code
`%+%` <- function(a, b) paste0(a, b)
"new " %+% "string"
#> [1] "new string"

21.11.3 替换形式

Show the code
`second<-` <- function(x, value) {
  x[2] <- value
  x
}
x <- 1:10
second(x) <- 5L
x
#>  [1]  1  5  3  4  5  6  7  8  9 10

21.11.4 特殊形式

所有特殊形式都作为原函数 实现(即在 C 中),这意味着打印这些函数没有信息。

Show the code
`for`
#> .Primitive("for")
formals(`for`)
#> NULL