ElixirのAgentモジュールを使って値の更新とかメモ化とか

Elixirの変数は変更不能らしいです。変化出来ない数、変数。ふしぎ。 安全性が高くて良いのですが、Webのアクセスカウンター的なものを作りたいときとか、メモ化とかやりたいときに困ります。 というときに登場するのがAgentってやつらしいです。

とりあえず使ってみる

defmodule Counter do
    def counter do
    Agent.update(:counter, fn x -> x + 1 end)
    Agent.get(:counter, fn x -> x end)
    end

    def start_agent do
    Agent.start_link fn -> 0 end, name: :counter
    end
end

Counter.start_agent
IO.inspect Counter.counter
IO.inspect Counter.counter
IO.inspect Counter.counter

こんな感じで使います。

Counter.start_agent関数でAgentとやらを開始しています。スレッドみたいなもので、変数を保持しておいてくれるらしい。 第一引数は初期値を返す関数で、第二引数は識別子を渡しています。 識別子さえ変えておけば複数のAgentを立ち上げても大丈夫。

Counter.counter関数を実行すると、Agent.update関数を使って値を更新して、Agent.get関数で値を取得しています。 単純にカウントするだけだけどちょっと面倒臭い…。

メモ化のために使ってみる

メモ化といえばフィボナッチ数ということで、フィボナッチ数の実装をやってみました。

defmodule Memorize do
    def fib 0 do
    0
    end

    def fib 1 do
    1
    end

    def fib x do
    case Agent.get(:fib_memo, &Map.get(&1, x)) do
        nil ->
        v = fib(x - 1) + fib(x - 2)
        Agent.update(:fib_memo, &Map.put(&1, x, v))
        v

        v -> v
    end
    end

    def start_agent do
    Agent.start_link &Map.new/0, name: :fib_memo
    end
end


Memorize.start_agent
IO.inspect Memorize.fib 1024

Agentにmapを持っておいてもらって、fib関数が呼ばれる度に検索しにいっています。 見付かったらその値をそのまま返し、無ければ計算して保存する感じ。

メモ化しておけば1024とかで計算させてもすごいスピードが出ます。 事実上は1024回ループが回ってるだけだからね。


なんだか面倒臭い感じがするのですが、向いてないってことなのかなぁ…。

参考: Go VS Elixir (1)フィボナッチ数列 - 技術備忘記