はじめに
- KFIEという近畿大学産業理工学部の情報系コミュニティがあります
- 最近は、毎週火曜日にLT会をやっているそうです
- 私が学生だったのはもうずいぶん昔のことなのですが、参加させてもらっています
- 毎週、5分間時間をもらって、Elixirいいよ! を伝えていきたいとおもいます
- 今日は以下を学びます
- Pattern matching
- A journey of a thousand miles begins with a single step.
- この記事はElixirを触るのがはじめてという方に向けて書いています
もくじ
001 mix new, iex -S mix, mix format
|> 002 型
|> 003 Pattern matching
|> 004 Modules and functions
|> 005 Pipe operator and Enum module
|> 006 HTTP GET!
|> 007 Flow
|> 008 AtCoderを解いてみる
|> 999 Where to go next
Pattern matching
-
iex
を立ち上げていろいろ見てみましょう
iex> x = 1
1
iex> 1 = x
1
iex> 1 = 1
1
# Tuple
iex> tuple = {:hello, "world", 42}
{:hello, "world", 42}
iex> {a, b, c} = tuple
{:hello, "world", 42}
iex> a
:hello
iex> b
"world"
iex> c
42
iex> {a, b} = tuple
** (MatchError) no match of right hand side value: {:hello, "world", 42}
# Map
iex> torifuku = %{name: "torifuku", age: 5, height: 200}
%{age: 5, height: 200, name: "torifuku"}
iex> %{name: name, age: age} = torifuku
%{age: 5, height: 200, name: "torifuku"}
iex> name
"torifuku"
iex> age
5
iex> %{name: name, age: age, weight: weight} = torifuku
** (MatchError) no match of right hand side value: %{age: 5, height: 200, name: "torifuku"}
# List
iex> list = [1, 2, 3, "ダー"]
[1, 2, 3, "ダー"]
iex> [one, two, three, d] = list
[1, 2, 3, "ダー"]
iex> one
1
iex> two
2
iex> three
3
iex> d
"ダー"
iex> [one, two, three] = list
** (MatchError) no match of right hand side value: [1, 2, 3, "ダー"]
iex> [head | tail] = list
[1, 2, 3, "ダー"]
iex> head
1
iex> tail
[2, 3, "ダー"]
iex> hd(list)
1
iex> tl(list)
[2, 3, "ダー"]
Matches the value on the right against the pattern on the left.
関数の結果を tuple で受け取る
- 以下、Pattern matching をどういうときに使うのかいくつか具体例をみていきましょう
$ iex
iex> {:ok, content} = File.read("mix.exs")
{:ok,
"defmodule PatternMatching.MixProject ..."}
iex> String.length(content)
589
iex> String.split(content, " ") |> Enum.count
133
iex> System.halt
-
589
や133
の結果は、mix.exs
ファイルの内容によります
準備
- ここからはプロジェクトを作って楽しんでいきましょう
$ mix new pattern_matching
$ cd pattern_matching
フィボナッチ数
f_0=0,\\f_1=1,\\f_{n}=f_{n-2}+f_{n-1}
lib/pattern_matching.ex
defmodule PatternMatching do
def fib(n) do
if n == 0 do
0
else
if n == 1 do
1
else
fib(n - 2) + fib(n - 1)
end
end
end
end
$ iex -S mix
iex> PatternMatching.fib(10)
55
- 動いてはいますが、美しくありません
- Elixirっぽく書いてみましょう
- 最初にでてきた
数値 = 変数
を使います
lib/pattern_matching.ex
defmodule PatternMatching do
def fib(0 = n), do: 0
def fib(1 = n), do: 1
def fib(n), do: fib(n - 2) + fib(n - 1)
end
- 上記はさらに以下のようにスリムにすることができます
lib/pattern_matching.ex
defmodule PatternMatching do
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n - 2) + fib(n - 1)
end
- 数式と全く同じです! 美しい!
- マシンスペックにもよりますが、nが40を超える当たりからけっこう時間がかかるようになります
- 以下のようにしたほうが速く計算は終わります
lib/pattern_matching.ex
defmodule PatternMatching do
def fib(n), do: do_fib(n, 0, {0, 1})
defp do_fib(n, i, {n_2, _n_1}) when i >= n, do: n_2
defp do_fib(n, i, {n_2, n_1}), do: do_fib(n, i + 1, {n_1, n_2 + n_1})
end
iex> recompile
iex> PatternMatching.fib(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
Fizz Buzz
- 3で割り切れる数の場合は
Fizz
を返す - 5で割り切れる数の場合は
Buzz
を返す - 3でも5でも割り切れる数の場合は
Fizz Buzz
を返す - それ以外の数の場合は、その数字を返す
以下、関係があるところのみを書きます(フィボナッチ数の関数は残したままでも問題はありません)
lib/pattern_matching.ex
defmodule PatternMatching do
def fizz_buzz(n) do
if rem(n, 15) == 0 do
"Fizz Buzz"
else
if rem(n, 3) == 0 do
"Fizz"
else
if rem(n, 5) == 0 do
"Buzz"
else
n
end
end
end
end
end
- パターンマッチを使って美しく書き直してみましょう!
lib/pattern_matching.ex
defmodule PatternMatching do
def fizz_buzz(n), do: do_fizz_buzz(rem(n, 3), rem(n, 5), n)
defp do_fizz_buzz(0, 0, _n), do: "Fizz Buzz"
defp do_fizz_buzz(0, _, _n), do: "Fizz"
defp do_fizz_buzz(_, 0, _n), do: "Buzz"
defp do_fizz_buzz(_, _, n), do: n
end
iex> recompile
iex> 1..15 |> Enum.map(&PatternMatching.fizz_buzz/1)
[1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14,
"Fizz Buzz"]
クイックソート
lib/pattern_matching.ex
defmodule PatternMatching do
def quicksort([]), do: []
def quicksort([x | xs]) do
smaller_or_equal = (for a <- xs, a <= x, do: a)
larger = (for a <- xs, a > x, do: a)
quicksort(smaller_or_equal) ++ [x] ++ quicksort(larger)
end
end
iex> recompile
iex> PatternMatching.quicksort([10,2,5,3,1,6,7,4,2,3,4,8,9])
[1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10]
- リストのパターンマッチの例として出しました
- ソートはEnum.sort/1やEnum.sort/2を使うとよいでしょう
参考
Wrapping Up
-
=
は束縛 - 次回は、すでにちょいちょいでてきていますが、Modules and functionsを詳しくみていきたいとおもいます
- 来週を待ちきれない方は、リソースやコミュニティの情報を Where to go next にまとめていますのでダイブしてください!
- Enjoy!!!