# R语言高级编程-表达式、数学公式与特殊符号

y <- function(x) log(x) + sqrt(x) + x^(1/3)
lot(y, 1, 1000, main = expression(y == log(x) + sqrt(x) + sqrt(x, 3)), lwd = 3,
col = "blue")

# 一、R语言的“表达式”

``````## Class "expression" [package "methods"]
##
## No Slots, prototype of class "expression"
##
## Extends: "vector"
getClass("language")
## Virtual Class "language" [package "methods"]
##
## No Slots, prototype of class "name"
##
## Known Subclasses:
## Class "name", directly
## Class "call", directly
## Class "{", directly
## Class "if", directly
## Class "<-", directly
## Class "for", directly
## Class "while", directly
## Class "repeat", directly
## Class "(", directly
## Class ".name", by class "name", distance 2, with explicit coerce
``````

# 二、产生“表达式”的函数

### 1、expression 函数

expression函数可以有一个或多个参数，它把全部参数当成一个列表，每个参数都被转成一个表达式向量，所以它的返回值是表达式列表，每个元素都是表达式类型对象，返回值的长度等于参数的个数：

``````(ex <- expression(x = 1, 1 + sqrt(a)))
## expression(x = 1, 1 + sqrt(a))
length(ex)
## [1] 2
ex[1]
## expression(x = 1)
mode(ex[1])
## [1] "expression"
typeof(ex[1])
## [1] "expression"
ex[2]
## expression(1 + sqrt(a))
mode(ex[2])
## [1] "expression"
typeof(ex[2])
## [1] "expression"
``````

expression(x+11=1)

### 2、quote函数

quote函数只能有一个参数。quote函数的返回值一般情况下是call类型，表达式参数是单个变量的话返回值就是name类型，如果是常量那么返回值的存储模式就和相应常量的模式相同：

• quo()
• expr(expr)
• enexpr(arg)
• exprs(
...,
.named = FALSE,
.ignore_empty = c("trailing", "none", "all"),
.unquote_names = TRUE
)
• enexprs(
...,
.named = FALSE,
.ignore_empty = c("trailing", "none", "all"),
.unquote_names = TRUE,
.homonyms = c("keep", "first", "last", "error"),
.check_assign = FALSE
)
• ensym(arg)
• ensyms(
...,
.named = FALSE,
.ignore_empty = c("trailing", "none", "all"),
.unquote_names = TRUE,
.homonyms = c("keep", "first", "last", "error"),
.check_assign = FALSE
)
• quo(expr)
• enquo(arg)
• quos(
...,
.named = FALSE,
.ignore_empty = c("trailing", "none", "all"),
.unquote_names = TRUE
)
• enquos(
...,
.named = FALSE,
.ignore_empty = c("trailing", "none", "all"),
.unquote_names = TRUE,
.homonyms = c("keep", "first", "last", "error"),
.check_assign = FALSE
)
``````(cl <- quote(1 + sqrt(a) + b^c))
## 1 + sqrt(a) + b^c
mode(cl)
## [1] "call"
typeof(cl)
## [1] "language"
(cl <- quote(a))
## a
mode(cl)
## [1] "name"
typeof(cl)
## [1] "symbol"
(cl <- quote(1))
## [1] 1
mode(cl)
## [1] "numeric"
typeof(cl)
## [1] "double"
``````

quote返回值如果是name或常量类型，它的长度就是1；如果是call类型，返回值长度就与函数/运算符的参数个数n对应，长度等于n+1，多出的长度1是函数/符号名。

``````length(quote(a))  #name或常量类型，返回值长度为1
## [1] 1
length(quote(!a))  #单目运算符，返回值长度为2
## [1] 2
length(quote(-b))  #单目运算符，返回值长度为2
## [1] 2
length(quote(a + b))  #双目运算符，返回值长度为3
## [1] 3
length(quote((a + b) * c))  #多个运算符只算优先级最低的一个
## [1] 3
``````

### 3、quote、bquote 、 substitute 函数（base R函数）

``````substitute(expr, env)
quote(expr)
enquote(cl)
``````
``````bquote(1 + sqrt(a) + b^c) == quote(1 + sqrt(a) + b^c)
## [1] TRUE
substitute(1 + sqrt(a) + b^c) == quote(1 + sqrt(a) + b^c)
## [1] TRUE
``````

