34

More than 5 years have passed since last update.

posted at

updated at

# やりたいこと

パターンマッチをバキバキに仕上げてくれるのが、`when`から始まるguard構文。標準で様々な演算子と関数が使えるようになっている。やや量が多いので、しっかりと使いこなせるようにするため汎用性の高そうなguardを一通り書いてみた。

## 演算子

``````比較演算子(==，!=，===，!==，>，<，<=，>=)

in演算子
``````

## チェック関数

で、以下がメインコンテンツ。標準ライブラリから様々なチェック関数が提供されているので、使いそうなものを片っ端から使ってみた。また、いくつかElixirの変数型仕様によるハマりどころもあったので、メモ書き程度だが付記しておく。

test/guard_test.exs
``````defmodule Demo.GuardTest do
use Demo.ConnCase

def this_is(x) when is_nil(x), do: "This is nil"
def this_is(x) when is_atom(x), do: "This is Atom"
def this_is(x) when is_binary(x), do: "This is Binary"
def this_is(x) when is_bitstring(x), do: "This is Bitstring"
def this_is(x) when is_boolean(x), do: "This is Boolean"
def this_is(x) when is_integer(x), do: "This is Integer"
def this_is(x) when is_float(x), do: "This is Float"
def this_is(x) when is_list(x), do: "This is List"
def this_is(x) when is_tuple(x) and tuple_size(x) == 2, do: "This is Tuple with two elements"
def this_is(x) when is_tuple(x), do: "This is Tuple"
def this_is(x) when is_map(x), do: "This is Map"
def this_is(x) when is_function(x, 2), do: "This is Function with two arguments"
def this_is(x) when is_function(x), do: "This is Function"

def this_is_number(x) when is_number(x), do: "This is Number; can be both Integer/Float"
def this_is_actually_boolean(x) when is_boolean(x), do: "This is Actually Boolean"

def they_are(x, y) when is_atom(x) and is_binary(y), do: "They are Atom and Binary"

test "nil in guards", _ do
assert this_is(nil) == "This is nil"
end

test "Atoms in guards", _ do
assert this_is(:foobar) == "This is Atom"
assert this_is(false) == "This is Atom"
end

test "Strings in guards", _ do
assert this_is("foobar") == "This is Binary"
assert this_is(<<102>>) == "This is Binary"
assert this_is(<< 1 :: size(1)>>) == "This is Bitstring"
assert this_is('foobar') == "This is List"
end

test "Booleans in guards", _ do
assert this_is(true) != "This is Boolean" # this matches is_atom so the function returns "This is Atom"
assert this_is_actually_boolean(true) == "This is Actually Boolean"
assert this_is_actually_boolean(:true) == "This is Actually Boolean" # this matches is_boolean too because true/false are technically atom
end

test "Numbers in guards", _ do
assert this_is(1) == "This is Integer"
assert this_is(0b10) == "This is Integer" # Binary
assert this_is(0o77) == "This is Integer" # Octal
assert this_is(0x1F) == "This is Integer" # Hexadecimal
assert this_is(0.1) == "This is Float"
assert this_is_number(101) == "This is Number; can be both Integer/Float"
assert this_is_number(1.1) == "This is Number; can be both Integer/Float"
end

test "Lists in guards", _ do
assert this_is([:ok, "foo", 0]) == "This is List"
assert this_is('foobar') == "This is List"
assert this_is([foo: 1, bar: 2]) == "This is List"
end

test "Tuples in guards", _ do
assert this_is({:error}) == "This is Tuple"
assert this_is({:ok, "foo"}) == "This is Tuple with two elements"
end

test "Maps in guards", _ do
assert this_is(%{:foo => "foo", "bar" => "bar"}) == "This is Map"
end

test "Functions in guards", _ do
double = fn(x) -> x * 2 end
remainder = &rem/2

assert this_is(double) == "This is Function"
assert this_is(remainder) == "This is Function with two arguments"
end

test "Multiple arguments in guards", _ do
assert they_are(:ok, "foobar") == "They are Atom and Binary"
end

end
``````

## 注釈:Stringについて

Elixirは文字列についての扱いが若干ややこしく、学習を進める前にBinaries, strings and char listsを熟読しておくと良い。

かいつまんで解説すると、Elixirで文字列と言えば、ダブルクォーテーションで囲まれた文字列で表される（i.e `"foobar"`）、UTF-8でエンコーディングされたバイナリである。また、String型というようなものは存在しないため、`is_string`というモノが仮にあったとしても、厳密にそれが文字列かどうかの保証はできない。従って、`is_binary/1`を使って文字列かどうかの大まかな判定を行うのがElixirでの習わしとなっている。

# あとがき

guards素敵すぎ。パターンマッチは言語機能として非常に強力で、簡潔に期待する引数を記述できる上、書いてて楽しいという俺得プログラマー得な機能。是非マスターして、かっこ良くて分かりやすいコードを書けるようにしたい。

# こんなのもあるよ

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
What you can do with signing up
34