Erlang递归

本文介绍了Erlang中的递归概念,通过一个递归演示例子展示了普通递归在处理大规模计算时可能导致大量内存消耗。接着讨论了尾递归的优势,它在递归调用作为函数末尾的唯一操作时能有效减少资源消耗。文章总结了线性递归与尾递归的差异,并提及使用LISTS:FOLDL/FOLDR作为更高效的选择,但强调实际项目中应考虑优化算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Erlang递归

Erlang是一种函数式编程语言,需要记住的是所有函数式编程语言,它们不提供任何循环结构。 相反,函数式编程依赖于一种称为递归的概念。

编程语言中,函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。递归函数不能定义为内联函数

递归demo1

测试递归和性能

tets(N) ->
  Result = sum(N),
  io:format("~p~n", [Result]),
  erlang:process_info(self()).

sum(1) ->
  1;
sum(N) ->
  N + sum(N - 1).

结果:

(gs_framework@192.168.77.41)5> lib_test:tets(100000000).
5000000050000000
[{current_function,{lib_test,tets,1}},
 {initial_call,{erlang,apply,2}},
 {status,running},
 {message_queue_len,0},
 {messages,[]},
 {links,[<0.55.0>]},
 {dictionary,[]},
 {trap_exit,false},
 {error_handler,error_handler},
 {priority,normal},
 {group_leader,<0.54.0>},
 {total_heap_size,237289198},
 {heap_size,237288211},
 {stack_size,25},
 {reductions,99951263},
 {garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,
                                       size => 0}},
                      {min_bin_vheap_size,46422},
                      {min_heap_size,233},
                      {fullsweep_after,65535},
                      {minor_gcs,46}]},
 {suspending,[]}]
(gs_framework@192.168.77.41)6> 

{heap_size,237288211},32位系统1word为4byte,64位系统1word为8byte,237288211word换算成内存为1,810.36537933349609375M(237288211*8/1024/1024)

?????????????

这么辣鸡吗 就算个1+2+3+4+。。。+100000000 用了老子1810M内存???

拉闸啊!!!不,等下 这只是递归 八四尾递归 那我们用尾递归看看

尾递归

如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。

test2(N) ->
  Result = sum1(N),
  io:format("~p~n", [Result]),
  erlang:process_info(self()).

sum1(N) ->
  sum1(N, 0).
sum1(0, Result) ->
  Result;
sum1(N, Result) ->
  sum1(N - 1, N + Result).
(gs_framework@192.168.77.41)4> lib_test:test2(100000000 ).
5000000050000000
[{current_function,{lib_test,test2,1}},
 {initial_call,{erlang,apply,2}},
 {status,running},
 {message_queue_len,0},
 {messages,[]},
 {links,[<0.55.0>]},
 {dictionary,[]},
 {trap_exit,false},
 {error_handler,error_handler},
 {priority,normal},
 {group_leader,<0.54.0>},
 {total_heap_size,3196},
 {heap_size,1598},
 {stack_size,25},
 {reductions,99959793},
 {garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,
                                       size => 0}},
                      {min_bin_vheap_size,46422},
                      {min_heap_size,233},
                      {fullsweep_after,65535},
                      {minor_gcs,3}]},
 {suspending,[]}]

{heap_size,1598},换算为0.0121917724609375 牛逼啊

总结QAQ

普通的线性递归比尾递归更加消耗资源, 在实现上说, 每次重复的过程
调用都使得调用链条不断加长. 系统不得不使用栈进行数据保存和恢复.而尾递归就不存在这样的问题, 因为他的状态完全由N和Result保存.

PS 那用LISTS:FOLDL/FOLDR来循环呢

List =lists:seq(1, N),
  Fun = fun(Number, ResultStr) ->
    ResultStr + Number
        end,
  Out = lists:foldl(Fun, 0, List),
  io:format("~p~n", [Out])

以上方法使匿名函数Fun只要生成一次,作为参数放到lists函数即可,避免每次循环匿名函数都会重新生成,数组如果是提前生成的 使用lists:foldl只会消耗CPU性能不消耗内存哦。。(lists:seq(1,10000000)生成数组 但是消耗内存哦)

不过一般写这样的打死吧。。毕竟算法可以优化。项目这样写会被在头上爆扣

(PS:为什么写这个,因为面试了数字游戏公司,主程问erlang有什么和别的语言不同主要体现在哪几点,巴拉巴拉说到了没循环,用递归和尾递归,区别在于前者易于堆栈溢出后者不会,回答完了主程又问我 为什么要用尾递归 。。。算了,那么问题来了 为什么要用尾递归啊)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值