過去コラムZEN Style化①:再帰で書かれたkeyによるロジック切り替え(Strategy/StateパターンをElixirらしくスマートに)
fukuoka.ex/kokura.exのpiacereです
ご覧いただいて、ありがとうございます
過去のQiita Elixirコラムを、「Elixir ZEN Style」で書き換えると、どうなるかのミニコラムです
今回は、@darui_kara さんが書かれた下記コラムでやってみます
[Elixir]キーワードリストのkeyによって処理を変える一例
https://qiita.com/darui_kara/items/364b75f1cd8d28df6272
なお、「Elixir ZEN Style」については、下記コラムをご覧ください
Elixir Zen スタイルプログラミング講座
https://qiita.com/zacky1972/items/619f39cc77fbb52b1bbf
本コラムの検証環境
本コラムは、以下環境で検証しています(Windowsで実施していますが、Linuxやmacでも動作する想定です)
- Windows 10
- Elixir 1.10.1 ※最新版のインストール手順はコチラ
キーワードリストのkeyによってロジックを切り替える
キーワードリストのkey毎に、異なる処理を実行できるような切り替えを、登録済みkeyのリストと、関数のガードで実現しているのが、元コラムの内容です
モジュール内の「@変数」にkeyのリストを登録し、ガード「in」でリストとのマッチングを行います
defmodule ArgumentsSample 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
実行結果は、以下の通りです
iex> ArgumentsSample.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
元のコードが、再帰で実装されているところを、Enum.eachでElixir ZEN Style化します
defmodule ArgumentsSample do
@spec sample( Keyword.t ) :: any
def sample( kw ) when is_list( kw ) do
IO.puts "Start..."
kw |> Enum.each( & sample( &1 ) )
IO.puts "...End"
end
@match1 [ :hoge, :huge ]
@match2 [ :foo, :bar ]
def sample( { key, value } ) when key in @match1 do
IO.puts "@match1 match"
IO.puts "#{ key } = #{ value }"
end
def sample( { key, value } ) when key in @match2 do
IO.puts "@match2 match"
IO.puts "#{ key } = #{ value }"
end
end
実行結果は変わらず、同じ結果が出力されます
書き換えのポイント
- 再帰によるフロー制御を無くすことで、各sampleからフロー制御を除去でき、リスト分割も除去できました
- この結果、keysとvaluesを事前に取り出す中間処理が不要になりました
- 最終メッセージ出力を、再帰の終端(≒空リスト)として、最後のsampleでやっていたが、メイン側に移せました
効果
- コード行数が30%削減できました
- 制御フローと処理の分割で見通しが良くなりました
このテクニックの使いどころ
- コード行数が30%削減できました
- 制御フローと処理の分割で見通しが良くなりました
このテクニックの使いどころ
いわゆる「Strategyパターン」や「Stateパターン」のような、入力や状態によって、ハンドラをスイッチするようなものであれば、このテクニックによって、非常に保守性が高く、見通しの良いコードとなります(いちいちクラスを作るウザさもElixirなので当然ありません)
キーワードリストに入力や状態を並べ、ハンドラを呼ぶkey毎にグルーピングを行えばOKです
なお、各リストにkeyが単数しか無い場合は、下記のように、関数パターンマッチとして直接書けば良いため、inによるガードも、「@変数」の登録も、不要になります
…
def sample( { :hoge, value } ) do
IO.puts "@match1 match"
IO.puts "#{ key } = #{ value }"
end
def sample( { :foo, value } ) do
IO.puts "@match2 match"
IO.puts "#{ key } = #{ value }"
end
…
ちなみに…
ガードの「in」の指定が、「@変数」で無いと、エラーになります
これは不思議な仕様のように見えますが、ガードに書ける構文には、割と制約がある(動的な関数や自己定義関数はまず使えない)ので、致し方ありません
恐らく、「@変数」は、コンパイル時に定数であるため、この制約に引っかからないのでしょう
p.s.「いいね」よろしくお願いします
ページ左上の や のクリックを、どうぞよろしくお願いします
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!
Author And Source
この問題について(過去コラムZEN Style化①:再帰で書かれたkeyによるロジック切り替え(Strategy/StateパターンをElixirらしくスマートに)), 我々は、より多くの情報をここで見つけました https://qiita.com/piacerex/items/66c9e533f9701deeb7b4著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .