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回ループが回ってるだけだからね。
なんだか面倒臭い感じがするのですが、向いてないってことなのかなぁ…。