循环和函数式“批处理”
学会了批处理创建变量,很容易就能掌握批处理修改变量名字,批处理修改文件名字等等。这里的引申,是想玩玩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),1, function(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.循环
也是一行,很直接。