【问题标题】:Recursive function call hanging, Erlang递归函数调用挂起,Erlang
【发布时间】:2015-07-04 04:18:27
【问题描述】:

我目前正在自学 Erlang。一切都很顺利,直到我发现这个功能有问题。

-module(chapter).
-compile(export_all).

list_length([])      ->   0;
list_length([_|Xs])  ->   1+list_length([Xs]).

这是从教科书中取出的。当我使用 OTP 17 运行此代码时,它只是挂起,这意味着它只是如下所示。

1> c(chapter).
{ok,chapter}
2> chapter:list_length([]).
0
3> chapter:list_length([1,2]).

查看任务管理器时,Erlang OTP 正在使用 200 Mb 到 330 Mb 的内存。这是什么原因造成的。

【问题讨论】:

    标签: windows memory erlang erlang-shell


    【解决方案1】:

    它不会终止,因为您在每种情况下都在创建一个新的非空列表:[Anything] 始终是一个非空列表,即使该列表包含一个空列表作为其唯一成员([[]] 是一个成员的非空列表)。

    正确的列表如下终止:[ Something | [] ]。

    所以考虑到这一点......

    list_length([])      ->   0;
    list_length([_|Xs])  ->   1 + list_length(Xs).
    

    在大多数函数式语言中,“正确的列表”是 cons 样式的列表。查看the Wikipedia entry on "cons" 和the Erlang documentation about lists,然后思考一下您在示例代码中看到的列表操作示例。

    注意事项

    1. 在运算符周围放置空格是一件好事;它将防止您使用彼此相邻的箭头和二进制语法运算符来做混淆的事情,同时避免其他一些歧义(无论如何它更容易阅读)。

    2. 1234563 ,它必须在堆栈上留下一个引用。要让任何东西加 1,必须完成list_length/1 的执行,返回一个值,在这种情况下,记住该挂起值的次数与列表中的成员数一样多。阅读 Steve 的回答,了解如何使用 accumulator 值编写尾递归函数。

    【讨论】:

    • 谢谢,这很有帮助。
    【解决方案2】:

    由于 OP 正在学习 Erlang,还要注意 list_length/1 函数不适合 tail call optimization 因为它的加法操作,这需要运行时递归调用函数,取它的返回值,加 1 到它,并返回结果。这需要堆栈空间,这意味着如果列表足够长,您可以用完堆栈。

    请考虑这种方法:

    list_length(L)           -> list_length(L, 0).
    list_length([], Acc)     -> Acc;
    list_length([_|Xs], Acc) -> list_length(Xs, Acc+1).
    

    这种方法在 Erlang 代码中很常见,它在 list_length/1 中创建一个累加器来保存长度值,将其初始化为 0 并将其传递给执行递归的 list_length/2。然后每次调用list_length/2 都会增加累加器,当列表为空时,list_length/2 的第一个子句返回累加器作为结果。但请注意,这里的加法操作发生在递归调用发生之前,这意味着调用是真正的尾调用,因此不需要额外的堆栈空间。

    对于非初学者的 Erlang 程序员,使用 erlc -S 编译该模块的原始版本和修改版本并检查生成的 Erlang 汇编程序可能是有益的。在原始版本中,汇编程序包含allocate 调用堆栈空间并使用call 进行递归调用,其中call 是正常函数调用的指令。但是对于这个修改版本,不会生成allocate 调用,而是使用call 执行递归,而不是使用call_only,这是针对尾调用优化的。

    【讨论】:

      猜你喜欢
      • 2018-06-16
      • 1970-01-01
      • 2014-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-03
      • 1970-01-01
      相关资源
      最近更新 更多