递归调用如何在这个erlang函数中工作?

时间:2017-10-22 11:17:28

标签: function recursion erlang

fun({0, M}) -> {M+1, M-2};
fun({N, M}) ->
 {A, B} = fun({N-1, M+1}),
            {B, A+1}.

所以我不确定A和B是什么以及下一次递归调用将是什么。比方说2,2

所以它会是

f(2,2) -> {A,B} = fun({1,3}), {B,A+1}
f(1,3) -> {A,B} = fun({0,4}), {B,A+1}
f(0,4) -> {5,2}

但是A和B去哪里,他们在每次递归调用中都会改变?

2 个答案:

答案 0 :(得分:2)

作为“我的变量在哪里”的非常基本解释,请考虑此示例模块中的倒计时功能:

-module(example).
-export([countdown/1]).


-spec countdown(non_neg_integer()) -> ok.

countdown(0) ->
    io:format("Blastoff!~n");
countdown(N) when N > 0 ->
    ok = io:format("Counting down in: ~w~n", [N]),
    Next = N - 1,
    countdown(Next).

如果我们点击基本情况,即0,那么我们就会停止。整个函数的返回值是原子ok(因为这是成功调用io:format/2的返回值)。

如果输入大于0,那么我们匹配第二个子句,这意味着我们为此特定迭代分配N唯一的输入参数 。我们接下来要做的就是进行输出调用。然后我们将Next分配给值N - 1。然后我们使用当前调用的主体中Next的值作为输入参数再次调用相同的函数(执行循环)。

下一次迭代所有变量都是全新的,因为这是一个全新的执行上下文。旧的NNext不再存在。实际上,它们并不存在于任何地方的堆栈上,因为Erlang使用“tail call optimization”来维护constant space中的递归尾调用,就像大多数其他语言明确for一样。或whiledo while或[插入表单]。

正如Alexy指出的那样,请注意令牌fun - 它是Erlang中的关键字,而不是合法的函数名称。它是anonymous function的非名称(也称为lambda)。换句话说,除非您提供标签,否则每个匿名函数的名称只是fun

fun也是用于通过标签引用函数的关键字(用作值本身)而不是调用它。例如,countdown(10)使用10参数调用上面的函数。 引用函数fun countdown/1将函数本身作为值返回。顺便说一下,为什么模块顶部的函数导出声明被写为-module([countdown/1]),因为这是该函数的显式名称。考虑一下:

1> c(example).
{ok,example}
2> example:countdown(2).
Counting down in: 2
Counting down in: 1
Blastoff!
ok
3> Countdown = fun example:countdown/1.
#Fun<example.countdown.1>
4> Countdown(2).
Counting down in: 2
Counting down in: 1
Blastoff!
ok

我在谈论这个话题时......

与大多数语言相比,Erlang的关键字非常少(实际上语法很少)。这是the list of reserved words

  

之后和andalso乐队开始bnot bsl bsr bxor case catch cond div end fun如果不让或者orelse接收rem尝试当xor

答案 1 :(得分:1)

你只需要重新开始:

f({1, 3}) -> {A, B} = {5, 2}, {B, A+1} -> {2, 6}
f({2, 2}) -> {A, B} = {2, 6}, {B, A+1} -> {6, 3}

(请注意,fun是Erlang中的关键字,f(N,M)f({N,M})不同。)

  

并且每次递归调用都会改变

是的,正如你所看到的那样。