LoginSignup
5
1

More than 3 years have passed since last update.

Enjoy Elixir #003 Pattern matching

Last updated at Posted at 2020-06-16
1 / 11

はじめに

  • 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
  • 589133の結果は、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/1Enum.sort/2を使うとよいでしょう

参考


Wrapping Up

  • =は束縛
  • 次回は、すでにちょいちょいでてきていますが、Modules and functionsを詳しくみていきたいとおもいます
    • 来週を待ちきれない方は、リソースやコミュニティの情報を Where to go next にまとめていますのでダイブしてください!
  • Enjoy!!!
5
1
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
5
1