简体   繁体   English

Elixir尾调用递归函数

[英]Elixir tail-call recursive function

I have this function that finds the even numbers in a list and returns a new list with only those numbers: 我有这个函数,在列表中找到偶数,并返回一个只包含这些数字的新列表:

  def even([]), do: []

  def even([head | tail]) when rem(head, 2) == 0 do
    [head | even(tail)]
  end

  def even([_head| tail]) do
    even(tail)
  end

Is this already tail-call optimized? 这是否已经优化了尾调用? Or does every clause have to call itself at the end (the second version of the "even" function doesn't)? 或者每个子句都必须在最后调用自己(“偶数”函数的第二个版本不是)? If not, how can it be refactored to be tail-call recursive? 如果没有,如何重构为尾调用递归?

I know this can be done with filter or reduce but I wanted to try without it. 我知道这可以通过过滤器或减少来完成,但我想尝试没有它。

You're right that this function is not tail recursive because the second clause's last call is the list prepend operation, not a call to itself. 你是对的,这个函数不是尾递归的,因为第二个子句的最后一个调用是列表前置操作,而不是对它自己的调用。 To make this tail-recursive, you'll have to use an accumulator. 要使这个尾递归,你将不得不使用累加器。 Since the accumulation happens in reverse, in the first clause you'll need to reverse the list. 由于积累是反向发生的,因此在第一个子句中,您需要反转列表。

def even(list), do: even(list, [])

def even([], acc), do: :lists.reverse(acc)

def even([head | tail], acc) when rem(head, 2) == 0 do
  even(tail, [head | acc])
end

def even([_head| tail], acc) do
  even(tail, acc)
end

But in Erlang, your "body-recursive" code is automatically optimized and may not be slower than a tail-recursive solution which does a :lists.reverse call at the end. 但是在Erlang中,你的“body-recursive”代码会自动优化,并且可能不会比尾部递归解决方案慢,后者在最后执行:lists.reverse调用。 The Erlang documentation recommends writing whichever of the two results in cleaner code in such cases. Erlang文档建议在这种情况下将两个结果中的任何一个写入更干净的代码中。

According to the myth, using a tail-recursive function that builds a list in reverse followed by a call to lists:reverse/1 is faster than a body-recursive function that builds the list in correct order; 根据神话,使用尾递归函数,反向构建一个列表,然后调用lists:reverse/1比以正确顺序构建列表的body-recursive函数更快; the reason being that body-recursive functions use more memory than tail-recursive functions. 原因是body-recursive函数比tail-recursive函数使用更多的内存。

That was true to some extent before R12B. 在R12B之前,这在某种程度上是正确的。 It was even more true before R7B. 在R7B之前更是如此。 Today, not so much. 今天,不是那么多。 A body-recursive function generally uses the same amount of memory as a tail-recursive function. 身体递归函数通常使用与尾递归函数相同的内存量。 It is generally not possible to predict whether the tail-recursive or the body-recursive version will be faster. 通常不可能预测尾递归或身体递归版本是否会更快。 Therefore, use the version that makes your code cleaner (hint: it is usually the body-recursive version). 因此,请使用使代码更清晰的版本(提示:它通常是正文递归版本)。

For a more thorough discussion about tail and body recursion, see Erlang's Tail Recursion is Not a Silver Bullet . 有关尾部和体递归的更全面讨论,请参阅Erlang的尾部递归不是银弹

Myth: Tail-Recursive Functions are Much Faster Than Recursive Functions. 神话:尾递归函数比递归函数快得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM