erlang 尾递归

前面的博文中说过递归和尾递归的区别.在ERLANG中,到处存在的都是递归,或者说我们实现的时候优先考虑的应该是尾递归.

那应该如何去写好尾递归呢?

递归,除去List Comprehensions,在ERLANG中唯一存在的循环结构就是递归了.所以在ERLANG中递归是需要重点掌握的概念,

当然,正确写好ERLANG递归程序也不是简单的事情.

这几天大量搜索了一下ERLANG关于递归程序写法,就在这总结一下.

Example One:

[plain] view plain copy
  1. |  %% @Func Duplicate_Forest  
  2. |  %% @Breif 给定一个参数,复制N次返回一个List  
  3. |  Duplicate_Forest(0,_) ->  
  4. |           [];  
  5. |  Duplicate_Forest(N,Term) when N > 0 ->  
  6. |           [Term|Duplicate_Forest(N-1,Term)].  


上面给的Example One是普通的递归写法,程序算是完成了,但是在ERLANG世界里,这样子的程序可是不合格的.

所以需要将这段代码改成尾递归的形式才算是合格.

Tail Recursion实现:

[plain] view plain copy
  1. | %% @Func Tail_Duplicate_Forest  
  2. | %% @Breif 给定一个参数,复制N次返回一个List(尾递归实现)  
  3. | Tail_Duplicate_Forest(N,Term) ->  
  4. |           Tail_Duplicate_Forest(N,Term,[]).  
  5.    
  6. | Tail_Duplicate_Forest(0,_,List) ->  
  7. |           List;  
  8. | Tail_Duplicate_Forest(N,Term,List) when N > 0 ->  
  9. |           Tail_Duplicate_Forest(N-1, Term, [Term|List]).  


前一篇博文中说过,尾递归的实现需要借助辅助变量,是对递归的"尾"调用的实现,结果都保存在形参中.

所在在上面的尾递归的实现中,添加了第三个辅助变量"List",形参List一直被用来传递上一次迭代调用的结果,作为下一次迭代调用的形参.

递归需要递归的终止条件.而该片段代码的终止条件是N的值为0,所以只需要无条件返回调用结果,而结果就保存在我们的辅助变量List中.

而我们在程序的参数要求方面是两个:N/Term,所以需要辅助变量初始化为[].

Example Two:

[plain] view plain copy
  1. | %% @Func Reverse_Forest  
  2. | %% @Breif 倒转List中的Element  
  3. | Reverse_Forest([]) ->  
  4. |           [];  
  5. | Reverse_Forest([H|T]) ->  
  6. |           Reverse_Forest(T)++[H].  


这同样是普通递归实现的一个小函数,函数的功能是实现对List的元素倒置.同样,需要将其改为合格的ERLANG尾递归程序.

Tail Recursion实现:

首先记住我们需要的参数只有一个,但是需要一个辅助变量,所以第一个函数分支是比较好些的.

[plain] view plain copy
  1. | Tail_Recursion_Forest(List) ->  
  2. |           Tail_Recursion_Forest(List,[]);   

下面需要的是对参数的检查,如果List是[],该怎么处理.

[plain] view plain copy
  1. | Tail_Recursion_Forest([],Acc) -> Acc;  

最后就应该是尾递归的迭代实现部分了.注意调用的结果是通过形参传递就可以了.而Acc就是辅助变量,所以调用应该通过Acc传递.

 

[plain] view plain copy
  1. | Tail_Recursion_Forest([H|T],Acc) ->  
  2. |           Tail_Recursion_Forest(T,[H|Acc]).  

 

所以尾递归实现的完整代码如下:

[plain] view plain copy
  1. | %% @Func Reverse_Forest  
  2. | %% @Breif 倒转List中的Element(尾递归实现)  
  3. | Tail_Recursion_Forest(List) ->  
  4. |           Tail_Recursion_Forest(List,[]).      
  5. | Tail_Recursion_Forest([],Acc) -> Acc;  
  6. | Tail_Recursion_Forest([H|T],Acc) ->  
  7. |           Tail_Recursion_Forest(T,[H|Acc]).  


Example Three:

 

[plain] view plain copy
  1. | %% @Func Sublist_Forest  
  2. | %% @Breif 返回一个List中的前N个Elements  
  3. | Sublist_Forest(_,0) -> [];  
  4. | Sublist_Forest([],_) -> [];  
  5. | Sublist_Forest([H|T],N) when N > 0 ->  
  6. |           [H|Sublist_Forest(T,N-1)].  


下面是尾递归实现的代码:

Tail Recursion实现:

[plain] view plain copy
  1. | %% @Func Tail_Sublist_Forest  
  2. | %% @Breif 返回一个List中的前N个Elements(尾递归实现)  
  3. | Tail_Sublist_Forest(List,N) -> Tail_Sublist_Forest(List,N,[]).  
  4. | Tail_Sublist_Forest(_,0,SubList) -> SubList;  
  5. | Tail_Sublist_Forest([],_,SubList) -> SubList;  
  6. | Tail_Sublist_Forest([H|T],N,SubList) ->   
  7. |           Tail_Sublist_Forest(T,N-1,[H|SubList]).  

Tips:

tail recursion as seen here is not making the memory grow because
when the virtual machine sees a function calling itself in a tail position
(the last expression to be evaluated in a function), it eliminates the current
stack frame. This is called tail-call optimisation (TCO) and it is a special case
of a more general optimisation named Last Call Optimisation (LCO).

LCO is done whenever the last expression to be evaluated in a function body
is another function call. When that happens, as with TCO, the Erlang VM avoids
storing the stack frame. As such tail recursion is also possible between multiple
functions. As an example, the chain of functions a() -> b(). b() -> c(). c() -> a().
will effectively create an infinite loop that won't go out of memory as LCO avoids
overflowing the stack. This principle, combined with our use of accumulators
is what makes tail recursion useful.

上面这段小贴士的主要是说明Tail Recursion为什么和普通的Recursion不同,不会造成Stack Flow.

同时也说明了EVM对Tail Recursion的优化.

Erlang , Recursion World !!!! ~~~~ ~,~


 


内容概要:本文围绕可变桨叶四旋翼无人机的规范控制与点对点运动模拟展开,重点研究优化推力分配策略在翻转动作中的应用与性能比较。通过Matlab代码实现,构建了四旋翼动力学模型,并设计了多种控制算法以实现精确的姿态调整与轨迹跟踪。研究对比了不同推力分配方案在执行高机动性翻转动作时的稳定性、能耗效率与响应速度,旨在提升无人机在复杂飞行任务中的动态性能与控制精度。该仿真研究为无人机飞控系统的设计与优化提供了理论依据和技术支持。; 适合人群:具备一定自动控制理论基础和Matlab编程能力,从事无人机控制、飞行器动力学或机器人系统研究的科研人员及研究生。; 使用场景及目标:① 实现四旋翼无人机在三维空间中的精确点对点运动控制;② 对比分析不同推力分配策略在执行翻转等高难度动作时的控制效果与能耗表现,优化飞行性能;③ 为无人机自主飞行、特技飞行及复杂环境下的机动控制提供算法验证平台。; 阅读建议:此资源以Matlab仿真为核心,建议读者结合相关控制理论知识,深入理解代码实现细节,重点关注动力学建模、控制律设计与推力分配模块。在学习过程中,应动手调试参数,复现文中翻转动作的仿真结果,并尝试拓展至其他复杂飞行任务,以加深对无人机控制机理的理解。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值