たとえば、こんな感じの複数条件を次々とトライしていく処理を書くとき、caseで書くと、ネストが深くなって、とにかく醜い
def to_datetime( str ) when is_binary( str ) do
case str |> case str |> Timex.parse( "%Y/%m/%d", :strftime ) do
{ :ok, result } -> result
{ :error, _ } -> case Timex.parse( "%Y/%m/%d %H:%M:%S", :strftime ) do
{ :ok, result } -> result
{ :error, _ } -> case str |> Timex.parse( "%Y/%m/%d %H:%M:%S.%L", :strftime ) do
{ :ok, result } -> result
{ :error, _ } -> case str |> Timex.parse( "%Y-%m-%dT%H:%M:%SZ", :strftime ) do
{ :ok, result } -> result
end
end
end
end
end
条件判定後の処理に類似性があるなら、リスト処理でまとめられて、エレガント(処理的には途中抜けしないため、若干効率は悪いかもだが、条件追加したいときは、リスト追加だけで済む)
def to_datetime( str ) when is_binary( str ) do
[
"%Y/%m/%d",
"%Y/%m/%d %H:%M",
"%Y/%m/%d %H:%M:%S",
"%Y/%m/%d %H:%M:%S.%L",
"%Y-%m-%dT%H:%M:%SZ",
]
|> Enum.map( &( str |> Timex.parse( &1, :strftime ) ) )
|> Enum.filter( &elem( &1, 0 ) == :ok )
|> List.first
|> Tpl.ok
end
※上記Tpl.ok()は、タプルで結果を返す関数をパイプ内で止めないための処理
もし、条件判定後の処理に類似性が無いなら、手前で文字列を分解し、後続の関数でパターンマッチ
String.split()の代わりに、Regex.named_captures()を使い、関数側はmapでパターンマッチすることもできる
def to_datetime( str ) when is_binary( str ) do
str |> String.split( "/" ) |> to_datetime
end
def to_datetime( [ y, m, d, others ] ) do
# %Y/%m/%d ~のパターンの処理
end
def to_datetime( [ y, m, d ] ) do
# %Y/%m/%dのパターンの処理
end
def to_datetime( [ tz ] ) do
# %Y-%m-%dT%H:%M:%SZのパターンの処理
end
手前の関数での文字列パターンマッチも可能ではあるが、表現力が弱く、複雑になるので、早々にあきらめている
def to_datetime( <<number::bytes-size(4)>> <> "/" <> <<number::bytes-size(2)>> <> "/" <> <<number::bytes-size(2)>> ) do
# %Y/%m/%dのパターンの処理
end
def to_datetime( <<number::bytes-size(4)>> <> "-" <> <<number::bytes-size(2)>> <> "-" <> <<number::bytes-size(2)>> ) do
# %Y-%m-%dのパターンの処理
end
p.s.上記をアレコレしている中で、if内部でパターンマッチができることが分かった(ただし複数条件を並べるには、elseが入ってきて、あまりキレイとは言えないが、caseの冗長性よりはマシな気がした)
if { :ok, result } = Timex.parse( str, "%Y/%m/%d %H:%M:%S", :strftime ), do: result