``````a <- 3
b <- 2
(bq <- bquote(y == sqrt(.(a), .(b))))
## y == sqrt(3, 2)
(ss <- substitute(y == sqrt(a, b), list(a = 3, b = 2)))
## y == sqrt(3, 2)
bq == ss
## [1] TRUE
``````

bquote函数的帮助档说这个函数类似于LISP的backquote宏，对于像我这样的LISP盲，使用substitute函数好一些。
substitute函数的典型用途是替换表达式中的变量，如果我们希望在表达式中使用变量并且希望这些变量在运行过程中做出相应改变，就可以使用substitute函数。

``````par(mar = rep(0.1, 4), cex = 2)
plot.new()
plot.window(c(0, 10), c(0, 1))
for (i in 1:9) text(i, 0.5, substitute(sqrt(x, a), list(a = i + 1)))
``````

### 4、parse 函数

parse函数用于从文件读取文本作为表达式，返回的值是expression类型，这函数也很有用。后面有例子。

# 三、表达式规则与paste函数：

``````x <- 1
x + "x"
## Error: 二进列运算符中有非数值参数
expression(x + "x")
## expression(x + "x")
quote(x + "x")
## x + "x"
``````

``````expression(x + +++y)
## expression(x + +++y)
expression(x - ---y)
## expression(x - ---y)
# expression(x****y) (Not run) expression(x////y) (Not run)
# expression(1<=x<=4) (Not run)
quote(x + +++y)
## x + +++y
quote(x - ---y)
## x - ---y
# quote(x****y) (Not run) quote(x////y) (Not run) quote(1<=x<=4) (Not run)
``````

+ - 运算连续使用不出错是因为它们还可以当成求正/负值运算的符号。 在表达式产生函数中使用paste函数可以解决这样的问题。在这种条件下，paste对参数的处理方式和表达式产生函数一样，检查运算符但不检查变量名。用NULL作为运算符的参数可以获得意外的效果：

``````ex <- expression(paste(x, "////", y))
cl <- quote(paste(x, "****", y))
par(mar = rep(0.1, 4), cex = 2)
plot.new()
plot.window(c(0, 1.2), c(0, 1))
text(0.2, 0.5, ex)
text(0.6, 0.5, cl)
cl <- quote(paste(1 <= x, NULL <= 4))
text(1, 0.5, cl)
``````

# 四、R绘图函数对文本参数中的表达式的处理

quote, bquote 和 substitute 的返回值有三种类型call, name 和 常量，事实上expression 函数的结果最终也是这三种类型。因为expression函数的结果是expression列表，我们取列表元素的值检查看看：

``````(ex <- expression(1 + sqrt(x), x, 1))
## expression(1 + sqrt(x), x, 1)
ex[[1]]
## 1 + sqrt(x)
mode(ex[[1]])
## [1] "call"
typeof(ex[[1]])
## [1] "language"
ex[[2]]
## x
mode(ex[[2]])
## [1] "name"
typeof(ex[[2]])
## [1] "symbol"
ex[[3]]
## [1] 1
mode(ex[[3]])
## [1] "numeric"
typeof(ex[[3]])
## [1] "double"
``````

``````par(mar = rep(0.1, 4), cex = 2)
plot.new()
plot.window(c(0, 1.2), c(0, 1))
text(0.2, 0.5, ex[1])
text(0.6, 0.5, ex[2])
text(1, 0.5, ex[3])
``````

name 和常量类型都很简单，直接输出文本，而call类型就不好判断了。我们前面说过call类型返回值的长度与函数/运算符的参数个数有关。这是怎么体现的呢？由于文本参数最终得到的是文本，我们用as.character函数来看看：

``````as.character(quote(x - y))
## [1] "-" "x" "y"
as.character(quote(1 - x + y))
## [1] "+"     "1 - x" "y"
as.character(quote((1 + x) * y))
## [1] "*"       "(1 + x)" "y"
as.character(quote(!a))
## [1] "!" "a"
as.character(quote(sqrt(x)))
## [1] "sqrt" "x"
``````

``````2 + 4
## [1] 6
2 - 4
## [1] -2
2 <= 4
## [1] TRUE
2 >= 4
## [1] FALSE
``````

R绘图函数对表达式中包含的函数名和它们的参数首先应用Tex文本格式化规则进行处理，这种规则的具体情况可以使用 ?plotmath 进行查看，主要是一些数学公式和符号的表示方法。把这个说明文档中字符串拷贝到maths.txt文件中并保存到当前工作目录后可以用下面的代码做出后面的表格：

