第1回 - 基礎だ 2 -
GMO Pepabo, Inc.
Joe Honzawa
2015/5/15 Elixir勉強会
Elixirだ
今回の内容
> エコシステム
> Mixプロジェクト
> プロジェクトの作成
> テストの実行
> ライブラリの使用
> 再帰の考え方
エコシステム
Mix
> Rubyにおけるbundlerとrake
> 依存性解決
> タスクランナー
> mix new my_app
> mix test
> mix something.i.defined
Hex
> Rubyにおけるrubygems
> パッケージマネージャ
> https://hex.pm
> ErlangのライブラリもOK
> ファイル構成見てよしなにシュッと
はじめての
Mixプロジェクト
$ mix new please
$ cd ./please
$ ls
Mix project
> lib/please.ex
> test/
> please_test.exs
> test_helper.exs
> config/config.exs
> mix.exs
lib/please.ex
defmodule Please do
def give_me(:sushi), do: “🍣”
def give_me(:beer), do: “🍺”
def give_me(_other), do: “❓”
end
$ iex -S mix
iex(1)> Please.give_me :sushi
“🍣”
余談
> BEAMファイル
> バイトコード
> JavaでいうClassファイル
> _build/の奥のほう
> Elixir.Please.beam
テスト
テストライブラリ
> ExUnit
> とりあえずこれ覚えるべき
> ShouldI
> Bruce Tateさん作
> One Experiment, Multiple Measurements
> 中身はExUnit
> ESpec
test/please_test.exs
defmodule PleaseTest do
use ExUnit.Case
alias Please, as: Plz
test “sushi” do
assert Plz.give_me(:sushi) == ”🍣”
end
test “neither sushi nor beer” do
assert Plz.give_me(:hoge) == ”❓”
end
end
$ mix test
Redも見ておきましょう
defmodule PleaseTest do
use ExUnit.Case
alias Please, as: Plz
test “sushi” do
assert Plz.give_me(:sushi) == ”🍕”
end
test “neither sushi nor beer” do
assert Plz.give_me(:hoge) == ”❓”
end
end
ワンダーである
ワンダーである
> ==をつかったことがバレている
> assertにboolは渡っていない?
> エイリアスしたこともバレている
> なんぞこれ
> 第2回のマクロの話で
ライブラリの使用
代表的ライブラリ
> JSONを扱いたい
> Poison など
> Webアプリ作りたい
> Plug, Phoenix など
> hex.pmには便利なライブラリいっぱい
Poison使おう
> mix.exsに依存を記述
defp deps do
[
{:poison, "~> 1.4"}
]
end
Poison使おう
defp deps do
[
# master使いたい
{:poison, github: "devinus/poison"},
# 特定のbranch使いたい
{:poison,
github: "Joe-noh/poison",
branch: "experimental"}
]
end
tagとrefも使える
取ってくる
$ mix deps.get
しれっと使える
defmodule Please do
def pass_to_poison(arg) do
Poison.decode(arg)
end
end
Please.pass_to_poison "{"a": 1}"
他のdeps系タスク
$ mix deps.compile
$ mix deps.clean --all
$ mix deps.update
$ mix deps.unlock
$ mix deps
再帰
再帰的定義とは
> 定義に自身が含まれていること
> 注意: 無限ループすることがある
> これを防ぐためのベースケース
> ある条件を満足したらもう再帰しない
総和
body recursive
sum([1, 2, 3])
= 1 + sum([2, 3])
= 1 + 2 + sum([3])
= 1 + 2 + 3 + sum([])
= 1 + 2 + 3 + 0
= 6
body recursive
defmodule MyModule.Math do
# ベースケース
def sum([]), do: 0
def sum([h|t]), do: h + sum(t)
end           ↑
再帰から戻ってきた後にまだ仕事がある
body recursive
> スタックを消費する
f1 f1 f1 f1 f1 f1
 ↑
ベースケース
どこへ戻るか記録している
↓   ↓   ↓   ↓
tail recursive
sum([1, 2, 3])
= sum([1, 2, 3], 0)
= sum([2, 3], 0+1)
= sum([3], 0+1+2)
= sum([], 0+1+2+3)
= 6
tail recursive
defmodule MyModule.Math do
def sum(list), do: sum(list, 0)
# ベースケース
defp sum([], acc), do: acc
defp sum([h|t], acc) do
sum(t, acc+h)
end
end
 ↑
再帰呼び出しが最後の仕事
tail recursive
> どうせ仕事無いならイチイチ戻らない
f1 f1
 ↑
ベースケース
f1 f1 f1 f1
簡易計測
> 1からnまでのリストをつくる関数
> body_recursive(n)
> tail_recursive(n)
> alco/benchfellaで計測
簡易計測
計算時間[us/op]
1E+00
1E+01
1E+02
1E+03
1E+04
1E+05
1E+06
1E+07
要素数 n
1E+04
3E+04
5E+04
1E+05
3E+05
5E+05
1E+06
3E+06
5E+06
body [us/op] tail [us/op]
body / tail実行時間比(body/tail)
0.0
0.5
1.0
1.5
2.0
2.5
3.0
要素数 n
1E+04
3E+04
5E+04
1E+05
3E+05
5E+05
1E+06
3E+06
5E+06
簡易計測なので参考まで
というかメモリ測るべきか
練習問題
> リストの要素数を数える
> count([1,2,3]) == 3
> Enum.count/1 使っちゃダメ
count
defmodule Please do
def count(list) do
count(list, 0)
end
defp count([], acc), do: acc
defp count([_h|t], acc) do
count(t, acc+1)
end
end
練習問題
> リストの全要素に関数を適用する
> map([1], &(3 * &1)) == [3]
> いわゆるマップ処理
> Enum.map/2 使っちゃダメ
map
defmodule Please do
def map(list, f) do
map(list, f, [])
end
defp map([], _f, acc) do
Enum.reverse acc
end
defp map([h|t], f, acc) do
map(t, f, [f.(h) | acc])
end
end
今回の内容
> エコシステム
> Mixプロジェクト
> プロジェクトの作成
> テストの実行
> 依存性の解決
> 再帰の考え方

Elixirだ 第1回強化版 後半