高级编程--创建函数

在R中处处是函数。算数运算符+、-、/和*实际上也是函数。例如,2 + 2等价于 "+"(2, 2)。

1. 函数语法

函数的语法格式是:

functionname <- function(parameters){

 statements

 return(value)

}

如果函数中有多个参数,那么参数之间用逗号隔开。

参数可以通过关键字和/或位置来传递。另外,参数可以有默认值。请看下面的函数:

f <- function(x, y, z=1){

  result <- x + (2*y) + (3*z)

  return(result)

}

f(2,3,4)    #通过位置传递

 高级编程--创建函数(函数语法、对象范围、环境)_高级编程

f(2,3)    #通过位置传递

 高级编程--创建函数(函数语法、对象范围、环境)_默认值_02

f(x=2, y=3)     #通过关键字传递

 高级编程--创建函数(函数语法、对象范围、环境)_高级编程_03

f(z=4, y=2, 3)    #y和z是通过关键字传递的

 高级编程--创建函数(函数语法、对象范围、环境)_默认值_04

可以使用args()函数来观测参数的名字和默认值:

args(f)

 高级编程--创建函数(函数语法、对象范围、环境)_数据_05

args()被设计用于交互式观测。如果你需要以编程方式获取参数名称和默认值,可以使用formals()函数。它返回含有必要信息的列表。

参数是按值传递的,而不是按地址传递。请看下面这个函数语句:

result <- lm(height ~ weight, data=women)

result

 高级编程--创建函数(函数语法、对象范围、环境)_默认值_06

2. 对象范围

在典型情况下,有如下几点:

(1)在函数之外创建的对象是全局的(也适用于函数内部)。在函数之内创建的对象是局部的(仅仅适用于函数内部)。

(2)局部对象在函数执行后被丢弃。只有那些通过return()函数(或使用算子<<-分配)传回的对象在函数执行之后可以继续使用。

(3)全局对象在函数之内可被访问(可读)但是不会改变(除非使用<<-算子)。

(4)对象可以通过参数传递到函数中,但是不会被函数改变。传递的是对象的副本而不是变量本身。

x <- 2

y <- 3

z <- 4

f <- function(w){

  z <- 2

  x <- w*y*z

  return(x)

}

f(x)

 高级编程--创建函数(函数语法、对象范围、环境)_默认值_07

x

 高级编程--创建函数(函数语法、对象范围、环境)_数据_08

结果分析:在这个例子中,x的一个副本被传递到函数f()中,但是初始的x不变。y的值通过环境得到。尽管z存在于环境中,但是在函数中设置的值被使用并不改变在环境中的值。

 

3  环境

 

在R中,环境包括框架和外壳。框架是符号-值(对象名称及其内容)的集合,外壳是指向封闭环境的一个指针。封闭环境也称为父环境。R允许人们在语言内部操作环境,以便达到对范围的细微控制以及函数和数据的分离。

 

在互动部分,当你第一次看到R的提示时,你处于全局环境。你可以通过new.env()函数创建一个新的环境并通过assign()函数在环境中创建任务。对象的值可以通过get()函数从环境

 

中得到。这里有一个例子:

 

x <- 5

 

myenv <- new.env()

 

assign("x", "Homer", env=myenv)

 

ls()

 

 高级编程--创建函数(函数语法、对象范围、环境)_搜索_09

 

 ls(myenv)

 

 高级编程--创建函数(函数语法、对象范围、环境)_默认值_10

 

x

 

 高级编程--创建函数(函数语法、对象范围、环境)_默认值_11

 

 get("x", env=myenv)

 

 高级编程--创建函数(函数语法、对象范围、环境)_搜索_12

 

结果分析:在全局环境中存在一个称为x的对象,其值为5。一个称为x的对象还存在于myenv的环境中,其为"Homer"。

 

另外使用assign()和get()函数时可以使用$符号。例如:

 

myenv <- new.env()

 

myenv$x <- "Homer"

 

myenv$x

 

 高级编程--创建函数(函数语法、对象范围、环境)_搜索_13

 

parent.env()函数展示了父环境。继续这个例子,myenv的父环境就是全局环境:

 

parent.env(myenv)

 

 高级编程--创建函数(函数语法、对象范围、环境)_搜索_14

 

全局环境的父环境是空环境。

 

因为函数是对象,所以它们也有环境。这在探讨函数闭包(function closure,以创建时状态被打包的函数)时非常重要 。请看由另一个函数创建的函数:

 

trim <- function(p){

 

  trimit <- function(x){

 

    n <- length(x)

 

    lo <- floor(n*p) + 1

 

    hi <- n + 1 - lo

 

    x <- (x, partial = unique(c(lo, hi)))[lo:hi]

 

  }

 

  trimit

 

}

 

trim(p)函数返回一个函数,即从矢量中修剪掉高低值的p%:

 

x <- 1:10

 

trim10pct <- trim(.1)

 

y <- trim10pct(x)

 

y

 

 高级编程--创建函数(函数语法、对象范围、环境)_数据_15

 

trim20pct <- trim(.2)

 

y <- trim20pct(x)

 

y

 

 高级编程--创建函数(函数语法、对象范围、环境)_搜索_16

 

这样做是因为p值在trimit()函数的环境中并被保存在函数中:

 

ls(environment(trim10pct))

 

 高级编程--创建函数(函数语法、对象范围、环境)_搜索_17

 

get("p", env=environment(trim10pct))

 

 高级编程--创建函数(函数语法、对象范围、环境)_搜索_18

 

一般情况下,对象的值是从本地环境中获得的。如果未在局部环境中找到对象,R会在父环境中搜索,然后是父环境的父环境,直到对象被发现。如果R搜索到空环境仍未搜索到对象,它会抛出一个错误。我们把它称为词法域(lexical scoping)。

 


作者:zhang-X,转载请注明原文链接​