How recursion works in Elixir

A simple function in Elixir that returns a list of numbers from to:

defmodule MyList do

  def span(_), do: raise "Should be 2 args"
  def span(from, to) when from > to,  do: [ to | span(to + 1, from) ]
  def span(from, to) when from < to,  do: [ from | span(from + 1, to) ]
  def span(from, to) when from == to, do: [ from ]
end

I have no idea why this works and returns a list of numbers.

MyList.span(1,5)
#=> [1,2,3,4,5]

I just can't think it over:

[ from | span(from + 1, to) ]

Well, the first loop, I suppose, will return the following:

[ 1 | span(2, 5) ]

What's next? [ 1, 2 | span(3, 5) ]? Why?

How does he know when to stop? Why does it even work?

Please do not chase points - do not worry if you do not try to make something clear (er) for the functional noob programmer (OO programmer).

As a bonus to your answer, could you give me tips on how to start thinking recursively? Is there a panacea?

How does he track his head? How does a function create a new list at each iteration, preserving the values ​​obtained in the previous one?

Thanks!

+4
1

, .

Erlang call-by-value. :

[call-by-value - ] , .

, Elixir (, , Erlang) , (, , ) , .

, :

def add(a, b), do: a + b

, , :

add(10 * 2, 5 - 3)
# becomes:
add(20, 2)
# kind of becomes:
20 + 2
# which results in:
22

, call-by-value, | . , :

|(1, [])         #=> [1]
|(29, [1, 2, 3]) #=> [29, 1, 2, 3]

, | ( , - ).

span(1, 5), (, ) :

|(1, span(2, 5))

, | , 1 span(2, 5), span(2, 5). :

|(1, |(2, span(3, 5)))
|(1, |(2, |(3, span(4, 5))))
|(1, |(2, |(3, |(4, span(5, 5)))))
|(1, |(2, |(3, |(4, [5]))))))
# now, it starts to "unwind" back:
|(1, |(2, |(3, [4, 5])))
|(1, |(2, [3, 4, 5]))
|(1, [2, 3, 4, 5])
[1, 2, 3, 4, 5]

(, |(), , | ).

, " , []". (span(1, 5)) [1|span(2, 5)]. , span(1, 5) , [1|span(2, 5)]: , ! span(2, 5) ..

-, : , . , , :

# First call is pushed on the stack
span(1, 5)
# Second call is pushed on top of that
span(1, 5), span(2, 5)
# ...
span(1, 5), span(2, 5), ..., span(5, 5)
# hey! span(5, 5) is not recursive, we can return [5]. Let pop span(5, 5) from the stack then
span(1, 5), ..., span(4, 5)
# Now span(4, 5) can return because we know the value of span(5, 5) (remember, span(4, 5) is expanded to [4|span(5, 5)]

, span(1, 5) ( span(1, [2, 3, 4, 5])) , , [1, 2, 3, 4, 5].

, , , - :). , , . , , ; , :

+13
source

Source: https://habr.com/ru/post/1617514/


All Articles