How does the recursive call work in this erlang fu

2019-07-19 11:01发布

问题:

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

so I am kinda unsure of what the A and B would be and how the next recursive call would be. let say 2,2

so it would be

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}

but where does A and B go and do they change in each recursive call?

回答1:

As a very basic explanation of "where is my variable", consider the countdown function in this example module:

-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).

If we hit the base case, which is 0, then we stop. The return value of the function overall is the atom ok (because that is the return value of a successful call to io:format/2).

If the input is greater than 0 then we match on the second clause, which means we assign N the sole input argument for this particular iteration. The next thing we do is make our output call. Then we assign Next to the value N - 1. Then we call the same function again (do a loop) using the value of Next in body of the the current call as the input argument.

The next iteration all the variables are totally new because this is a fresh execution context. The old N and Next no longer exist. In fact, they don't event exist on a stack anywhere because Erlang uses "tail call optimization" to maintain recursive tail calls in constant space, the same way most other languages would do an explicit for or while or do while or [insert form].

As Alexy points out, be careful about the token fun -- it is a keyword in Erlang, not a legal function name. It is the non-name of an anonymous function (also known as a lambda). In other words, unless you provide a label, the name of every anonymous function is just fun.

fun is also the keyword that is used to reference a function by label (to use as a value itself) instead of calling it. For example, countdown(10) calls the function above with an argument of 10. Referencing the function as fun countdown/1 returns the function itself as a value. That is, incidentally, why the function export declaration at the top of the module is written as -module([countdown/1]), because that is the explicit name of this function. Consider this:

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

While I'm on the subject...

Erlang has very few keywords compared to most languages (and very little syntax, actually). Here is the list of reserved words:

after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse receive rem try when xor



回答2:

You just need to go back up:

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

(note that fun is a keyword in Erlang and that f(N,M) is not the same as f({N,M}))

and do they change in each recursive call

Yes, as you can see.