[Elixir in Action]Erlang/Elixirの再帰計算におけるnon-tail recursionとtail recursion

ちょっと印象的だったので。

再帰計算を行う関数の最後が、別の関数呼び出しかどうか、という違いです。

non tail recursion

defmodule ListHelper do
  def sum([]), do: 0

  def sum([head | tail]) do
    head + sum(tail)
  end
end

https://github.com/sasa1977/elixir-in-action/tree/master/code_samples/ch03

tail recursion

defmodule ListHelper do
  def sum(list) do
    do_sum(0, list)
  end

  defp do_sum(current_sum, []) do
    current_sum
  end

  defp do_sum(current_sum, [head | tail]) do
    new_sum = head + current_sum
    do_sum(new_sum, tail)
  end
end

https://github.com/sasa1977/elixir-in-action/blob/master/code_samples/ch03/sum_list_tc.ex

Dive to source code

Elixirには、再帰表現をまとめたコードとして Enum.reduce(collection, acc, fun) なんかを提供しています。
この中身を少しみてみましょう。

Elixirの の再帰箇所は以下のように書かれています。

def reduce(collection, acc, fun) when is_list(collection) do
  :lists.foldl(fun, acc, collection)
end

Erlangの資料によると、 :lists.foldl(Fun, Acc0, List) はtail recursionとのこと。
こちら

foldl/3 is tail recursive and would usually be preferred to foldr/3.

ということは、 Enum.reduce(collection, acc, fun) はtail recursionなのですね。

コード追うと Enum.reduce/2Enum.reduce/3 を結局は呼ぶので、reduceはtail recursionで実装されていて、巨大なリストに対してもちゃんと再帰計算ができることを重視されているのですね。
Elixirの List.foldr/3List.foldl/3 もそれらを呼び出しているので、tail recursionなのですね。

利点として、このtail recursionの場合、メモリの消費を増やすことなく、無限に再帰呼び出しができると。
一方、これは手続き型な書き方な側面や、性能的にnon tail recursionよりも遅い場合があります。

なるほどなるほど。


[更新]20150806

Erlangの :lists.foldr(Fun, Acc0, List):lists.foldl(Fun, Acc0, List) に書き直しました。

Advertisements

One thought on “[Elixir in Action]Erlang/Elixirの再帰計算におけるnon-tail recursionとtail recursion

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s