やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

【OTP入門】ElixirとOTPを使ってスタックサーバーを実装するまで

そもそもOTPとは

一言で言えば、Erlangで用意された便利なライブラリなどの集合体で便利ツールをまとめたものという認識をしている
OTPとはopen telecom platoformの略で当初は堅牢性が重要な電話交換機を開発するために使用されていた
今になってはElixirでもOTPを使用して気軽に様々なアプリを作成することが可能

OTPには behaviour(ビヘイビア: 振る舞い、動作)というルール(規約)、役割のようなものが数多く用意されている
ドラクエでいう職業のようなもので、それぞれのビヘイビアによって実行する内容や役割が異なる

今回、扱うのは「GenServer」と言われるビヘイビアでサーバーを実装するためのものだ
このGenServerを使用することでstateを保持するサーバーを簡単に実装することが可能
関数型言語でどのように値を保持しているかという内容を補足的に解説しておくと、以前から扱っているアキュムレーターがまさにそれである
内部的には再帰関数によってアキュムレーターによる値の保存を行なっている
しかし、それを毎度毎度、記述するのはどうなのよということでGenServerの出番というわけだ
アキュムレーターについて解説した記事もあるので宜しければ、ご覧ください

www.okb-shelf.work

また合わせて「Supervisor」と言われるプロセスの死活管理のためのビヘイビアについても軽く扱う
戦士(GenServer)と僧侶(Supervisor)の二人組のパーティを作っていくと思ってもらえれば良い
戦士(GenServer)が死亡した際に、僧侶(Supervisor)が面倒をみてくれる
Supervisorは優秀で彼が使う蘇生呪文は「ザオリク」であり、100%プロセスを復活させる(例え話ね)
そのためOTPを使ったアプリケーションは高い保守性と堅牢性とトラッキング(ログ、行動管理など)を実現することが出来る

GenServerについて

先ほども説明した通り、用意されたビヘイビアの1つで値を保持するサーバーを実装するために今回は用いる
GenServerにはクライアントのリクエストを捌くための2つの関数が用意されている

  • handle_call -> 同期関数で戻り値を指定可能
  • handle_cast -> 非同期関数で戻り値を指定不可

自身では戻り値があるやつとないやつ程度のレベルで理解している
このhandle_callとhandle_castがクライアントからのリクエストを受け付ける関数で第1引数のアトムでパターンマッチをして対象の関数を呼び分ける
第2引数はpid(Process Identifier(プロセス認識子))が渡り、第3引数にはサーバーで保持しているstateが渡る
今回は簡略化のためmoduleでwrapしていないが、実際にはそうではない。詳しくはまた後ほど記述するだろう

handle_call

# 戻り値は{:reply, クラインとに返したい値, サーバーのstateとして更新する値}を記述する
# stateを1に
def handle_call(:one, _form, _state), do: {:reply, 1, 1}

# stateを2に
def handle_call(:two, _form, _state), do: {:reply, 2, 2}

# stateを3に
def handle_call(:three, _form, _state), do: {:reply, 3, 3}

handle_cast

# 戻り値は{:noreply, サーバーのstateとして更新する値}を記述する
# stateを0にしてリセット
def handle_cast(:reset, _state), do: {:noreply, 0)

こんな感じでcallもcastも同名の関数でパターンマッチを使用して処理を分岐させてるんだなーという理解をしてもらえれば100点だ
他にも

  • init()
  • handle_info()
  • terminate()
  • code_change()
  • form_status()

という関数も用意されているがそれぞれについて詳しくは触れない

いまさらながらスタックについて

有名なデータ構造の1つでいわゆる「後入れ先出し」という動きをとる
スタックにはpush(追加)とpop(取得&除去)という2つの動き方がある
リストを使って説明をすると以下のようになる
なお、先頭要素は1で末尾要素は5なので1.皿の上に2.皿が。2.皿の上に3.皿が....というイメージをしてみてほしい

# 最初のデータ
init_stack_values = [1,2,3,4,5]

# 6をpush
stack_values = [1,2,3,4,5,6]

# 99をpush
stack_values = [1,2,3,4,5,6,99]

# pop(末尾の値を取得して取り除く)
popped_value = 99
stack_values = [1,2,3,4,5,6]

# pop
popped_value = 6
stack_values = [1,2,3,4,5]

今回はこのpushとpopに加えて、現在のstackの状態を知らせるinfoという関数も実装する

プロジェクトの作成

一通り説明したところでさっそくスタックサーバーを作成する
まずはプロジェクトを新規作成する。その際に「--sup」というオプションを付与しておく
これは後にsupervisorを実装する際に必要となるので指定しておく

$ mix new --sup stack

コマンドの実行後、stackというディレクトリが生成され、中には様々なファイルが生成される
./ilb/stack にserver.exを新規作成して以下の記述をする

.lib/stack/server.ex

defmodule Stack.Server do
  # GenServerを使うということを宣言(必須)
  use GenServer

  # GenServerの立ち上げ用の関数
  def start_link(init_lst) do
    # arguments: 1 -> 対象のモジュール, 2: -> stateの初期値, name: -> プロセスに名前付け
    # __MODULE__ -> Stack.Serverが参照される
    GenServer.start_link(__MODULE__, init_lst, name: __MODULE__)
  end

  # 以下、handole_castとhandle_callでpopとpushとinfoを実装
  def handle_call(:pop, _form, state) do
    [head | tail] = Enum.reverse(state)
    {:reply, head, Enum.reverse(tail)}
  end

  # 値を渡したい時は第1引数をタプルにする
  # 第1引数: {:push(パターンマッチ用), 渡された値}
  def handle_cast({:push, value}, state) do
    {:noreply, state ++ [value]}
  end

  def handle_call(:info, _form, state) do
    {:reply, state, state}
  end
end

さっそく動作を確認してみる
GenServerのプロセスを立ち上げて、スタックの動きを確認する

$ iex -S mix

iexのプロンプトが立ち上がる

iex> Stack.Server.start_link([1,2,3])
{:ok, #PID<0.148.0>}

iex> GenServer.call(Stack.Server, :pop)
3

iex> GenServer.cast(Stack.Server, {:push, 99})
:ok

iex> GenServer.call(Stack.Server, :info)
[1, 2, 99]

どうやら一連の動きは問題なく稼働しているようだ
しかしながら、例外を無理矢理、発生されると当然ながらエラーになる(stackを空にしてpop)

iex> GenServer.call(Stack.Server, :pop)
99

iex> GenServer.call(Stack.Server, :pop)
2

iex> GenServer.call(Stack.Server, :pop)
1

iex> GenServer.call(Stack.Server, :info)
[]

iex> GenServer.call(Stack.Server, :pop)
22:11:06.670 [error] GenServer Stack.Server terminating
** (MatchError) no match of right hand side value: []
:

一度、プロセスがcrashするともうGenServerの関数を呼び出しても反応がなくなる

iex> GenServer.call(Stack.Server, :info)
** (exit) exited in: GenServer.call(Stack.Server, :info, 5000)
:

再度、GenServer(プロセス)を立ち上げてやれば問題はない

iex> Stack.Server.start_link([11,22,33])
{:ok, #PID<0.163.0>}

iex> GenServer.call(Stack.Server, :info)
[11, 22, 33]

しかしながら面倒だ
Elixir、Erlangでは死んだプロセスは破棄して新たにプロセスを立ち上げてやれば良いという方針で
こんな時にSupervisorが役に立つ。死んだプロセスを破棄して新たなプロセスを立ち上げてくれる

supervisorを設定する

今回はシンプルなsupervisorを設定する
iexプロンプトの立ち上げ時にGenServer(プロセス)の立ち上げとプロセスがcrashした際に新規にプロセスを立ち上げるようにしてもらう
--supをプロジェクト生成時に渡しているため、以下のファイルが生成されているはずだ

./lib/stack/application.ex

defmodule Stack.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Starts a worker by calling: Stack.Worker.start_link(arg)
      # {Stack.Worker, arg}
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Stack.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

初期の状態は上記のようになっているので、これを以下のように変更する

defmodule Stack.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    # モジュールのimport
    import Supervisor.Spec, warn: false
    # List all child processes to be supervised
    children = [
      # 起動したいGenServer(モジュール)を指定し、第2引数にstateの初期値をリストで渡す
      worker(Stack.Server, [[1,2,3]])
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Stack.Supervisor]
    Supervisor.start_link(children, opts)
  end

プロンプトを一度、終了して再度立ち上げる

$ iex -S mix

すでにGenServer(プロセス)が立ち上がっているのが分かる

iex> GenServer.call(Stack.Server, :info)
[1, 2, 3]

そしてわざとcrashさせてみる。先ほどは再度、GenServer(プロセス)を立ち上げたが今回はどうか

iex> GenServer.call(Stack.Server, :pop)
3
iex> GenServer.call(Stack.Server, :pop)
2
iex> GenServer.call(Stack.Server, :pop)
1
iex> GenServer.call(Stack.Server, :pop)
22:31:05.161 [error] GenServer Stack.Server terminating
** (MatchError) no match of right hand side value: []
:

iex> GenServer.call(Stack.Server, :info)
[1, 2, 3]

supervisorによってGenServer(プロセス)が新たに作成されているようだ
初期値は先程、applicationの初期値として指定したものが与えられている
crash前の値を保持できないの?と当然思うが、別のプロセスを用意してそちらへ保管させることで可能である
そのやり方についてはプログラミングElixirにも記述があるが別の記事にまとようと思う

例外処理と関数の整理

最後にGenServerの利便性を上げるためにちょっとした修正をする
毎回、GenServer.call(Stack.Server, :info)とやるのは面倒だし、よりシンプルにスタックを利用したいため、関数を以下の様に整備した
またスタックが空になった時に、errorになってしまう問題を解決するために再帰関数でお馴染みのパターンマッチを用意する

defmodule Stack.Server do
  use GenServer
  def start_link(init_lst) do
    GenServer.start_link(__MODULE__, init_lst, name: __MODULE__)
  end

  # pop関数を実装 -> GenServer.callを呼び出す
  def pop(), do: GenServer.call(__MODULE__, :pop)

  # push関数を実装 -> 受け取った値をGenServer.castに渡して呼び出す
  def push(val), do: GenServer.cast(__MODULE__, {:push, val})

  # info関数を実装
  def info(), do: GenServer.call(__MODULE__, :info)

  # stackが空になった場合に例外処理が発動する様に関数を追加
  def handle_call(:pop, _form, []), do: {:reply, [], []}
  def handle_call(:pop, _form, state) do
    [head | tail] = Enum.reverse(state)
    {:reply, head, Enum.reverse(tail)}
  end

  def handle_cast({:push, value}, state) do
    {:noreply, state ++ [value]}
  end

  def handle_call(:info, _form, state) do
    {:reply, state, state}
  end
end

こうしてやることでよりシンプルで利便性よくGenServerを利用することが可能だ

iex> Stack.Server.pop()
3
iex> Stack.Server.pop()
2
iex> Stack.Server.push(99)
:ok
iex> Stack.Server.info()
[1, 99]
iex> Stack.Server.push(999)
:ok
iex> Stack.Server.info()
[1, 99, 999]
iex> Stack.Server.pop()
[]

呼び出しがシンプルになり、例外のerrorが無事に発生しなくなった
いいね

感想というか語らせて

以前からOTPというものの存在を理解はしていたが、どちらかというとElixirのsyntaxや仕様ばかりに着目していた
今回OTPを自身でまとめてみたのには理由がある
知人の方からElixirでリモートワークを募集しているページを見せて頂いた
求人の要項には

  • OTPへの理解がある方
  • phoenixへの理解がある方
  • 新しいものへの挑戦心

という項目が多かった。3つ目はばっちりだとしても上記の2つはよく分かっていない
特にOTPはそうで、どこで使うだとか、全くイメージできていないレベル感で「こりゃあかんな」と思った
今まで趣味レベルでのElixirへのアプローチをとってきたが少し、仕事を意識してしまったというのが今回の記事が生まれた経緯となる
というか絶対に落ちないサーバーってめちゃくちゃかっこいいやん(joe曰く99.9999%落ちない)
なんでスルーしてたんだろうと反省

ようやく入門できたかなと自己満足して終了

参考文献

プログラミングElixir
What is OTP?
Elixir初心者がOTPって結局なんなのか調べてみた
[翻訳] ElixirにおけるOTPの紹介
スタック

みんなのAIキャリカレッジin関西にて頂いた質問17個への回答まとめ

みんなのAIキャリアカレッジin関西

edgeai.connpass.com

先日こちらのイベントのパネルディスカッションなるものに登壇させて頂きまして
参加者の方から頂いた質問にお答えしました
自分自身は業務として機械学習に取り組む人間の1人として回答をさせて頂きました

お恥ずかしながら「回答がためになりました」と好評を頂きましたので、せっかくなのでテキストにまとめておこうかと思います
当日はお話できなかった情報も一部付与しています
まだまだ先駆者が少なく確定したジョブモデルが少ないので、自分自身、日々試行錯誤しながら手を進めています

同じように悩んでいる方の助けになればと思います
司会を勤められた岸さんはとても気さくな方でした。当日はありがとうございました^^

人生3度目の関西、新幹線でしたが無事に辿り着くことが出来た
f:id:takamizawa46:20190811001830j:plain:w500

質問と回答

自身は知名度のある大学院を出ているわけではなく博士をもっているわけでもない
どちらかといえばビジネスサイドにおける機械学習の適応、問題への落とし込みの方法という視点から回答をしている
研究での機械学習とビジネスでの機械学習では毛色がかなり異なると考えている

特にビジネスにおける機械学習の立ち振る舞いはまだ先駆者が少なく十分な情報とモデルがない
技術の情報は論文や記事を通じてキャッチできるが、機械学習案件の回し方や要件定義の仕方というものはまだまだ情報が少ない

テーマ1. 必要なスキルと学習法

プログラム未経験者ですが、AIやデータサイエンス業界へ興味を持ち、年初から独学で半年ほどですがPythonを勉強しています。はじめはどのようなスキルを磨くと業界への転職に有利なのかご教示願えますでしょうか

とりあえず手を動かせばいいのではないかと考えます
kaggleをやるのもよし、公開のデータセットを使用して画像分類をするもよし
やり方はいくらでもあるでしょう
私はkaggleのタイタニックチュートリアルから始めました

また実務ではPythonのみで何とかなるいう場面は少なく

などなど広い知識が要求される場合があります

実務経験がない場合に、実力を測る指標としてkaggleの成績が利用されるケースがあると思いますが、kaggleでどの程度の成績を残せばアピールになりますか

企業のレベル感によります。メダルを保持していればいるほど評価されるようなところもあるかと考えます
実務では課題に対してどのようにトライをするかという点が重視されるので成績よりも「なぜその施策をしたのか」ということに企業は興味をもつかと
その部分の説明ができれば十分なアピールになるのではないでしょうか

数学的知識や、論文読解力の高度な知識はどの程度要求されますか。

あればあるほど良いですが、必ずしも必要になるとは考えていません
分からない部分があれば適宜、インプットすれば良いので全てを熟知する必要はありません
ただ、論文を調査する際に英語がスラスラ読めると楽ですし、取得可能な情報量が増えます
論文英語は分野の専門用語が多いので、留学経験者であってもスラスラ読めない場合が多いです

論文の情報はどこでチェックするのが良いですか。

テーマ2. AIとSIの違い

このテーマは現在多くの機械学習をテーマに取り組み企業が悩んでいる部分でもある
模索しつつ、最も適切な手段を選択しているつもりだが中々、クライアントに理解してもらえないことが多い
クライアントは「AIなら何でも出来るんでしょ? この前ニュースでやってたぞ」という姿勢でやってくる

AIの開発と通常のシステム開発の違いを教えてください。

AI開発は受注したというものの、最終的な納品物を定義することが難しいです
例えば、精度95%(accuracy)と言っても達成できる保証はなく納品物として定義するとひどい目をみることがあります
SIと大きく異なるのはこの「納品物の定義が難しく、ある意味あいまいである」ということだと考えています

スケジュールを切って、この日まで確実なモデルが手に入ることはほとんどないということです
精度といっても全てのデータに対する精度ではないので。あくまでもテストデータや軽い運用で得た結果なので
時間とともに精度も変わるでしょうし、中々、難しいところではあります

システムエンジニアからAI技術者へ転身するには、具体的にどのような学習が有効でしょうか。

システムエンジニアとしての経験があるのであればコードはすらすらかけると思うので何か機械学習の問題に取り組むのが良いのではないでしょうか
そして、AI開発とSI開発が異なることを知ること、理解することです

同じように考えると痛い目にあいますし、部下に痛い目をあわさせてしまいます
上司がSI出身やセールスでAIの領域の理解が進まなくて案件や自社プロジェクトが停滞している問題は頻繁に発生しています

一般的なSierであまり馴染みがないPoCという工程(考え方)について教えて欲しい。

先ほども回答した通り、そもそもAI開発というものは最終的な納品物を定義することが難しいです
そのため、そもそもこの問題って機械学習で解決できるものなのかを調査するような、砕けた言い方をすれば「お試し」が必要になります
機械学習で解けない問題を受注して、最終的な納品物が達成できないというリストを回避するためにもミニマムでスタートして
実現可能なのかをPoC(proof of concept)で確認をするという流れになるのです

なぜPoCが必要なのかということをクライアントに伝えることは非常に重要でセールスにも絶対に理解してもらう必要があります
基本的にクライアントは現状ではAIは何でもやってくれるというレベル感なので説明が重要です

しかし、その一方でいつまでもPoCばかりで本開発に乗り出せないPoC地獄という問題も待ち構えている...辛い...

テーマ3. 具体的な仕事

現在、案件で最も多いデータの種類は何か(画像、時系列など)

ここ数ヶ月は時系列データ(言語データ)が特に多いかな
正直なところ、画像の分類はクラウドや手軽なサービスが多く、誰にでもトライできるレベルになっています

その点、言語データは扱いが難しく、何よりも課題の落とし込みが難しいですが
世界的な技術のレベルが上がってきたため、ようやくビジネスにも展開できるようになったという印象です

今まで一番テンションの上がったプロジェクトは何ですか。

言語生成モデルの作成(雑談bot)

1日の仕事の流れを教えて頂きたいです。

日によって様々ですが、リストだしをするとこんな感じに

  • セールスとの打ち合わせ
  • セールスに同行してクライアントと打ち合わせ
  • データの処理やらクレンジング
  • ラベル付け(アノテーション)
  • モデル構築
  • 報告レポートの作成
  • API作成
  • cloud環境の作成
  • 最新動向の調査

など、今思いつくだけでもこれだけあります
特に重要なのはクライアントの課題を見つけて機械学習に解ける形に落とし込むこと
社内のメンバーに期待するよりも私は自分でクライアントの元へ行ってヒアリングをした方が早いと考えています

ユーザーへどのようなアウトプットをされていますか。

このブログを読んでいただきありがとうございます

大小さまざまだと思いますが、一般的に一案件あたり、どれくらいの人数/工期なのでしょうか。

自身が所属するAIチームでは1案件に対してそれぞれが手を動かすため、はっきりした人数は出せないが2ヶ月でPoCを実施することが多いです
(クライアントの金額感がちょうど2ヶ月程度なため)

いわゆる機械学習エンジニアがヒアリングから課題設定、工数出し、レポート作成までほとんど全てを行なっています

テーマ4. 企業の特性

AI領域でいち早く経験を積むなら、現状は、データを持つ会社に行くのがいいか、AI技術をもつ会社に行くのがよいのか。

これは非常に難しい問題で、どちらにもメリットがありデメリットがあります
データを持つ会社で働くということは自社のプロダクトとして稼働できるため、無理難題を押し付けられても
ある程度ストップをかけることが出来るし、失敗しても社内だけの影響に終わりますが
受注では失敗は基本的にクライアント側から許されることはなく、リスクを常に抱えている

しかし、その一方でAI技術をもつベンチャーに行けば、クライアントへのヒアリングから提案、APIの作成までと
幅広い領域でAI開発に携わることが出来る。データを持つ大企業では割り振りされる作業はごく一部だろう

よく聞く黒い話はこんな感じ

  • データを持つ会社(大企業)でデータサイエンティストとして公募しておいてアノテーションを永遠とやらせる(スクールからの紹介が多いそうなので要注意)
  • AI技術をもつ会社(ベンチャー)では任されるタスクの範囲が広く、手に負えない

どちらを選ぶかはあなた次第です

AIの分野で今最も熱いベンチャー企業はどこですか。人工知能で最も注目している会社を教えてください。

日本で言えばやはりPFN。強靭、無敵、最強!!
普段、会社名を意識して情報収拾しないなので正直なところ会社名には疎いです

御社のデータサイエンティストの年収はいくらぐらいですか。

私の口からは大きな声で言えないが、普通の新卒より少し多いくらい(学部卒: 新卒23歳)

テーマ5. 将来像

これから先が楽しみな分野ですが、10年後にはこの技術はどうなっていると考えていますか。

いわゆる人工知能というものへの参入の障壁は下がり続け、現在の車やコンピューター同様に誰しも気軽に使えるものになっているでしょう
開発の難易度も同時に下がる。しかし、開発の難易度が下がるということはイコール、個人によって便利なものが生まれるとは考えていないです

人工知能というのは課題解決のための手段の1つであり、何に使うかが非常に重要なため、結局はアイディアは人間が捻出するので
人間の仕事を全て置き換えるような状態にはなっていないんじゃないかと勝手に想像してます

AI・人工知能に関する開発で、現在はまだ難しいがいつか解決したい社会の課題や、実現したい(創りたい)ことはありますか。

ひしひしと仕事をする中で感じさせられていることは「なぜそうなったのか」というプロセスが確認できません
いわゆるブラックボックスとして認知されている現在のディープラーニングでは実運用には困るということです
なぜそうなったのかが分からないということはすなわち、説明が出来ないということになります

「なぜ私が人工知能によってガンと診断されたのか教えてください」と問いかけても
「そういうものですので」という返答が返ってくるわけです

現在も研究は広く行われているが、イメージするには十分でしょう
いかにして人工知能(ディープラーニング)の決定プロセスを確認するか(xai -> 説明可能なAI)という分野の今後の発展に期待しています

また個人的には人工知能の技術を農業に落とし込みたい
日本は食料自給率が大変低く、多くを海外からの輸入で賄っています
その一方で、世界で最も食料を廃棄する国でもあるわけです

この矛盾はなんなのか。どうにかして解決したいと密かに思っています

感想

普段は前にたって偉そうにお話をすることはないので大変、緊張しました
正直なところ、研究という立場で開発をしていないので技術力に関してはへっぽこだと思う
しかし、どのようにビジネスとして運用するかという経験は多少なりとも積んできたつもりだ

何が問題になるのか、何が出来るのかは理解しているし、説明することも出来る
今後、技術がより一般的になる一方で課題を技術で解けるように落とし込むことは以前変わらず難しいだろう

これは私の生存戦略の1つで換えの効かないスキルとしてより高めていこうと感じた

小難しい話ばかりしてきたが、最後は教えていただいた「だるま」にて打ち上げ。本場はやはりソースの2度付けが禁止でした

f:id:takamizawa46:20190811001736j:plain

【空いた時間を有効活用】暇な人・挫折した人に見て欲しいTEDの素晴らしい公演7選

TEDとは何か

www.ted.com

世界中で講演会を開催している非営利団体であり、毎年世界各地で数多くの公演を開いている
公演の内容は非常に幅広く、経済や政治にはじまり技術や人生観など様々で非常に多様性がある
また、その公演の多くはTED公式サイト、youtubeなどを通して広く配信されており、気軽に公演を視聴することができる
海外の団体であるが、すでに日本人でも多くの方がTEDの公演に出演しており、非常に素晴らしい公演を披露している

私はTEDが好きで、落ち込んだ時ややる気が出ない時、作業用動画としてそこそこの数の講演を動画で見た
今回は過去のTEDの公演の中からyoutubeで視聴可能な日本人の公演もしくは日本語字幕があるものを10個を選別した
それぞれの動画に私がどんな時に見たものかという説明もつけた

  • 暇でやることがない
  • やる気ができない
  • 挫折してしまった
  • いいアイディアが思いつかない

など様々なコンセプトで動画を選別したので、ぜひ視聴してみてほしい
講演会のタイトルや公演内で使用されている英語を私の独断で翻訳して解釈している部分があり
正しい翻訳でない場合があるのでご了承お願いします

字幕はyoutubeの字幕のアイコンをクリックすることでon/offを切り替えることが可能です

ちなみに補足だがTEDにはTEDxという世界各地で行われている講演会がある
TEDxTOKYOのようにxの後には講演会を開催した場所が記述される
近年、日本各所でTEDxが開催されている

夢を叶えるために大切なこと

www.youtube.com

「思うは招く」
私がTEDにハマるきっかけとなった公演
北海道の民間企業でロケットの開発・研究を行う植松努さんの公演で、夢をあきらめないことの大切さを教えてくれる
幼少の頃より「ロケット開発を、おまえなんかに絶対できるわけない」
バカにされ続けてきた植松さんがどのように考え、どのようにして夢を実現したのかが語られている

笑いあり涙あり。「どうせむり」という言葉がいかに人の熱意を奪うのか。ぜひ一度視聴してみてほしい

1つのことを続けることができない(熱中できない)

www.youtube.com

この公演で語られている内容はまさに私のことだと感じた
昔から1つのことに熱中することが出来ず、すぐに違うことに興味が湧いてしまい、そちらに気持ちが流れていく
1つのことを極めることが出来ず、「自分はダメなやつだな...」と昔から劣等感を感じていたが、この公演を見て考え方が変わった

1つのことに熱中できず、様々なことに興味がもてる人を「マルチ・ポテンシャライト」と呼び
これは一種の才能であると語っている
さまざまな分野の知識がかけ合わさり、あなたにしか考え出せないイノベーションが生まれる。素晴らしい才能だということを教えてくれた

いいアイディアが思いつかない

www.youtube.com

今では懐かしさも感じるが平成の時代に「無限プチプチ」というおもちゃが大流行した
今でいう手持ち扇風機のような存在で、学生時代にはどのクラスにも1人は持っている人が居た
そんな「無限プチプチ」を開発した高橋さんが普段どのようにして独創的なアイディアを生み出しているかという方法が語られている

「アイディアは掛け算」である。A x Bの組み合わせで無限にアイディアを生み出すことが出来る。Bはしりとりでワードを繋いでいく
りんご x B で何かを考えてみた

  • りんご x スマホ
  • りんご x ホットケーキ
  • りんご x 切り方
  • りんご x たこ焼き
  • りんご x 幾何学

一見くだらないかもしれないが、この中からとんでもないアイディアが生まれることがあるというわけだ

何かを成し遂げるための時間の使い方

www.youtube.com

  • 叶えたい夢がある
  • 成し遂げたいことがある
  • お金持ちになりたい

など人には様々な目標がある
しかし、多くの人は「時間がない」と口にして実行に移ることはない
本当にそうなのかということを実数値で算出して時間の大切さを教えてくれる
100歳まで生きるとして誰しも平等に与えられている自由な時間は14年と4ヶ月

「この時間をどのように生きるのか」ということを教えてくれる
自分のためではなく誰かのために命を使う、つまりは「あなたの使命」をどのように考えるのか

今ある時間を大切に使わねばと考えさせられる公演
大学生の頃に視聴して貴重な時間をなんてくだらないことに費やしているんだろうと踏み出す勇気をもらった

考えの違う人を理解する方法

www.youtube.com

そりの合わない上司や同僚で悩んでいた時にたまたま見た公演
アンさんがアメリカから23歳の時に日本に越してきて日本の生活の中で見つけた「世界観」にまつわる話
なぜ日本人が「いただきます」という言葉を食事の前に言うのか。それは「Let's eat」という意味ではないということ
命を捧げてくれた動物・植物への感謝、運んでくれた人、全ての携わってくれた人への感謝が込められているということ

国や人はそれぞれの世界観をもっており、大事にしているものが違う

上司、友人、同僚、恋人、家族...それぞれが大事にしている「世界観」は異なる
人の大切にしている世界観に興味を持つことで自分と自分の将来を変えることが出来るということを教えてくれる
博多弁でユーモアに溢れる非常に素晴らしい公演

何でも良いからやってみて続けてみること

www.youtube.com

「何かしたいけど何をしていいのか分からない」

そんな時に荒川さんは兄から「口だけやん」と叱責され、突如「ゴミ拾い」を始めることを決意した
「ゴミ拾いなら出来るやろ」というマインドで毎日ゴミ拾いを始めた
ゴミ拾いを続ける中で非常にたくさんの嫌がらせを受けつつも、続けていくうちに仲間が増え、取材され...テレビにラジオに...

全国各地からゴミ拾いをする仲間が集まってきた

どんなことでも初めて続けてみることが大切、何かを成し遂げれば自信を身につけることが出来る
ゴミ拾いから始まった人生が大きく変わっていく話。どんなことでもバカにされても否定されてもやってみることの重要さを教えてくれる

誰しも最初から出来たわけではない。どんな人でもはじまりは小さなことだと気づかされる

数学の面白さ・不思議さ

www.youtube.com

私は昔から計算間違えが多く、算数や数学という科目でいつも高い点数が取れずいつのまにか苦手意識をもっていた
考え方や論理には興味がもてるものの、好きかと言われれば嫌いだったと思う

そんな中で、この公演で紹介される「ガウス曲線」とゴルドンボードの実験に鳥肌がだった
ランダムという現象がもたらす結果の分布はガウス曲線の形になっている

この現象が数学で証明されること
初期のgoogle検索の被リンク優位がゴルドンボードの実験と同じ現象を使って作られているということに何とも言えない神秘さを感じさせられる

数学は決して得意とは言えない私だが、興味を持つべき対象であるということを教えてくれた素晴らしい公演

【レポート】第10回清流elixir勉強会を開催しました【Elixirで速度測定】

トピック

今回で第10回目の勉強会を開催致しました
elixir-sr.connpass.com

3月の下旬に始めた当勉強会も、気づけば10回もやってたのかと感慨深い
「どうやって運営してるんですか?」とよく聞かれることがあるが別に何か特別なことはしていないはず
ただ、自分の知りたいことをテーマとして会場に持ち込んだり(ある程度アジェンダは組む)
参加者の方から気になってることを聞いて、調査しながら手を動かしたりと

ポイントは毎回のテーマの濃さよりも、とりあえずは続けることだと考えている
続けている内に内容も濃くなっていくはず

今回は前々から気になっていた
パターンマッチによる条件分岐とif, caseのような制御構文とどっちが速いのかを測定してみた
Elixirのお作法としてはパターンマッチを推奨しているが、実際は速度はどうなんやろと単純な疑問にて開催

清流elixir-infomation
開催場所: 丸の内(愛知)
参加人数: 5 -> 5
コミュニティ参加人数 : 13 -> 13
20190810現在

第10回の勉強会の内容について

Elixirに用意されている制御構文について

f:id:takamizawa46:20190810091037j:plain
パターンマッチによる条件分岐を推奨しているもののElixirにも制御構文は用意されている
それぞれについて詳しい説明はしないが、ざっくりと列挙する

  • if else(else if は無い)
  • unless
  • case
  • cond

ifやcase(jsでいうswtichに近しい)については説明するまでもないので省略
unlessとcondについては私は普段全くと言っていいぐらい使っていないので、今回初めて触れることになった

unlessについて

プログラミングElixirではunlessについては色々と酷い扱いがされている
実際のユースケースが私にも想定できない。not使えばええやんって
どうやらrubyではnotを使うことを推奨していないようでunlessを使う機会があるらしい
どっちにしろ極力使わないようにしようと思っている

unless true do
  IO.puts("--> True")
else
  IO.puts("--> False")
end

# result
--> True

condについて

condはconditionの略なんやろうな〜と思ってる。caseに似ているが条件式を複数個記述することが可能
以前、入門会で書いたFizzBazzもcondを使うとこんな感じにまとめる

num = 15

cond do
  rem(num, 15) == 0 -> IO.puts("FizzBuzz")
  rem(num, 3) == 0 -> IO.puts("Fizz")
  rem(num, 5) == 0 -> IO.puts("Bazz")
  true -> IO.puts("another")
end

# result
FizzBuzz

場合によっては使い所がありそうな気がするが、パターンマッチで条件分けが可能なので個人的な使用率はかなり低い
Elixirの制御構文について3段階で総合評価(max=3)をそれぞれにすると(個人的主観)

制御構文 かきやすさ 使用頻度
if 3 3
case 2 2
unless 3 1
cond 2 1

Elixirでの速度測定について

f:id:takamizawa46:20190810091044j:plain
速度の測定についてはElixirでの実行速度の測定と色々と実験してみた【Enun.sum vs Enum.reduce etc...】にまとめたのでこちらを参照してほしい

ざっくりとだけまとめておくと、Elixirには速度測定のための関数が用意されていないためErlangの:timer.tcという関数を使用する
:timer.tcには3種類の引数パターンがある

  • :timer.tc(function)
  • :timer.tc(function, [arguments])
  • :timer.tc(module, :function, [arguments])

戻り値は2つの要素をもつタプルになっている

{sec, result} = :timer.tc(function)

速度を測定してみる

ここまで準備が整ったところで、さっそく今回の目標である
「パターンマッチと制御構文どっちがはやいねん」を検証する

比較するモジュールは以下。めっちゃシンプルな受け取った果実名に対応する値段を返すのみ
リストを受けて測定をするためにhelper関数(helper_for_price)をそれぞれに用意している

# pattern match
defmodule Sample do
    def helper_for_price(lst) do
        Enum.map(lst, fn f -> price(f) end)
    end
    def price("apple") do
        110
    end
    def price("banana") do
        70
    end
    def price("orange") do
        120
    end
 end
 
# 制御構文(case)
 defmodule Sample2 do
    def helper_for_price(lst) do
        Enum.map(lst, fn f -> price(f) end)
    end
    def price(fruit) do
        case fruit do
            "apple" -> 110
            "banana" -> 70
            "orange" -> 120
        end
    end
 end

とりあえず記念(何の)に1度、それぞれを測定してみる

{sec1, _res} = :timer.tc(Sample, :price, ["apple"])
{sec2, _res} = :timer.tc(Sample2, :price, ["apple"])

IO.puts("pattern match: #{sec1}")
IO.puts("case: #{sec2}")

# result ----
# pattern match: 1
# case: 0

早すぎてcaseが0になってて草
単位はマイクロ秒なので恐ろしく速い
次に100個の要素を持つリストをhelper関数に渡して測定してみる
そのためにランダムでfruit名("apple" or "orange" or "banana")を指定個数分、要素にもつリストを用意する必要があるため
Enumを使ってさくっと作成

lst_size = 10
fruits = ["apple", "banana", "orange"]
fruits_lst = Enum.map(1..lst_size, fn _ -> Enum.random(fruits) end)
IO.inspect(fruits_lst)

# result
# ["orange", "apple", "banana", "banana", "orange", "apple", "banana", "orange", "apple", "apple"]

それぞれのモジュールのhelper関数に作成したリストを渡して速度を測定する
リストの要素数は適当に100000にした

lst_size = 100000
fruits = ["apple", "banana", "orange"]
fruits_lst = Enum.map(1..lst_size, fn _ -> Enum.random(fruits) end)

{sec1, _res} = :timer.tc(Sample, :helper_for_price, [fruits_lst])
{sec2, _res} = :timer.tc(Sample2, :helper_for_price, [fruits_lst])

IO.puts("pattern match: #{sec1}")
IO.puts("case: #{sec2}")

# result
# pattern match: 13832
# case: 9151

先ほどはほとんど差がなかった要素数が増えると如実に結果に現れた
ほう、何と制御構文の方が速いではないか
同じ条件で測定を100回行い平均値を算出してみる

lst_size = 100000
fruits = ["apple", "banana", "orange"]
fruits_lst = Enum.map(1..lst_size, fn _ -> Enum.random(fruits) end)


try_num = 100

sec1s = Enum.map(1..try_num, fn _ -> :timer.tc(Sample, :helper_for_price, [fruits_lst]) end)
        |> Enum.map(fn {sec, _} -> sec end)
        |> Enum.sum()

sec2s = Enum.map(1..try_num, fn _ -> :timer.tc(Sample2, :helper_for_price, [fruits_lst]) end)
        |> Enum.map(fn {sec, _} -> sec end)
        |> Enum.sum()
        
IO.puts("pattern match: average -> #{sec1s / try_num}")
IO.puts("case: average -> #{sec2s / try_num}")

# result
# pattern match: average -> 22682.64
# case: average -> 20379.93

やはり若干だが、caseの方が速いよう
ただほぼ誤差程度だと言えるので、だからパターンマッチよりも制御構文使った方が良いかというとそうではないと考える
公式がパターンマッチによる分岐を推奨しているので脳死で私はそちらを選択する
ただ、こうして数値として出してみることで考える機会になったので非常によかった

次回について

まだ内容について特に決まっておらず、現在模索中
そろそろ並列処理をやってもいいかなとも思っているし、phoenixをやりたいとも思っている
興味のあることや、気になるテーマがあればコメント、twitterなどでご意見を頂ければと思います
バラエティみたく気になるテーマを調査しますので

fukuoka.exさんのもくもく会にリモートで参加させて頂きました【Phoenix入門】

参加したもくもく会

以前から合同で勉強会をさせて頂いていたり、様々なことを教えて頂いているfukuoka.exさんのリモート勉強会に再び参加させて頂きました
名古屋には多くの勉強会があるとは言える状況ではないですが、リモートで接続可能な勉強会があるというのはありがたいことです

fukuokaex.connpass.com

また今回のもくもく会で以前よりtwitterでよしなにして頂いているElixirの先駆者piacereさんに初対面致しました

twitter.com

いつも「いいね、RT」頂きまして本当に有難うございます!!

今回も経験者トラックで参加しておりますが、選んだテーマはPhoenix入門
PhoenixでjsonAPIを作成したことはあるんですが、ページビュー含めて開発したいなと思ってこのテーマを選定
謎のエラーがでる & ほとんど清流elixirのメンバーと雑談しつつ、もくもくしていた(?)のであまり進めることは出来なかった

何とかphoenixのwelcome画面まで進めることが出来た

Phoenix大入門

いまさらながらPhoenixでプロジェクトを始めるまでの手順をまとめてみる
元々、elixirは自分の環境にインストール済みだったのでphoenixをインストールする(あれ、インストールしてたはずなんやが...)

$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez

無事にインストールが完了したっぽい

$ mix phx.new -v
Phoenix v1.3.4

さっそくプロジェクトを作成してみる
今回は以下の記事をもとにミニマムブログを作成してみる

qiita.com

プロジェクトの名前はmin_blogとした
コマンドを実行する

$ mix phx.new min_blog

お、色々と生成されて無事にプロジェクトが始められたっぽい

* creating test/config/config.exs
* creating test/config/dev.exs
* creating test/config/prod.exs
* creating test/config/prod.secret.exs
* creating test/config/test.exs
* creating test/lib/test/application.ex
* creating test/lib/test.ex
:
:
* creating test/assets/static/robots.txt
* creating test/assets/static/images/phoenix.png
* creating test/assets/static/favicon.ico

依存関係をインストールするかと聞かれているので「Y」と入力してお願いする
Yを選択すると

mix deps.get
:
mix deps.compile

のコマンドが走り、必要な外部ライブラリをDLしてくれる
DLが終了して「さぁPhoenixサーバーを立ち上げてくれよな」という文言が現れる

    $ cd min_blog

Then configure your database in config/dev.exs and run:

    $ mix ecto.create

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server

言われる通りにコマンドを実行していく

$ cd min_blog
$ mix ecto.create

おや... 謎のerrorが...

** (Postgrex.Error) FATAL 28000 (invalid_authorization_specification): role "postgres" does not exist
    (db_connection) lib/db_connection/connection.ex:163: DBConnection.Connection.connect/2
    (connection) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol
** (Mix) The database for MinimumBlog.Repo couldn't be created: FATAL 28000 (invalid_authorization_specification): role "postgres" does not exist

なんか怒られた
postgresqlはインストール済みであることを確認

$ psql --version
psql (PostgreSQL) 11.2

どうやら「postgres」というroleが生成されていないようなので急いで作成する

$ createuser -d postgres

再度、dbをcreateするためにコマンドを実行する
またエラーかい〜〜

$ mix ecto.create
warning: found quoted keyword "min_blog" but the quotes are not required. Note that keywords are always atoms, even when quoted. Similar to atoms, keywords made exclusively of Unicode letters, numbers, underscore, and @ do not require quotes
  mix.exs:57

The database for MinimumBlog.Repo has been created
➜  minimum_blog mix phx.server
warning: found quoted keyword "min_blog" but the quotes are not required. Note that keywords are always atoms, even when quoted. Similar to atoms, keywords made exclusively of Unicode letters, numbers, underscore, and @ do not require quotes
  mix.exs:57

warning: please add the following dependency to your mix.exs:

    {:plug_cowboy, "~> 1.0"}
:
:
[info] Application minimum_blog exited: MinimumBlog.Application.start(:normal, []) returned an error: shutdown: failed to start child: MinimumBlogWeb.Endpoint
    ** (EXIT) shutdown: failed to start child: Phoenix.Endpoint.Handler
        ** (EXIT) "plug_cowboy dependency missing"
** (Mix) Could not start application minimum_blog: MinimumBlog.Application.start(:normal, []) returned an error: shutdown: failed to start child: MinimumBlogWeb.Endpoint
    ** (EXIT) shutdown: failed to start child: Phoenix.Endpoint.Handler
        ** (EXIT) "plug_cowboy dependency missing"

あーこれは分かりやすいエラー。外部ライブラリが不足している様だ
./mix_blog/mix.exs/に以下を追加して再び外部ライブラリをDLする

defp deps do
    [
      :
      :
      {:cowboy, "~> 1.0"},
      {:plug_cowboy, "~> 1.0"}
    ]
  end
$ mix deps.get

3度目の正直。ecto.createを実行する

$ mix ecto.create
Generated min_blog app
The database for min_blog.Repo has been created

やったぜ。phoenixサーバーを立ち上げる

$ mix phx.server
[info] Running TestWeb.Endpoint with cowboy 2.6.3 at 0.0.0.0:4000 (http)
[info] Access TestWeb.Endpoint at http://localhost:4000

ようやくwelcome画面とご対面
f:id:takamizawa46:20190803144405p:plain:w700

ここまで長かった
docker-composeを使って楽すれば良かったと後悔

お知らせ

いつもお世話になっているfukuoka.exさんが小倉城という城(すごすぎ)をジャックして
ElixirConf JP 2019 Kokurajo~を開催されます

fukuokaex.connpass.com

私は現在お財布と予定とにらめっこ中です
会社からElixir関連では経費が落ちないそうです。くぅ~