vlambda博客
学习文章列表

循环和函数式“批处理”

学会了批处理创建变量,很容易就能掌握批处理修改变量名字,批处理修改文件名字等等。这里的引申,是想玩玩R和matlab中的函数式编程。


如何用R将工作空间中a1,a2,a3,到a100这100个变量乘以2变成b1到b100。


请留意对比R和Matlab的做法,其间的不同体现了各自的特色。用Mathematica做算是彩蛋。


一、R的做法

1. 循环

初步想的答案如下

for(i in 1:100){ temp = get( paste("a",i,sep="") ) assign( paste("b",i,sep="") ,2*temp)}

答案中,循环体写成了两行,容易理解。这两行分别用 `get`+字符串获取变量值,赋给中间变量temp,尔后用assign+字符串,创建新变量并赋值。


把两行合并,就可以简化为一行循环体

for(i in 1:100){  assign(  paste("b",i,sep="")  ,2*get( paste("a",i,sep="") ))}


2. 函数式

每当此时,我就很介意,因为循环体只有一行,for头就占了一行,感觉有些“浪费”,我通常会使用函数式编程搞定。原因是,一行语句方便形成一个匿名函数,将循环批处理转为函数式批处理。

apply(as.matrix(1:100),1function(i){assign(paste("b",i,sep="")  ,2*get(paste("a",i,sep=""))) })

apply作为R core中的函数,实现这个很容易;也可以用类似的map函数,不过得加载包,就不做了。


二、Matlab的做法

1. 循环

matlab可以充分利用字符串数组的特点,将赋值语句形成字符串,再使用eval执行之。可以看到循环的做法比R简单多了:

for k =1:100 eval(['b',num2str(k),'=2*a',num2str(k)])end

2. 函数式

类似R里的apply函数族(lapply,sapply等),matlab也有fun函数族(arrayfun, cellfun, structfun),方便做函数式处理

arrayfun( @(k) assignin('base',['b',num2str(k)], 2*evalin('base',['a',num2str(k)]) ), 1:100 )

在这个例子中,相对于循环的做法麻烦的地方是,匿名函数中须要提取global中的变量,并在global中新建变量。而一旦进入函数体,创建的变量都是local的,因而要有local和global的互通,就有了`evalin()`和`assignin()`两个函数。


三、比较

R的变量环境设计,由循环转为函数式很直接;Matlab需要注意环境的变化。看起来R更容易。

但是,R即便是循环都是麻烦的,其繁琐度和matlab的函数式相近。函数名字R(get和assign)就和matlab(evalin和assignin)相像。


你可能好奇,R也有eval函数,能否仿照写成matlab循环的方式,也是可以哒

for(i in 1:100){  eval( parse(text=paste("b",i,"=2*a",i,sep="")) )}

思路和matlab循环类似,通过paste连接字符串,使用parse将字符串转化为表达式(expression),最后再用eval执行表达式。

然而,这种使用eval的方式,在笔者看来可能会隐藏eval更精妙的地方,即它的第二个参数,即对于环境的设定,使得R里的eval函数有点类似于matlab里的evalin。


四、MMA的做法

MMA的函数式是天生的,并且不建议用循环,因此函数式在前。

1.函数式

MMA支持带下标的名字,所以对这个问题简直是太轻松了,我能想到好多种办法,先玩两种

(1)

(2)

2.循环

也是一行,很直接。