簡體   English   中英

Elixir尾調用遞歸函數

[英]Elixir tail-call recursive function

我有這個函數,在列表中找到偶數,並返回一個只包含這些數字的新列表:

  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

這是否已經優化了尾調用? 或者每個子句都必須在最后調用自己(“偶數”函數的第二個版本不是)? 如果沒有,如何重構為尾調用遞歸?

我知道這可以通過過濾器或減少來完成,但我想嘗試沒有它。

你是對的,這個函數不是尾遞歸的,因為第二個子句的最后一個調用是列表前置操作,而不是對它自己的調用。 要使這個尾遞歸,你將不得不使用累加器。 由於積累是反向發生的,因此在第一個子句中,您需要反轉列表。

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

但是在Erlang中,你的“body-recursive”代碼會自動優化,並且可能不會比尾部遞歸解決方案慢,后者在最后執行:lists.reverse調用。 Erlang文檔建議在這種情況下將兩個結果中的任何一個寫入更干凈的代碼中。

根據神話,使用尾遞歸函數,反向構建一個列表,然后調用lists:reverse/1比以正確順序構建列表的body-recursive函數更快; 原因是body-recursive函數比tail-recursive函數使用更多的內存。

在R12B之前,這在某種程度上是正確的。 在R7B之前更是如此。 今天,不是那么多。 身體遞歸函數通常使用與尾遞歸函數相同的內存量。 通常不可能預測尾遞歸或身體遞歸版本是否會更快。 因此,請使用使代碼更清晰的版本(提示:它通常是正文遞歸版本)。

有關尾部和體遞歸的更全面討論,請參閱Erlang的尾部遞歸不是銀彈

神話:尾遞歸函數比遞歸函數快得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM