スポンサーリンク

2015年8月20日

[Elixir]Keyword listsのkeyによって処理を変える一例

とある錬金術師の万能薬(Elixir)

Goal

キーワードリストのkeyによって処理を変える一例。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4

Text

キーワードリストを使ってarguments的なものをやってみる。

・forで行う簡単な例

defmodule OptionArguments do

  def sample(action, opts \\ []) do
    for opt <- opts do
      case opt do
        {:ok, value} -> IO.inspect "ok: #{value}"
        {:hoge, value} -> IO.inspect "hoge: #{value}"
        {:huge, value} -> IO.inspect "huge: #{value}"
        {:foo, value} -> IO.inspect "foo: #{value}"
        {:var, value} -> IO.inspect "bar: #{value}"
        _ -> IO.inspect "No match!!"
      end
    end

    Map.put_new(%{}, action, opts)
  end

end
実行してみる。
iex> OptionArguments.sample :walk, hoge: 1, foo: 2
"hoge: 1"
"foo: 2"
%{walk: [hoge: 1, foo: 2]}

or

iex> OptionArguments.sample(:walk, hoge: 1, foo: 2)
"hoge: 1"
"foo: 2"
%{walk: [hoge: 1, foo: 2]}
どうせなので、望んでいる引数の型を分かりやすくするために、
アノテーションを付けて型情報を明示的にしてみます。
defmodule OptionArguments do

  @type action :: :walk | :run | :hop | :step | :jump
  @spec sample(action, Keyword.t) :: %{action => Keyword.t}
  def sample(action, opts \\ []) do
    for opt <- opts do
      case opt do
        {:ok, value} -> IO.inspect "ok: #{value}"
        {:hoge, value} -> IO.inspect "hoge: #{value}"
        {:huge, value} -> IO.inspect "huge: #{value}"
        {:foo, value} -> IO.inspect "foo: #{value}"
        {:var, value} -> IO.inspect "bar: #{value}"
        _ -> IO.inspect "No match!!"
      end
    end

    Map.put_new(%{}, action, opts)
  end

end
実行は同じ結果なので割愛。
Description:
@typeで型情報を自分で作っている。
@specで型情報を使って宣言をしている。
詳しく知りたい方はドキュメントかGetting Startを見て下さい。
hexdocs - v1.0.5 Elixir Kernel.Typespec
elixir-lang - Typespecs and behaviours
Caution:
@specは型を強制するものではありません。
実際に適当な値を渡してみて下さい。
特に問題なく実行されます。
iex> OptionArguments.sample :main, hoge: 1, foo: 2
"hoge: 1"
"foo: 2"
%{main: [hoge: 1, foo: 2]}
別段難しい内容ではなかったですね。
といいますか・・・こんな簡単にargs的なのできるんですね。
C++使ってた時は、もっと面倒にやっていた記憶があります(笑)

・再帰で行う少し複雑な例

ついでなので、少し複雑な例を一つやりましょう。
たまには頭を使わないといけませんね。(この程度とか言わないで・・・)
defmodule ArgumentsTest do

  @spec sample(Keyword.t) :: any
  def sample(kw) do
    keys = Keyword.keys(kw)
    values = Keyword.values(kw)

    IO.puts "Start..."
    sample(keys, values)
  end

  @match1 [:hoge, :huge]
  @match2 [:foo, :bar]

  defp sample([key|keys_tail], [value|values_tail]) when key in @match1 do
    IO.puts "@match1 match"
    IO.puts "#{key} = #{value}"
    sample(keys_tail, values_tail)
  end

  defp sample([key|keys_tail], [value|values_tail]) when key in @match2 do
    IO.puts "@match2 match"
    IO.puts "#{key} = #{value}"
    sample(keys_tail, values_tail)
  end

  defp sample(key, value) do
    IO.puts "...End"
  end

end
Description:
以下の部分ですが・・・キーワードリストのkey部分のみを抽出しています。
keys = Keyword.keys(kw)
同じく、キーワードリストのvalue部分のみを抽出しています。
values = Keyword.values(kw)
Example:
iex> Keyword.keys([hoge: 1, foo: 2, bar: 3, hoge: 4, huge: 5])
[:hoge, :foo, :bar, :hoge, :huge]
iex> Keyword.values([hoge: 1, foo: 2, bar: 3, hoge: 4, huge: 5])
[1, 2, 3, 4, 5]
Caution:
:hoge、:huge、:foo、:barにマッチしない場合の処理は書いていません。
現在のままだとマッチしないキーが来たら、そこで処理が終わります。
さて、実行してみましょう。
iex> ArgumentsTest.sample hoge: 1, foo: 2, bar: 3, hoge: 4, huge: 5
Start...
@match1 match
hoge = 1
@match2 match
foo = 2
@match2 match
bar = 3
@match1 match
hoge = 4
@match1 match
huge = 5
...End
:ok
順番に実行の流れを見てみましょう。
1:
# sample(kw)を実行
sample([hoge: 1, foo: 2, bar: 3, hoge: 4, huge: 5])

# keysとvaluesの内容
keys: [:hoge, :foo, :bar, :hoge, :huge]
values: [1, 2, 3, 4, 5]

# マッチさせる内容
@match1 [:hoge, :huge]
@match2 [:foo, :bar]

2:
# when key in @match1にマッチ
sample([:hoge|[:foo, :bar, :hoge, :huge]], [1|[2, 3, 4, 5]])

3:
# when key in @match2にマッチ
sample([:foo|[:bar, :hoge, :huge]], [2|[3, 4, 5]])

4:
# when key in @match2にマッチ
sample([:bar|[:hoge, :huge]], [3|[4, 5]])

5:
# when key in @match1にマッチ
sample([:hoge|[:huge]], [4|[5]])

6:
# when key in @match1にマッチ
sample([:huge|[]], [5|[]])

7:
# sample(key, value)にマッチ
sample([], [])
こうすれば、キーによって処理を変えることができる。
Ectoでfromを処理する時にやっていると聞いたので、気になって作ってみた。
書き終わって気付いたのですが、
一例と言っておきながら、二つの例を上げている・・・

Bibliography

人気の投稿