# 可重用和面向对象编程

## 回到基础

##### 清单 1. 创建向量并分配维
> v = 1:1000
> typeof(v)
[1] "integer"
> attributes(v)
NULL
> dim(v) = c(10,10,10)  # (Re)dimension
> attributes(v)
\$dim
[1] 10 10 10
> v2 = matrix(1:1000, nrow=100, ncol=10)
> typeof(v2)
[1] "integer"
> attributes(v2)
\$dim
[1] 100  10
> attr(v2,'dim') = c(10,10,10)  # Redimension
> attributes(v2)
\$dim
[1] 10 10 10

##### 清单 2. 在矩阵向量上按行操作
> m = matrix(1:12, nrow=3, ncol=4)
> m
[,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> sum(m)  # sum of all elements of m
[1] 78
> sum(m[1,])  # sum of first row
[1] 22

##### 清单 3. 执行多个行操作的错误方式
> sum(m[c(1,2,3),])  # NOT sum of each row
[1] 78

##### 清单 4. 用 apply() 函数进行行操作
> apply(m, 1, sum) # by row
[1] 22 26 30
> apply(m, 2, sum) # by column
[1]  6 15 24 33
> apply(m, c(1,2), sum) # by column AND row (sum of each single cell)
[,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> a = array(1:24,c(3,4,2))
> apply(a, c(1,2), sum)  # sum by depth in 3-D array
[,1] [,2] [,3] [,4]
[1,]   14   20   26   32
[2,]   16   22   28   34
[3,]   18   24   30   36
> apply(a, c(3), sum)    # sum of each depth slice
[1]  78 222

## 无限序列

##### 清单 5. 用爱拉托逊斯筛法创建所有素数的 Haskell 列表
primes :: [Int]
primes = sieve [2 .. ]
sieve (x:xs) = x : sieve [y | y <- xs, (y `rem` x)/=0]

##### 清单 6. 定义一个向量和一个动态扩展它的方法
inf_vector = rnorm(10, 0, 1)   # arbritrarily start w/ init 10 items
assure <- function(index) {
extend_by = max(index-length(inf_vector), 0)
extras = rnorm(extend_by, 0, 1)
v <- c(inf_vector, extras)
assign("inf_vector", v, env=.GlobalEnv)
return(index)
}
getRand <- function(index) {
assure(index)
return(inf_vector[index])
}

##### 清单 7. 使用包装器函数作为无限虚拟向量的代理
> getRand(3)                # Single index
[1] 0.5557101
> getRand(1:5)              # Range
[1] -0.05472011 -0.30419695  0.55571013  0.91667175 -0.40644081
> getRand(sqrt(c(4,16)))    # Computed index collection
[1] -0.3041970  0.9166717
> getRand(100)              # Force background vector extension
[1] 0.6577079

##### 清单 8. 在访问前扩展向量（如果需要的话）
> assure(2000)
[1] 2000
> inf_vector[1500]
[1] 1.267652

## 面向对象的 R

R 能够进行完全面向对象的编程，但是要理解这种方式，需要重新回顾一下您对 OOP 的认识。Java™ 和 C++ 或者 Python、Ruby 或 Smalltalk 等语言的用户可能已经对面向对象形成了一种相当明确的认识。这是对的，但是面向对象并不只限于一种模型。

R 的 OOP 方式基于泛型函数（generic function），而不是基于类层次结构。对于使用过 Lisp 的 CLOS 或者读过我对使用 Python 进行多种分派的讨论（参考资料）的读者，这个概念应该是熟悉的。不幸的是，R 的方式仍然是单一分派方式；在这方面，它与 C++、Java 等 “传统”语言相同。

“第一个” 的意思比较微妙（参见 参考资料 中对 Python 中 MRO 设计的讨论）。R 采用相同的决策方式，但是它将继承的概念从内部转到了外部。R 并不用一系列 来定义和覆盖各种方法，而是创建一系列泛型函数，这些函数带有一个标记，指出它们应该在什么类型的对象上进行操作。

### 泛型函数

##### 清单 9. 创建泛型函数和一些标记方法
#------------- Create a generic method
> whoami <- function(x, ...) UseMethod("whoami")
> whoami.foo <- function(x) print("I am a foo")
> whoami.bar <- function(x) print("I am a bar")
> whoami.default <- function(x) print("I don't know who I am")

##### 清单 10. 用类成员关系标记对象
> a = 1:10
> b = 2:20
> whoami(a)                 # No class assigned
[1] "I don't know who I am"
> attr(a,'class') <- 'foo'
> attr(b,'class') <- c('baz','bam','bar')
> whoami(a)
[1] "I am a foo"
> whoami(b)                 # Search MRO for defined method
[1] "I am a bar"
> attr(a,'class') <- 'bar'  # Change the class of 'a'
> whoami(a)
[1] "I am a bar"

##### 清单 11. 每个方法的分派解析
> meth1 <- function(x) UseMethod("meth1")
> meth1.Mom <- function(x) print("Mom's meth1")
> meth2 <- function(x) UseMethod("meth2")
> meth1(a)   # Even though meth1.Dad exists, Mom comes first for a
[1] "Mom's meth1"
> meth2(a)

### 包含祖先

##### 清单 12. 用很简单的包装器函数实现基于继承的 MRO
char0 = character(0)
makeMRO <- function(classes=char0, parents=char0) {
# Create a method resolution order from an optional
# explicit list and an optional list of parents
mro <- c(classes)
for (name in parents) {
mro <- c(mro, name)
ancestors <- attr(get(name),'class')
mro <- c(mro, ancestors[ancestors != name])
}
return(mro)
}
NewInstance <- function(value=0, classes=char0, parents=char0) {
# Create a new object based on initial value,
# explicit classes and parents (all optional)
obj <- value
attr(obj,'class') <- makeMRO(classes, parents)
return(obj)
}
MaternalGrandma <- NewInstance()
PaternalGrandma <- NewInstance()
Mom <- NewInstance(classes='Mom', parents='MaternalGrandma')
Me <- NewInstance(value='Hello World', 'Me', c('Mom','Dad'))

##### 清单 13. 具有继承式 MRO 的对象
> print(Me)
[1] "Hello World"
attr(,"class")
[5] "Uncle"           "PaternalGrandma"

### 再论无限向量

R 中的操作符只是进行函数调用的简写方式；可以自由地专门化操作符在类上的行为，产生任何其他函数调用的效果。采用这种方式，可以修复第一个系统中的一些缺点：

• 希望能够根据需要生成任意数量的无限向量。
• 希望能够配置使用的随机分布。
• 希望能够用另一个向量中的值对一个无限随机向量进行初始化。

##### 清单 14. 定义一个可索引的无限随机向量
"[.infinite_random" <- function(v, index) {
name <- attr(v, 'name')
rfunc <- attr(v, 'rfunc')
extend_by = max(index-length(v), 0)
extras = rfunc(extend_by)
new <- c(v, extras)
makeInfiniteRandomVector(name, v=new, rfunc)
return(new[index])
}
unitnorm <- function(n) return(rnorm(n,0,1))
empty <- vector('numeric', 0)
makeInfiniteRandomVector <- function(name, v=empty, rfunc=unitnorm) {
# Create an infinite vector
# optionally extend existing vector, configurable rand func
attr(v,'class') <- 'infinite_random'
attr(v,'name') <- name
attr(v,'rfunc') <- rfunc
assign(name, v, env=.GlobalEnv)
}
makeInfiniteRandomVector('v')
# makeInfiniteRandomVector('inf_poisson', rfunc=my_poisson)
# Usage is just, e.g.: v[1]; v[10]; v[9:12]; etc.

##### 清单 15. 打印无限向量
print.infinite_random <- function(v) {
a_few = 5
len = length(v)
end_range = (len-a_few)+1
cat('* Infinite Random Vector *\n')
cat('[1] ', v[1:a_few], '...\n')
cat('[')
cat(end_range)
cat('] ', v[end_range:len], '...\n')
}

##### 清单 16. 打印无限向量的示例
> v[1000]
[1] -1.341881
> print(v)
* Infinite Random Vector *
[1]  -0.6247392 1.308057 1.654919 1.691754 -2.251065 ...
[996]  1.027440 0.8376 -0.7066545 -0.7778386 -1.341881 ...

## 结束语

#### 评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux
ArticleID=163255
ArticleTitle=使用 R 编写统计程序，第 3 部分: 可重用和面向对象编程
publish-date=03092006