``````ex <- parse("maths.txt")
n <- length(ex)
par(mar = rep(0.1, 4), cex = 0.8)
plot.new()
plot.window(c(0, 8), c(0, n/4))
y <- seq(n/4, by = -1, length = n/4)
x <- seq(0.1, by = 2, length = 4)
xy <- expand.grid(x, y)
text(xy, labs, adj = c(0, 0.5))
xy <- expand.grid(x + 1.3, y)
text(xy, ex, adj = c(0, 0.5), col = "blue")
box(lwd = 2)
abline(v = seq(1.3, by = 2, length = 4), lty = 3)
abline(v = seq(2, by = 2, length = 3), lwd = 1.5)
``````

``````ex <- expression(sqrt(x), x + y, x^2, x %in% A, x <= y, mean(x, y, z), x | y,
x & y)
n <- length(ex)
par(mar = rep(0.1, 4), cex = 1.5)
col <- c("red", "blue")
plot.new()
plot.window(c(0, n), c(0, 1))
for (i in 1:n) text(i - 0.5, 0.5, ex[i], colcol = col[i%%2 + 1])
``````

• 序言：七十年代末，一起剥皮案震惊了整个滨河市，随后出现的几起案子，更是在滨河造成了极大的恐慌，老刑警刘岩，带你破解...
沈念sama阅读 155,457评论 4 357
• 序言：滨河连续发生了三起死亡事件，死亡现场离奇诡异，居然都是意外死亡，警方通过查阅死者的电脑和手机，发现死者居然都...
沈念sama阅读 65,943评论 1 285
• 文/潘晓璐 我一进店门，熙熙楼的掌柜王于贵愁眉苦脸地迎上来，“玉大人，你说我怎么就摊上这事。” “怎么了？”我有些...
开封第一讲书人阅读 105,327评论 0 236
• 文/不坏的土叔 我叫张陵，是天一观的道长。 经常有香客问我，道长，这世上最难降的妖魔是什么？ 我笑而不...
开封第一讲书人阅读 43,307评论 0 201
• 正文 为了忘掉前任，我火速办了婚礼，结果婚礼上，老公的妹妹穿的比我还像新娘。我一直安慰自己，他们只是感情好，可当我...
茶点故事阅读 51,630评论 3 283
• 文/花漫 我一把揭开白布。 她就那样静静地躺着，像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上，一...
开封第一讲书人阅读 40,114评论 1 202
• 那天，我揣着相机与录音，去河边找鬼。 笑死，一个胖子当着我的面吹牛，可吹牛的内容都是我干的。 我是一名探鬼主播，决...
沈念sama阅读 31,514评论 2 305
• 文/苍兰香墨 我猛地睁开眼，长吁一口气：“原来是场噩梦啊……” “哼！你这毒妇竟也来了？” 一声冷哼从身侧响起，我...
开封第一讲书人阅读 30,227评论 0 193
• 序言：老挝万荣一对情侣失踪，失踪者是张志新（化名）和其女友刘颖，没想到半个月后，有当地人在树林里发现了一具尸体，经...
沈念sama阅读 33,859评论 1 234
• 正文 独居荒郊野岭守林人离奇死亡，尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
茶点故事阅读 30,186评论 2 238
• 正文 我和宋清朗相恋三年，在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
茶点故事阅读 31,741评论 1 255
• 序言：一个原本活蹦乱跳的男人离奇死亡，死状恐怖，灵堂内的尸体忽然破棺而出，到底是诈尸还是另有隐情，我是刑警宁泽，带...
沈念sama阅读 28,087评论 2 248
• 正文 年R本政府宣布，位于F岛的核电站，受9级特大地震影响，放射性物质发生泄漏。R本人自食恶果不足惜，却给世界环境...
茶点故事阅读 32,614评论 3 228
• 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹，春花似锦、人声如沸。这庄子的主人今日做“春日...
开封第一讲书人阅读 25,926评论 0 8
• 文/苍兰香墨 我抬头看了看天上的太阳。三九已至，却和暖如春，着一层夹袄步出监牢的瞬间，已是汗流浃背。 一阵脚步声响...
开封第一讲书人阅读 26,630评论 0 190
• 我被黑心中介骗来泰国打工， 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留，地道东北人。 一个月前我还...
沈念sama阅读 35,117评论 2 261
• 正文 我出身青楼，却偏偏与公主长得像，于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子，可洞房花烛夜当晚...
茶点故事阅读 35,076评论 2 257