再帰的な書き方に頭が馴染んできた: Elixir

exercismでElixirの問題を解いていた時、いろいろなるほどなーと面白かったのでメモ。

仕様は、入力(sentence)に対して単語区切りごとに、sentence全体の文字をキーに含まれる文字をカウントする、というもの。

例えば、

“abc de fge”

を入力とすると、abc、de、fgeがそれぞれ1つづつ存在するとカウントする。

“abc de abc”

を入力とすると、abcが2つ、deが1つ存在するとカウントする、というもの。

Elixirだと、繰り返しは再帰的に書く。数列的な書き方になるので、少し頭になじませる時間が必要でしたが、再帰的な書き方に考えが馴染むと数学のようにかけて、個人的には書きやすかったです。プログラムだと手続き型に慣れていたけれど、関数的にかけると数学的な考え方をそのまま残せるのが良いですね。

defmodule Words do
  @spec count(String.t) :: map()
  def count(sentence) do

    dict = %{}
    String.downcase(sentence)
    |> String.split(~r/[^\w-]|_/u)
    |> count_up(dict)
  end

  defp count_up([head|tail], dict) do
    if head != "" do
      dict = Dict.update(dict, head, 1, fn x -> x + 1 end)
    end
    count_up(tail, dict)
  end

  defp count_up([], dict) do
    dict
  end
end

これを、さらにreduceを使って書いてみます。

defmodule Words do
  @spec count(String.t) :: map()
  def count(sentence) do

    String.downcase(sentence)
    |> String.split(~r/[^\w-]|_/u,  trim: true)
    |> Enum.reduce(%{}, &count_up/2)
  end

  defp count_up(head, dict) do
    Dict.update(dict, head, 1, fn x -> x + 1 end)
  end
end

Elixir1.0.4におけるreduceの説明は以下。

reduce(collection, fun) (function) # ↑
Specs:

reduce(t, (element, any -> any)) :: any
Invokes fun for each element in the collection passing that element and the accumulator acc as arguments. fun‘s return value is stored in acc. The first element of the collection is used as the initial value of acc. Returns the accumulator.

Examples
iex> Enum.reduce([1, 2, 3, 4], fn(x, acc) -> x * acc end)
24

Elixir、なるほどね。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中