Scala之尾递归
不久前突然看到有人发帖问尾递归,感觉非常奇怪,这东西怎么从来没听说过呢?最近学到Scala才明白为什么需要尾递归。尾递归这东西挺有意思的,由于FP的immutability的特性,使得不可以使用循环,因为循环会改变变量的值,和FP的理念冲突。而如果不用循环就只能使用递归了。可是递归平时都是大家尽量避免的,因为很容易造成stackoverflow,因此就需要尾递归了。而尾递归的的本质其实还是循环,因此编译器最终把尾递归优化成循环来执行了。看到了没有,实际上尾递归解决的问题就是immutable的recursion,很简单吧?
在把递归转换成尾递归中,有一个道理非常重要。递归是表达问题的一个方式,而循环是寻找解决问题的一个方式。尾递归的本质是循环而不是递归。因此在转换的过程中,不要把思路拘泥于递归上,而应该扩展到循环上。理论上来讲,任何递归都可以转换成循环,因为尾递归的本质是循环,因此任何递归也应该都可以转换为尾递归。这是我的理解,因此又回顾那个帖子,当时大家认为不是所有的递归都可以转换成尾递归,这和我的理解相违背,因此我就尝试把一些大家认为无法转换的问题重新用尾递归的方式来诠释了。下面是我的代码。因为初学,一定不是最佳,大家见笑了。
1. Get binary tree height
-------------------------------------------------------------------------------
def getHeight(root:TreeNode):Int={
recursion(0,List(root))
}
def recursion(layer:Int,list:List[TreeNode]):Int= list match{
caseNil=> layer
case _=> recursion(layer+1, process(list))
}
defprocess(list:List[TreeNode]):List[TreeNode]={
varnewlist:List[TreeNode]=Nil
for(node<-list){
if(!node.left.isEmpty)newlist=node.left.get::newlist
if(!node.right.isEmpty)newlist=node.right.get::newlist
}
newlist
}
2.
int C(int m, int n)
{
if ((n == 0)|| (n == m))
return 1;
else
return C(m - 1, n) + C(m - 1, n - 1);
}
-------------------------------------------------------------------------------
def C(m:Int, n:Int):Int={
recursion(m, 0,None)(n)
}
def recursion(m:Int, i:Int,arr:Option[Array[Int]]):Array[Int]= i match{
case _if i==m+1 => arr.get
case 0=> recursion(m, 1, Option(Array[Int](0)))
case _=> val ret=process(i,arr.get);recursion(m, i+1, Option(ret))
}
def process(i:Int,arr:Array[Int]):Array[Int]={
valar=new Array[Int](i+1)
ar(0)=1
ar(i)=1
1 until iforeach(j=>ar(j)=arr(j)+arr(j-1))
ar
}
3.
public static double H(double val, int n)
{
if (n ==0)
return val;
else
{
double left = V(val - 1, n - 1);
double right = V(val + 1, n - 1);
return Math.Sqrt(Math.Abs(left * right));
}
}
public static double V(double val, int n)
{
if (n ==0)
return val;
else
{
double up = H(val * 2, n - 1);
double down = H(val / 2, n - 1);
return Math.Sqrt(Math.Abs(up + down));
}
}
---------------------------------------------------------------------------------------
def H(value:Double,n:Int):Double={
H_helper(Array(value),n)(0)
}
def V(value:Double,n:Int):Double={
V_helper(Array(value),n)(0)
}
def H_helper(arr:Array[Double],n:Int):Array[Double]=n match{
case 0=> caclV(arr)
case _=> {
val ar=newArray[Double](arr.length*2)
for(i<-0 until arr.length) {
ar(2*i)=arr(i)-1
ar(2*i+1)=arr(i)+1
}
V_helper(ar, n-1)
}
}
def V_helper(arr:Array[Double],n:Int):Array[Double]=n match{
case 0=> caclH(arr)
case _=>{
val ar=newArray[Double](arr.length*2)
for(i<-0 until arr.length){
ar(2*i)=arr(i)*2
ar(2*i+1)=arr(i)/2
}
H_helper(ar, n-1)
}
}
defcaclH(arr:Array[Double]):Array[Double]={
if(arr.length==1) return arr
valar=new Array[Double](arr.length/2)
for(i<- 0 untilar.length){
ar(i)=math.sqrt((arr(2*i)*arr(2*i+1)).abs)
}
caclV(ar)
}
defcaclV(arr:Array[Double]):Array[Double]={
if(arr.length==1) return arr
valar=new Array[Double](arr.length/2)
for(i<- 0 untilar.length){
ar(i)=math.sqrt((arr(2*i)+arr(2*i+1)).abs)
}
caclH(ar)
}
注意第三题Scala不支持对这种尾递归的优化,因此需要用到TailCalls才行。我用了一下还是挺容易用的。
转载地址:blog.sina.com.cn/s/blog_b9285de20101i5s1.html