elixirだ 第1回強化版 後半

43
第1回 - 基礎だ 2 - GMO Pepabo, Inc. Joe Honzawa 2015/5/15 Elixir勉強会 Elixirだ

Upload: joenoh

Post on 21-Jul-2015

172 views

Category:

Technology


1 download

TRANSCRIPT

第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.exdefmodule 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.exsdefmodule 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 recursivedefmodule 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

要素数 n1E+043E+045E+041E+053E+055E+051E+063E+065E+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

要素数 n1E+043E+045E+041E+053E+055E+051E+063E+065E+06

簡易計測なので参考まで というかメモリ測るべきか

練習問題> リストの要素数を数える > count([1,2,3]) == 3 > Enum.count/1 使っちゃダメ

countdefmodule 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 使っちゃダメ

mapdefmodule 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プロジェクト > プロジェクトの作成 > テストの実行 > 依存性の解決 > 再帰の考え方