Upgrade to Pro — share decks privately, control downloads, hide ads and more …

技術未来とElixir「第2回:プログラミング未経験でもExcelできれば関数型言語Elixirマスターできる」

piacerex
April 20, 2018

 技術未来とElixir「第2回:プログラミング未経験でもExcelできれば関数型言語Elixirマスターできる」

「オブジェクト指向言語を習得した人ほど、関数型言語に移行できない」という現象がありますが、その罠を回避するために、Excelの世界からElixirに進むことで、Elixirをサクッとマスターし、Web+DBアプリをElixirで作っていける、というパラダイムシフトをコンテンツ化してみました

piacerex

April 20, 2018
Tweet

More Decks by piacerex

Other Decks in Programming

Transcript

  1. 5 2.事前準備:Elixirのインストール まず、Elixirを使い始めるのに、3種類の方法があります ① インストーラやyum/apt/Homebrewを使う ② ソースコードからビルドする ③ DockerでElixirイメージをインスト―ル (pull)

    する Windows/macOSは①、Linux含むUNIX系は②、普段 Dockerを使い慣れている方は③がオススメです 以降で、①~③の手順をそれぞれ解説します
  2. 7 2.事前準備:Elixirのインストール ②は、以下の手順通り、まずErlangをソースコードからビルドして、 インストールします 次に、Elixirをソースコードからビルドして、インストールします > wget http://erlang.org/download/otp_src_20.3.tar.gz > tar

    vzfx otp_src_20.3.tar.gz > cd otp_src_20.3/ >./configure --enable-hipe > make && make install > git clone https://github.com/elixir-lang/elixir/ > cd elixir > git checkout v1.6 > git pull > export PATH="${PATH}:/usr/local/bin" > make && make install > elixir -v 実施するタイミング次第では、新しいものがリリース されているかも知れないので、気になる方は、下記 URLをチェックして、適宜、変更してください http://erlang.org/download Elixirのメジャーバージョンが新しくなっている場合は 適宜、バージョンを変更してください
  3. 12 Enum.filter()内のfn( n )は、データの1つ1つの値を変数n※ として、後ろの処理 (青枠) に渡す、という意味で、青枠の処理 では、1つ1つの値が「999」で無いことをチェックしています ※この「n」は、fn()で指定した変数と、後ろの処理で同一であれば、どんな変数名でも構いません なお「fn」は、関数

    (function) を意味しており、その後の「->」 から「end」までの間が、関数の処理を意味しています つまり、データの1つ1つの値が、「999で無いことをチェック」すると いう関数に順に渡され、 「999で無い値」だけが残るようフィルタ されている、というのが、Enum.filter()で行われている処理です 3.入門①:Excelの操作と同じElixir iex> [ 323, 999, 54 ] |> Enum.filter( fn( n ) -> n != 999 end ) [323, 54] iex> [ 323, 999, 54 ] |> Enum.filter( fn( n ) -> n != 999 end ) [323, 54]
  4. 15 文字列でも、Enum.sort()は、Excelと同じ結果になります Enum.Filter()や、Enum.map()も同様です 3.入門①:Excelの操作と同じElixir iex> [ "ゆじかわ", "つちろー", "enぺだーし", "ざっきー"

    ] |> Enum.sort ["enぺだーし", "ざっきー", "つちろー", "ゆじかわ"] iex> [ "ゆじかわ", "つちろー", "enぺだーし", "ざっきー" ] |> Enum.map( &( &1 <> "さん" ) ) ["ゆじかわさん", "つちろーさん", "enぺだーしさん", "ざっきーさん"] iex> [ "ゆじかわ", "つちろー", "enぺだーし", "ざっきー" ] |> Enum.filter( &( &1 != "つちろー" ) ) ["ゆじかわ", "enぺだーし", "ざっきー"]
  5. 16 複数の操作をパイプで繋げることもできます このデータの値を並べている「 [~] 」を、「リスト」と呼びます 3.入門①:Excelの操作と同じElixir iex> [ "ゆじかわ", "つちろー",

    "enぺだーし", "ざっきー" ] ¥ ...> |> Enum.sort ...> |> Enum.filter( &( &1 != "つちろー" ) ) ...> |> Enum.map( &( &1 <> "さん" ) ) ["enぺだーしさん", "ざっきーさん", "ゆじかわさん"]
  6. 17 3.入門①:Excelの操作と同じElixir 今度は、複数列のデータを扱ってみましょう これをElixirで表現すると、以下のような感じになります iex> data = [ %{ "名前"

    => "enぺだーし", "年齢" => 49, "所属" => "有限会社デライトシステムズ", "ポジション" => "代表取締役, 性能探求者" }, %{"名前" => "ざっきー", "年齢" => 45, "所属" => "公立大学法人 北九州市立大学", "ポジション" => "准教授, カーネルハッカー" }, %{"名前" => "つちろー", "年齢" => 34, "所属" => "カラビナテクノロジー株式会社", "ポジション" => "リードエンジニア, アプリマイスター" }, %{"名前" => "ゆじかわ", "年齢" => 30, "所属" => "カラビナテクノロジー株式会社", "ポジション" => "リードエンジニア, グロースハッカー" }, %{"名前" => "piacere", "年齢" => 43, "所属" => "株式会社TechJIN", "ポジション" => "CTO, 福岡Elixirプログラマ, 重力プログラマ, 技術顧問" } ]
  7. 18 3.入門①:Excelの操作と同じElixir 列名を日本語にすると、後で扱いにくくなるので、英語に変えます この複数列をまとめる「%{~}」を、「マップ」と呼びます (Enum.map()の「マップ」と紛らわしいですね…) 複数列データは、この「マップ」を「リスト」で包んでいるので「マップ リスト」と呼んだりします dataの中身は、次ページのように格納されます iex> data

    = [ %{ "name" => "enぺだーし", "age" => 49, "team" => "有限会社デライトシステム ズ", "position" => "代表取締役, 性能探求者" }, %{"name" => "ざっきー", "age" => 45, "team" => "公立大学法人 北九州市立大学", "position" => "准教授, カーネルハッカー" }, %{"name" => "つちろー", "age" => 34, "team" => "カラビナテクノロジー株式会社", "position" => "リードエンジニア, アプリマイスター" }, %{"name" => "ゆじかわ", "age" => 30, "team" => "カラビナテクノロジー株式会社", "position" => "リードエンジニア, グロースハッカー" }, %{"name" => "piacere", "age" => 43, "team" => "株式会社TechJIN", "position" => "CTO, 福岡Elixirプログラマ, 重力プログラマ, 技術顧問" } ]
  8. 19 3.入門①:Excelの操作と同じElixir [ %{ "age" => 49, "name" => "enぺだーし",

    "position" => "代表取締役, 性能探求者", "team" => "有限会社デライトシステムズ" }, %{ "age" => 45, "name" => "ざっきー", "position" => "准教授, カーネルハッカー", "team" => "公立大学法人 北九州市立大学" }, %{ "age" => 34, "name" => "つちろー", "position" => "リードエンジニア, アプリマイスター", "team" => "カラビナテクノロジー株式会社" }, %{ "age" => 30, "name" => "ゆじかわ", "position" => "リードエンジニア, グロースハッカー", "team" => "カラビナテクノロジー株式会社" }, %{ "age" => 43, "name" => "piacere", "position" => "CTO, 福岡Elixirプログラマ, 重力プログラマ, 技術顧問", "team" => "株式会社TechJIN" } ]
  9. 20 3.入門①:Excelの操作と同じElixir List.firstを使うと、先頭行のみ取得できます 取得した行から、特定の列の値を取り出すには、[ "【列名】" ] で行います iex> first_line =

    data |> List.first %{ "age" => 49, "name" => "enぺだーし", "position" => "代表取締役, 性能探求者", "team" => "有限会社デライトシステムズ" } iex> first_line[ "name" ] "enぺだーし" iex> first_line[ "age" ] 49
  10. 21 「年齢」列でフィルタするには、Enum.filter()にて、「年齢」列の 値の条件判断により、フィルタをかけます 3.入門①:Excelの操作と同じElixir iex> data |> Enum.filter( &( &1[

    "年齢" ] > 40 ) ) [ %{ "ポジション" => "代表取締役, 性能探求者", "名前" => "enぺだーし", "年齢" => 49, "所属" => "有限会社デライトシステムズ" }, %{ "ポジション" => "准教授, カーネルハッカー", "名前" => "ざっきー", "年齢" => 45, "所属" => "公立大学法人 北九州市立大学" }, %{ "ポジション" => "CTO, 福岡Elixirプログラマ, 重力プログラマ, 技術顧問", "名前" => "piacere", "年齢" => 43, "所属" => "株式会社TechJIN" } ]
  11. 22 複数列の「年齢」列でソートをかけるには、Enum.sort()にて、 &1 (現在行)と&2 (次行) の「年齢」列を、昇順なら「<」、 降順なら「>」で比較することでソートします 難しかったですかね?もし難しければ、ここは覚えなくて良いです 3.入門①:Excelの操作と同じElixir iex>

    data |> Enum.sort( &( &1[ "age" ] < &2[ "age" ] ) ) [ %{ "ポジション" => "リードエンジニア, グロースハッカー", "名前" => "ゆじかわ", "年齢" => 30, "所属" => "カラビナテクノロジー株式会社" }, … %{ "ポジション" => "代表取締役, 性能探求者", "名前" => "enぺだーし", "年齢" => 49, "所属" => "有限会社デライトシステムズ" } ]
  12. 23 「年齢」列のみを「変換」するには、Enum.map()にて、「年齢」 列の値の処理により、変換をかけます 「名前」列のみの変換もできます 特に処理を書かなかったら、何も変換されず、出力されます 3.入門①:Excelの操作と同じElixir iex> data |> Enum.map(

    &( &1[ "年齢" ] + 100 ) ) [149, 145, 134, 130, 143] iex> data |> Enum.map( &( &1[ "年齢" ] ) ) [49, 45, 34, 30, 43] iex> data |> Enum.map( &( &1[ "名前" ] <> "さん" ) ) ["enぺだーしさん", "ざっきーさん", "つちろーさん", "ゆじかわさん", "piacereさん"]
  13. 25 「fn~end」と関数を使った書き方で、複数列を、別の複数列に 変換せずに移すことも、Enum.map()でできます (結果的にこれは、特定の列を抜き出す処理になります) 3.入門①:Excelの操作と同じElixir iex> data |> Enum.map( fn

    %{ "name" => name, "age" => age } -> %{ "name" => name, "age" => age } end ) [ %{"age" => 49, "name" => "enぺだーし"}, %{"age" => 45, "name" => "ざっきー"}, %{"age" => 34, "name" => "つちろー"}, %{"age" => 30, "name" => "ゆじかわ"}, %{"age" => 43, "name" => "piacere"} ]
  14. 26 前ページの上記は、Enum.map()内を列毎に並べて書き直す と以下の通りです ちなみに、fnを使わないと、以下になります (どちらが簡単?) 3.入門①:Excelの操作と同じElixir iex> data |> Enum.map(

    &( %{ "name" => &1[ "name" ], "age" => &1[ "age" ] } ) ) iex> data |> Enum.map( fn %{ "name" => name, "age" => age } -> %{ "name" => name, "age" => age } end ) iex> data |> Enum.map( fn %{ "name" => name, "age" => age } -> %{ "name" => name, "age" => age } end )
  15. 27 「for」を使うと、Enum.map()と同じようなリスト操作ができます これを最初に出さなかったのは、以下3つの理由からです ① 既存のオブジェクト指向言語の「for」と同じで無いから ② Enum.map()のように、パイプを繋いで使えないから ③ 馴染みあるせいで使いたくなる、という思考が罠だから 3.入門①:Excelの操作と同じElixir

    iex> for record <- data do ...> %{ "name" => record[ "name" ], "age" => record[ "age" ] } ...> end [ %{"age" => 49, "name" => "enぺだーし"}, %{"age" => 45, "name" => "ざっきー"}, %{"age" => 34, "name" => "つちろー"}, %{"age" => 30, "name" => "ゆじかわ"}, %{"age" => 43, "name" => "piacere"} ]
  16. 30 4.事前準備:PhoenixでPJ作成 次に、ElixirのWebアプリケーションフレームワーク「Phoenix」を インストールします(要ネット接続) Phoenixプロジェクトを作成します (要ネット接続) DBを作成します ※作成しないとiex内でエラー連発となります Phoenixサーバーを起動します #

    mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez # mix phx.new sample_db --no-brunch # iex -S mix phx.server 掲載スペースの関係で2行になっていますが、 2行目URLも続けて1行で入力してください PJ名には、英小文字と「_」を指定しないと エラーないしはビルド途中でコケることがあります # cd sample_db # mix ecto.crete
  17. 33 5.初級:DBデータをWeb表示 DBの準備をするため、psqlを起動し、既に「mix ecto.crete」 で作成したデータベースに接続します 以下SQLで、Webに表示する元データのテーブルを作成します create table members (

    id integer, name varchar(255), age integer, team varchar(255), position varchar(255) ) postgres-# ¥c sample_db_dev データベース "sample_db_dev" にユーザ "postgres" として接続しました。 postgres-# ¥d members テーブル "public.members" 列 | 型 | 照合順序 | Null 値を許容 | デフォルト ----------+------------------------+----------+---------------+------------ id | integer | | | name | character varying(255) | | | age | integer | | | team | character varying(255) | | | position | character varying(255) | | |
  18. 34 5.初級:DBデータをWeb表示 以下SQLで、Webに表示する元データを追加します insert into members( id, name, age, team,

    position) values ( 1, 'enぺだーし', 49, '有限会社デライトシステムズ', '代表取締役, 性能探求者' ); insert into members( id, name, age, team, position) values ( 2, 'ざっきー', 45, '公立大学法人 北九州市立大学', '准教授, カーネルハッカー' ); insert into members( id, name, age, team, position) values ( 3, 'つちろー', 34, 'カラビナテクノロジー株式会社', 'リードエンジニア, アプリマイスター' ) insert into members( id, name, age, team, position) values ( 4, 'ゆじかわ', 30, 'カラビナテクノロジー株式会社', 'リードエンジニア, グロースハッカー' ); insert into members( id, name, age, team, position) values ( 5, 'piacere', 43, '株式会社TechJIN', 'CTO, 福岡Elixirプログラマ, 重力プログラマ, 技術顧問' );
  19. 35 5.初級:DBデータをWeb表示 selectを直接発行するために、以下のモジュールを用意します ※実は、性能重視のSQLを書く場合に、このモジュール重宝します defmodule Db do def query( sql

    ) when sql != "" do { :ok, result } = Ecto.Adapters.SQL.query( SampleDb.Repo, sql, [] ) result end def columns( %{ columns: columns } = _result ), do: columns def rows( %{ rows: rows } = _result ), do: rows def columns_rows( result ) do result |> rows |> Enum.map( &( Enum.into( List.zip( [columns( result ), &1 ] ), %{} ) ) ) end end lib/util/db.ex
  20. 36 5.初級:DBデータをWeb表示 selectでDBデータを取得して、HTMLを出力します 最初のfor (3行目) は、各行毎のデータを「record」に渡し、 行数分だけループします 1つ内側のfor (5行目) は、列名を「column」に渡し、6行目

    の「record[ column ]」で、各行各列の出力に使います <% data = Db.query( "select * from members" ) %> <table border="1"> <%= for record <- Db.columns_rows( data ) do %> <tr> <%= for column <- Db.columns( data ) do %> <td><%= record[ column ] %></td> <% end %> </tr> <% end %> </table> lib/sample_db_web/templates/page/index.htm
  21. 38 5.初級:DBデータをWeb表示 列名もHTML出力してみます lib/sample_db_web/templates/page/index.htm <% data = Db.query( "select *

    from members" ) %> <table border="1"> <tr> <%= for column <- Db.columns( data ) do %> <th><%= column %></th> <% end %> </tr> <%= for record <- Db.columns_rows( data ) do %> <tr> <%= for column <- Db.columns( data ) do %> <td><%= record[ column ] %></td> <% end %> </tr> <% end %> </table>
  22. 44 6.Excelから関数型言語に行った理由 「オブジェクト指向言語」と比較した「関数型言語」には、以下の ような大きな違いがあります ① メンバー変数やグローバル変数のような「状態」を持てない ② 一度、代入した変数は、変更できない (≒イミュータブル) ③

    Elixir以外の関数型言語には、オブジェクト指向言語では、 一切出ない、「カリー化」 「部分適用」「クロージャ」「モナド」 等の摩訶不思議なキーワードが幾つも出てくる ※関数型の習得に必須に見えるが、上記は必須では無い こうした違いは、とても有用な一方、真正面から受けると、プログ ラミング経験が幾らあっても、隔たりが大き過ぎて、辛いのです こうした罠を回避するための「Excelで覚えるElixir」だったのです