LoginSignup
3
2

More than 5 years have passed since last update.

複数条件をトライするロバストな処理

Last updated at Posted at 2018-01-24

たとえば、こんな感じの複数条件を次々とトライしていく処理を書くとき、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
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2