基于语言的计算

上一节介绍了 R 中的函数式编程工具,理解了函数只是另一种可以被传递的对象。当
创建一个新函数 fun( ) 时,也同时创建了一个与函数相关联的环境。这个环境被称作函
数的封闭环境,可以通过 environment(fun) 访问。每次调用该函数时,一个新的包含
尚未求值的实参(也称为 promise )的执行环境就会被创建,以支持函数的执行,这也是惰
性求值的基础。执行环境的父环境是函数的封闭环境,也是词法作用域的基础。
函数式编程允许我们在高阶抽象层级上编写代码,元编程则更进一步。它允许我们调
整语言本身,使特定的语言结构在特定情况下更方便使用。一些流行的 R 扩展包在其函数
中使用元编程以简化问题。本节将展示元编程的能力和它的优缺点,以便了解相关扩展包
和函数的工作方式。
在深入了解其工作原理之前,我们先看一些使用元编程的内置函数是如何使事情变得
简单的。
假设我们想把内置数据集 iris 的每个数值列中超过 80% 的项筛选出来。
标准方法是通过编写逻辑向量按行选取数据框子集:
iris[iris$Sepal.Length > quantile(iris$Sepal.Length, 0.8) &
iris$Sepal.Width > quantile(iris$Sepal.Width, 0.8) &
iris$Petal.Length > quantile(iris$Petal.Length, 0.8) &
iris$Petal.Width > quantile(iris$Petal.Width, 0.8), ]
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 110 7.2 3.6 6.1 2.5 virginica
## 118 7.7 3.8 6.7 2.2 virginica
## 132 7.9 3.8 6.4 2.0 virginica
上述代码中,每次调用 quantile( ) 函数,就给某列设置一个 80% 的阈值。代码可以运
行,但非常繁琐,因为每次使用一列数据,都要从 iris$开始,以上代码总共出现了 8 次 iris$。
内置函数 subset( ) 可以有效简化上述问题:
subset(iris,
Sepal.Length > quantile(Sepal.Length, 0.8) &
Sepal.Width > quantile(Sepal.Width, 0.8) &
Petal.Length > quantile(Petal.Length, 0.8) &
Petal.Width > quantile(Petal.Width, 0.8))
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 110 7.2 3.6 6.1 2.5 virginica
## 118 7.7 3.8 6.7 2.2 virginica
## 132 7.9 3.8 6.4 2.0 virginica
返回结果完全相同,但是看起来更整洁。为什么上述代码省略了 iris$依旧可以运行,
而前面代码省略了就不能运行呢?
iris[Sepal.Length > quantile(Sepal.Length, 0.8) &
Sepal.Width > quantile(Sepal.Width, 0.8) &
Petal.Length > quantile(Petal.Length, 0.8) &
Petal.Width > quantile(Petal.Width, 0.8), ]
## Error in `[.data.frame`(iris, Sepal.Length > quantile(Sepal.Length, 0.8)
& : 找不到对象'Sepal.Length'
这段代码不能正常运行,是因为 Sepal.Length 和其他列在计算子集表达式的域(或环境)
中没有定义。然而,subset( ) 函数使用元编程技术调整了其参数的计算环境,使表达式
Sepal.Length > quantile(Sepal.Length, 0.8) 在包含 iris 所有列的环境中被计算。
此外,subset( ) 不仅适用于行的选取,也同样适用于列的选取。例如,我们可以
直接将列名作为变量来指定 select 参数,而不必使用字符向量:
subset(iris,
Sepal.Length > quantile(Sepal.Length, 0.8) &
Sepal.Width > quantile(Sepal.Width, 0.8) &
Petal.Length > quantile(Petal.Length, 0.8) &
Petal.Width > quantile(Petal.Width, 0.8),
select = c(Sepal.Length, Petal.Length, Species))
## Sepal.Length Petal.Length Species
## 110 7.2 6.1 virginica
## 118 7.7 6.7 virginica
## 132 7.9 6.4 virginica
如你所见,subset( ) 调整了它的第 2 个参数(subset)和第 3 个参数(select)
的计算方式。因此,我们可以编写更精简的代码。
接下来的几个小节里,我们将会学习代码背后的机制及其工作原理。

捕获和修改表达式

执行表达式

非标准计算

posted @ 2019-02-11 10:40  NAVYSUMMER  阅读(208)  评论(0编辑  收藏  举报
交流群 编程书籍