やわらかテック

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

【頭おかしい(褒めてる】ゼロからトースターを作ってみた結果を読了

なぜ読んだのか

下記ブログにて紹介されていたところを発見し、ぶっとんだタイトルとおそらく著者が作成したであろうトースター?のサムネイルが強烈すぎて、これは読まずにはいられない!!とAmazonにて即購入。中古品でだいたい600円ぐらい。新品だと税別で750円の模様
著者が当時は大学院だったというバックグラウンドも面白いと思った
※ブログ運営費のためリンクを貼らせて頂きます

いわゆる文庫本のサイズでページ数は約200ページ程。元々がブログであったためにシンプルかつユーモアな記述でスラスラと読むことができ、2時間ほどで読了

概要

お察しの通りだが、タイトルのままである
著者トーマス・トウェイツは突如「トースター」を作ることを決意する。トースターとは何か。日本人ならば誰しもが知っているパンを焼くアレだ
そんなトースターをゼロから、つまりは実際に鉄鉱石を採取して精錬して型を取るようなレベルから作成が始まる
30ページぐらいにはキャリーバック1つでツルハシも持たず鉱山に鉄鉱石を堀りに行き出して最高にぶっ飛んでいる

その中で様々な問題にぶち当たり、バカなのか天才なのか(褒めてる)と思わせるアイディアで問題を乗り越えていく

本書で扱うメインのトピックは個人視点で3つ

  • 金属について
  • プラスティックについて
  • これまでの製作を経て、実際にトースターを焼くまで

それぞれのトピックに問題とアイディアがあり、経験を経て著者が何かを感じていく..
いかに自分の力が世界のスケールでは小さいもので、身の回りには長い歴史を得て形作られた様々な技術が溢れていることに気づいていく

著者のTwitterが無いものかと調べたところTEDでの公演を発見したので少しでも興味があれば視聴してみてはいかがでしょうか

www.ted.com

プロダクト視点で面白いと思ったポイント

個人的にいいねと思ったのは2点ある

専門家に相談するということ

1つは無謀とも思えるトースターをゼロから作り上げるためにどのような計画を立てれば良いかという部分で非常に感心させられた
著者は当時デザインを学ぶ大学院生であり、電気機器や電気回路の知識が必ずしもある状態ではない。その状態でスタートして、最終的にトースターを作れたのは何故なのか?(パンは焼けなかったけど..)

著者が最初に行なったことは「トースターの分解」であり、すなわちリバースエンジニアリングとよばれる作業だ
市販のトースターを一度、細部まで分解してみることで

  • どんな材料が必要なのか
  • どんな作業が必要なのか
  • どんな道具が必要なのか
  • どんな技術を知る必要があるのか
  • どんな知識を知る必要があるのか

という点をリスト出しすることで何をすればいいのかをハッキリさせている
その上で、同大学の鉱石の分野で活躍している教授、鉱山職員や、企業、多くの専門家の意見やアイディアを取り入れていく
分からないことは専門家に聞くというフットワークの軽さに驚いた

私が推しているElxiirの前衛となったErlangというプログラミング言語の作者の1人であるJoe Armstrongも以下の記事でこう答えている

問題を解決する際には、まず最も難しい問題から解決せよ
最も難しい問題が何であるかは専門家に聞け

postd.cc

素人には何が最も難しい問題なのかを判断することがそもそも出来ない。何が最も難しい問題なのかをハッキリさせるためにも専門家の協力を仰ぐということは非常に重要なことだということをJoeは語っている

日本では他社に協力を求めるという行為はあまり推奨されない。それ故に著者の当たり前のようなこの行動に思わず「いいね」と思った

身近にあるものは多くのモノの集合であること

トースターを家電屋で買おうと思えば1000円もあれば現代では十分だろう
しかしながら、トースターがトースターというものを型取るためには多くの材料と技術が使用されている

電気回路を作成するための基盤や、パンをトーストするための発熱体、電源コード、カバーに至るまで多種多様だ
上げればキリが無いが、どれか1つ欠けてもトースターはトースターになることはない

どれだけ鉄の精錬を熟知していてもそれはトースターを作る過程ではある1つの知識にしかなり得ない
しかしながら、鉄の精錬を熟知しているものがいなければトースターはいつまで経っても完成しないだろう

現代の物の多くが相互、影響を与え合って作られている。そのことに気づき、我々に伝えてくれる著者に「いいね」と思った

ここに書いていないこと

なぜ倫理をもって技術を考えるべきなのかという著者の考えがまとまっており、この部分はぜひ自身で読んでみてほしい(要約: 買ってね)
人によっては考え方が分かれそうなので、あえて記述しません

docker-composeを使ってReact+phoenix+postgresの環境を構築する

概要

ほとんど個人的メモです
自身でよく構築することの多い以下の環境を毎度、調べながら作るのが面倒なのでまとめたって感じです

  • React(front end)
  • phoenix(backend, elixir web framework)
  • postgres(open source database)

毎度悩むのが、dockerのimageをpullしてきたけど、どの場所、どのタイミングでcreate-react-appなりmix phx.new ...を実行するかなといったところ
個人的なベストプラクティスとしてはlocal環境で作成して依存環境ガァ^〜で何度も怒られてつらい思い出があるので
作成したcontainerの内部で上記のコマンドを実行してプロジェクトを作成し
生成されたプロジェクトをcontainerからlocal環境にコピーしてくるという方針を採用してみた

プロジェクトの生成環境が実行環境と同じになるというメリットがあるが、準備に多少の手間が発生する
しかしながら、やることは毎度同じなので大した苦労ではないなーと感じている(まだ2回しか作ってないけど)

やってることは色んな記事からアイディアをパクってまとめただけです
記事の一番最後にreferenceをまとめているのでご参照下さい

共通作業

pathはどこでも構わないので適当にdocker-composeをwrapするディレクトリを生成しておきます

$ mkdir react-phoenix-postgres
$ cd react-phoenix-postgres

以下の作業は全て、このディレクトリ内で行います
ちなみにDockerとdocker-composeについてはinstall済みという前提で進めていますのでお願いします

React環境の構築とcreate-react-appの実行

まずはReactのための環境から。node imageをbaseにした環境を用意して、そこでReactのプロジェクトを作成します
さっそくReactのためのディレクトリとDockerfileを生成

$ mkdir frontend
$ cd frontend
$ touch Dockerfile

続いて最低限の設定をDockerfileに記述

FROM node:12.2.0-alpine

RUN mkdir /app
WORKDIR /app

# nodeとnpmがinstallされているか確認用。省略しても問題なし
RUN node -v && npm --version

このDockerfileを使ってcontainerを作成します
-t というオプションの後に記述されているのは割り当てるタグと呼ばれるものです
後にタグを使ってコマンドを実行するので視認性のあるものにしておくと良いかと思います

$ docker build -t react_test:dev .

このコマンドを実行することで手元のDockerfileを元にcontainerが作成されます
メッセージログが流れてしまったので恐縮ですが

RUN node -v && npm --version
12.2.0
6.xx

Successfully built **containerID**

のようなメッセージが出ていればnode環境の作成は完了です
次にcreate-react-appを実行するための用意をします

create-react-appのコマンドは一度しか使用しないので、dockerのcontainerにaccessして、直接installします まずはcontainerにaccessするためにdockerのcontainerを立ち上げます
以下オプションの詳細ですが、無視してコマンドを打って頂いて構いません。一応書いておきました

  • d -> backgroundでdocker daemonを実行
  • t -> tty true -> docker containerを立ち上げたままにしておく
  • p -> spec port -> 立ち上げるportとexposeするportの指定
  • name -> containerに名前を割り当てる(タグとは別で実行中のみ有効と判断しています)
  • react_test:dev 先ほど付与したタグ名

cotainerを立ち上げます

$ docker run -dt --name react_train -p 3000:3000 react_test:dev
# cotainerIDが表示され、terminalが入力可能状態になれば成功

よくあるミスとしては以下が候補に上がります

  • すでにportが使用されている(別のportにするか重複元の環境を停止させる)
  • 同盟の--nameが割り当てられたcontainerがすでにUPされている(docker stop containerIDで停止させる)

問題なく進められていれば先ほど立ち上げたcontainerにashを使ってアクセスします
なんでash?ってなりますけど、alpineではshellにashが使われているそうで、bashやらにすると怒られます

$ docker exec -it react_train ash

無事にアクセスができるとroot#containerIDのようなプロンプトが立ち上がります
まずは適当にnode.jsとnpmのversionでも確認しておきます

# dockerのcontainer内部
node -v && npm --version
12.2.0
6.x.x

無事にinstallされているようなので、次にcreate-react-appコマンドが使いたいのでnpm経由でinstallします
ここは-gをつけてglobal installにしておいてください。package.jsonファイルが現状のディレクトリにないので-gをつけないと怒られます
ついでにcreate-react-appも実行します

$ npm i -g create-react-app && create-react-app app-name

Happy Hacking!と準備できたからserver立てれるよって文言が表示されていればOKです
これでプロジェクトの生成は完了です。あとはこの作成したプロジェクトをlocal環境にcopyしてあげるのみ

exitと入力し現在、アクセス中のcontainerから離脱します
続いて、以下コマンドを使ってlocal環境にプロジェクトをcopyします

# 現在のカレントディレクトリにプロジェクトをcopy
$ sudo docker cp react_train:/app/app-name .

これでlocal環境に生成したプロジェクトがcopyされた
しかしながらcopyされたプロジェクトの権限をlコマンドで確認してみるとオーナーがrootになっていることがある
そのためchownを使ってオーナーを変更しておきます(後にハマってだるいことになったので

sudo chown -R login-user-name app-name

再びlコマンドで確認してオーナーが自身のユーザーネームになっていればOKです
これでReactのプロジェクトの生成は完了です

phoenix(Elixir)環境の構築とmix phx.newでのプロジェクト作成

やることはReact環境を用意した時と全く同じです。まずはディレクトリとDockerfileを用意します

$ mkdir backend
$ cd backend
$ touch Dockerfile

やはり最低限の設定をDockerfileに記述

FROM elixir:alpine

WORKDIR /app

RUN yes | mix local.hex
RUN yes | mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez
RUN mix local.rebar --force

# elixirがinstallされているかの確認用(省略可)
RUN elixir -v

先ほど同様に作業を進めていきます

$ docker build -t elixir_test:div .
$ docker run -dt --name elixir_train -p 8080:8080 elixir_test:dev
$ docker exec -it elixir_train ash

これでphoenixのプロジェクトの作成準備は整いましたので早速...

# dockerのcontainer内部(phoenixの生成に関するオプションは適宜、自身で付与してください(no-webpackを打ち忘れたorz))  
mix phx.new backend

これでphoenixのプロジェクトが生成されたので同様にlocal環境にcopyします

$ sudo docker cp elixir_train:/app/backend .

phoenixのプロジェクトの生成は以上ですが、database設定を行う必要があるためdocker-composeの設定と共に行なっていきます

docker-compose.ymlの作成とdatabaseの設定

これまで行なってきたdocker runをdocker-composeを使ってまとめてできるようにしてあげます
記述量は若干ありますが、どれも1つずつ見るとやっていることは大したことではないのでご心配なく
レゴを積み上げるようなノリで記述出来ます

version: "3"

services:

  # reactへのsetting
  frontend:
    build:
      context: test # buildしたいDockerfile(node)が格納されているディレクトリ
      dockerfile: Dockerfile
    container_name: react_frontend # cotainerに命名
    volumes:
      - "./test:/app/test" # 先ほど作成したreactプロジェクトのディレクトリ名
    ports:
      - "3001:3000" # 内部ポートと外部ポートをexpose
    environment: 
      - NODE_ENV=development
    tty: true # 起動を継続するように指定
    working_dir: "/app/test" # マウントしたプロジェクトのディレクトリを指定(package.jsonがあるため)
    command: "npm run start" # 起動時に実行させるcommand

  # phoenixへのsetting(ほとんどReactと同じ)
  backend:
    build:
      context: elixir
      dockerfile: Dockerfile
    volumes:
      - "./elixir/backend:/app/backend"
    container_name: elixir_backend
    ports:
      - "8080:4000"
    tty: true
    working_dir: "/app/backend"
    command: ash -c "mix deps.get && mix phx.server"
    depends_on: # databaseを使いたいので下記記述のdbに連携しておく
      - db

  # postgresへのsetting
  db:
    image: postgres:12.0 # ymlの中でimageを指定
    container_name: postgres_for_phoenix
    tty: true
    volumes:
      - "./postgres/lib:/var/lib/postgres"
    # postgres用の環境変数をset
    environment:
      - MYSQL_ROOT_PASSWORD=rootpassword
      - POSTGRES_USER=root
      - POSTGRES_PASSWORD=root_password
      - POSTGRES_INITDB_ARGS="--encoding=UTF-8"
      - POSTGRES_HOST=db
    ports:
      - "5432:5432"

やっていることはかなり共通事項が多いので特に難しくないです
インフラを構築する才能がないねーと言われた僕ですら書けるのがdocker-composeです。最高か

postgresのマウント用に指定したディレクトリが現在ない状態ですので、作っておきます

$ mkdir -p mysql/lib

phoenixのdatabase設定

configディレクトリ内部のdev.exsの最下部を編集します
先ほどdocker-compose.ymlに環境変数として用意したものに値を変更します

./project-name/config/dev.exs

config :backend, Backend.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "root", # POSTGRES_USER
  password: "root_password", # POSTGRES_PASSWORD
  database: "backend_dev", # ecto.createコマンドを使わないので変更しても良いし、しなくても良い
  hostname: "db", # service名
  pool_size: 10

準備も完了したのでcontainer達を立ち上げてみます

$ docker-compose up --build

frontendとdatabaseは特に問題なく立ち上がるかと思いますが、backendのcontainerはdatabase is not exist的なmessageを出し続けているかと思います
なので接続するためのdatabaseを作ってあげましょう

先ほどdocker-compose up --buildでcontainerを立ち上げているので、postgresのimageをbuildして生成されたcontainerが立ち上がっているはずです
docker psで確認します

$ docker ps
9f676931727a        postgres:12.0          "docker-entrypoint.s…"   2 days ago          Up 49 seconds       0.0.0.0:5432->5432/tcp   postgres_for_phoenix

立ち上がっているのが確認できたら、このcontainerにアクセスして、内部にdatabaseを作成します
postgresのプロンプトを立ち上げる

$ docker exec -it container-id /bin/bash
psql
root=#

先ほど./project-name/config/dev.exsに指定した名前のdatabaseを作成します

root=# create database backend_dev;
CREATE DATABASE

lで作成したdatabaseが存在していることを確認できたらdatabaseの作成は完了です
これで全作業が終了

Reactとphoenixのそれぞれのプロジェクトに対してdocker-compsoe.ymlで設定したportにアクセスしてみて下さい

でそれぞれ、welcomeページが表示されていれば成功です(phx->no-webpackを指定し忘れてcssが読み込まれていない)
f:id:takamizawa46:20191007222214p:plain
f:id:takamizawa46:20191007222301p:plain

docker psで確認すると3つのcontainerが立ち上がっているはずです

$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                    NAMES
a9457b933808        react_train_frontend   "npm run start"          52 seconds ago      Up 49 seconds       0.0.0.0:3001->3000/tcp   react_frontend
a80a6a939350        react_train_backend    "ash -c 'mix deps.ge…"   2 days ago          Up 47 seconds       0.0.0.0:8080->4000/tcp   elixir_backend
9f676931727a        postgres:12.0          "docker-entrypoint.s…"   2 days ago          Up 49 seconds       0.0.0.0:5432->5432/tcp   postgres_for_phoenix

containerを終了したい時はdocker-compose downとすればOKです

参考文献

【レポート】第12回清流elixir勉強会を開催しました【elixirでミニCodeReTreatやろうぜ!!】

トピック

f:id:takamizawa46:20190928165330j:plain:w500

今回で第12回目の勉強会を開催致しました。いつのまに12回も...
elixir-sr.connpass.com

以前よりずっと個人的にやりたいなーって思っていたCodeReTreatを開催しました
名古屋でも開催されているのをちらちらと見たことがありますが、中々参加できない、1日中は難しい...

あ、だったら自分で開催しちゃえばいいじゃんってのが今回の勉強会です
CodeReTreat? 何それ、おいしいの?という方はこちらから詳細をご覧ください

qiita.com

一言でいえば、テーマにペアプロで挑んでお互いに強くなろうぜっていう取り組みです
本来は結構大々的に時間決めて、1日中やろうぜっていうもんですが

  • 時間の都合
  • 場所の都合
  • お金の都合

もあり、ミニCodeReTreatという形で開催をしました(世知辛い...
時間は30分の開発と15分の共有会をワンセット。勉強会時間の都合で2回のセッションを回す形になります

清流elixir-infomation
開催場所: 丸の内(愛知)
参加人数: 6 -> 5 コミュニティ参加人数 : 15 -> 16 update!
20190928現在

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

テーマについて

上記の通り今回はミニCodeReTreatをElixir(別に縛っているわけではない)でやってみた
取り組むテーマは「アルゴリズム」について

テーマの選定に深い理由はなく、それとなく無難に取り組めるテーマだからだ
アプリ開発したり、frameworkを使って何かやろうとすると環境整わなかったりで面倒だったという過去はある
出来る限りシンプルに、開発環境を問わないテーマを選ぶのがコツだと思っている

アルゴリズムといっても話が広すぎるので今回は2セッションのために以下を選定した

第1セッション セレクトソートの実装

私は第1セッションは普段はrubyを使った開発をされているosamumu21さんとペアプロしました
かくいう私もペアプロをというものをしたことがなく、osamumu21さんも同様のことでワイワイとやれたかなと

せっかくなのでということで私が普段業務で使用しているpython3を使ってセレクトソートを実装した

osamumu21さんにコードを書いて頂いて横から主にpython3のsyntaxについてワチャワチャと提案した

その時のメモがこれ
何やら色々とメモがあるね。基本syntaxに関するものが多いかな

# coding: utf-8
# Your code here!
import ramdom

lst = [ramdom(0,100) for _ in range(10)]


def select_sort(lst):
    
    
select_sort(lst)


sorted_lst = []

sorted_lst.(56)


for _ in range(10)


len(lst)

]


if 条件式:
    print("nice")
else:

そして、当日はじめてpython3に挑んだosamumu21さんと書き上げたコードがこれ

# lst = [random.randint(0,100) for _ in range(10)]
lst = [55, 13, 11, 80, 90, 10, 39, 60, 21, 34]

def select_sort(lst, is_min):
    sorted_lst = []
    for _ in range(len(lst)):
        if is_min:
            fetch_val = min(lst)
        else:    
            fetch_val = max(lst)
            
        print(fetch_val)
        sorted_lst.append(fetch_val)
        print(sorted_lst)
        lst.remove(fetch_val)
        print(lst)
    return sorted_lst
    
    
sorted_lst = select_sort(lst, False)    
print(sorted_lst)
  • 時間内の実装
  • シンプルな実装
  • 降順・昇順の切り替え
  • ランダム数値をもつ配列の作成

と初めて書いたとは思えない成果となり、自分自身、python3での考え方などアウトプットできてrubyではこうなんだよねという比較の知識も手に入り非常に勉強になった
いいね

続いて若者チーム。ありがたいことにElixirを使ってセレクトソートに挑戦してくれた
時間には間に合わなかったものの、アキュムレーターの考え方、for文ないのきちーと勉強になったようで何より
あ、コードはpaizaで実装していたため、ブラウザ更新して消えてしまった模様ww

第2セッション フィボナッチ数列の実装

フィボナッチ数列の詳しい解説はしないが、関数型言語の最初の取り組みとしてよく例題として扱われる問題だ
今回は普段はpythonをメインで書いているヤングマンNoneさんとElixirを使ってペアプロを行なった

最近Elixir欲が止まらない模様で「ぜひElixirで」ということになった。いいね
Elixirのsyntaxについても助言することもなく、以前よりフィボナッチ数列の実装を何度かやったことがあるそうでスラスラとコードを書いて頂いた
「お、そんな風にフィボナッチ数列ってかけるんけ」と非常に勉強になった

自分の頭の中にあったのはreduce使って前の値を保存しておいて、その値に次の値を足せばええやんと思っていただけにいい意味で意表を突かれた
すげー。再帰的でおしゃれや

defmodule Fibo do
    def fibo(num) do
        Enum.map(1..num,fn(n) -> sub(n) end)
        |>IO.inspect
    end
    def sub(count) when count == 1 or count == 2 do
        1
    end
    # ここがしゃれてる
    def sub(count) do
        sub(count - 1) + sub(count - 2)
    end
end
<200b>
Fibo.fibo(10)

時間が余ったので色んな情報を共有して、プログラミング以外の知識も新たに入ってきた

一方、もう1チームは常連メンバーのG_kenkunが主導となりかなり本格的にElixir使って実装を行なってくれた
再帰関数使って時間内に実装してかなり盛り上がっていました。いいね

感想

CodeReTreatくっそおもろいやんけ!!!!
準備何もしなくていいし、人数だけ揃えば気軽に開催できるし、凄い楽しかった
ペアプロしてもらったこともないので、非常に経験値もあがったので、また定期的に開催したい

写真をとっておけば、この楽しい雰囲気が伝わったのになと反省
あとはテーマの選定だけかな。難しすぎず、簡単すぎず...

おまけのコーナー

せっかくなのでセレクトソートをElixirで実装してみた
再帰関数使ったverとEnum.reduceでゴリ押したverがあるよ

defmodule Sort do
  @moduledoc """
    ## sample
    iex> Sort.execute_sort(5)
    iex> Sort.execute_reduce_sort(5)
  """
  def select_sort(lst) do
    _select_sort(lst, [])
  end
  defp _select_sort([], accum), do: accum
  defp _select_sort(lst, accum) do
    min_num = Enum.min(lst)
    removed_min_num = List.delete(lst, min_num)
    _select_sort(removed_min_num, accum ++ [min_num])
  end
  
  def reduce_select_sort(lst) do
    Enum.reduce(1..length(lst), {lst, []}, fn _, acc -> 
      {base_lst, sorted} = acc
      min_num = Enum.min(base_lst)
      next_lst = List.delete(base_lst, min_num)
      {next_lst, sorted ++ [min_num]}
    end) |> (fn {_, sorted} -> sorted end).()
  end
  
  # 動作確認のためのそれ
  def execute_sort(try_num, lst_size \\ 100, max_num \\ 100) do
    Enum.map(1..try_num, fn _ -> create_random_lst(lst_size, max_num) end)
    |> Enum.map(&(select_sort(&1)))
    |> IO.inspect()
  end
  
  def execute_reduce_sort(try_num, lst_size \\ 100, max_num \\ 100) do
    Enum.map(1..try_num, fn _ -> create_random_lst(lst_size, max_num) end)
    |> Enum.map(&(reduce_select_sort(&1)))
    |> IO.inspect()
  end
  
  defp create_random_lst(lst_size, max_num) do
    Enum.map(1..lst_size, fn _ -> :random.uniform(max_num) end)
  end
end

【レポート】第11回清流elixir勉強会を開催しました【Taskを使った並行処理に入門】

トピック

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

隔週にて勉強会を開催していたのですが、先月は仕事の都合で日程が合わずで3週間時間が空いてしまった
こういうのは一回サボる癖が付いてしまうと徐々に習慣化してしまうので気をつける様にしなければと反省
仕事って大変だ

最近は毎回参加して頂ける方、新規の参加者の方も増え、8人用のレンタルオフィスが手狭に感じるようになった
主催者側としてはより快適な場所を提供したいという気持ちもあるがこのローカル、アングラ感が気に入っていたりもする

さて今回はついにElixirの強力な並行処理について入門していくことになった

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

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

Elixirでの並行処理について話を始める前に、まず並行処理というものが何なのか、よく耳にする並列処理と何が違うのかという部分からスタートした

ざっくりと並行処理と並列処理について

技術が進化し、現代のCPUはマルチコアであることが一般的だ
ここでいうコアとはCPUの中に、CPUと同じ様な演算が行えるものが入っていると思えばいい(厳密にはCPUとイコールではない)
つまりはCPU in CPUという状態でたくさんの処理を行うことが可能になるというわけだ
よく聞くデュアルコアとは2つのコアを持つCPUで、クアッドコアというのも最近よく聞くようになった

そして我々がyoutubeを見たり、iTunesを使って音楽を聞いたりという操作をPCで行うわけだが、この際にそれぞれの仕事(task)はプロセスという単位で管理がされている
youtubeを見るためにブラウザを立ち上げることでブラウザを管理するプロセスが立ち上がる。iTunesも同様だ
このプロセスというものをどの様に扱う(動かす)かで並列処理なのか並行処理なのかが分かれる

並行処理について

1つのコア上で複数のプロセスを動作させることをいう。イメージとしては同じキッチンで切る、煮る、焼く、揚げるなどを1人が行なっていると思ってもらえれば分かりやすいのではないかと思う
あまりにも高速で動いているため、それぞれの動作が同時に行われているように見えるが、実際には高速で行う処理を切り替えている
この切り替えをコンテキストスイッチと呼ぶ(詳しくはggってね)

Elixirで並行処理を行うにはプロセス間でやり取りを行えば良い
よく取り上げられるElixirの強力な並行性というのは並行処理を指している場合が多いのではないだろうか
この部分は非常に混同しやすいので自身も気をつけている

  • process間通信(messageの送受信)
  • Task
  • Agent etc...

並列処理について

こちらは複数のコアでそれぞれ1つのプロセスを割り当てて処理することをいう。イメージとしては複数のキッチンそれぞれで1品の料理を調理すると思ってもらえれば分かりやすいのではないかと思う
性能という数の暴力とでもいえばかなり効率が良さそうだ

Elixirで並列処理を行うためには以下を使用するらしいが、私自身こちらにまだ触れていないため間違いがあれば指摘頂きたい

  • OTP: behaviourの1つ GenStage
  • 外部ライブラリ: Flow

Elixirでのプロセスの扱いについて

通常プロセスを生成する祭にはOSのAPIを使用して管理を行うが、Elixirの場合はErlang実行環境である、EVM(Erlang Virtual Machine)上で独自のプロセスを生成する(EVMで生成されるプロセスの実態はCの構造体)
このプロセスはそれぞれが個別にメモリ空間を確保しており、ヒープやスタックなどとCPUを使用する権限を持ち合わせている
そしてCPU使用の権限はプロセス毎に移り変わっていく

そうすることでCPUを独占してしまうような再帰処理を防ぐことが出来るし、メモリ空間をプロセス毎に持っているため、ガベージコレクタをプロセス単位で実行することが可能である

Taskについて

f:id:takamizawa46:20190908130053j:plain
Elixirには気軽にプロセスを生成して並行処理を行うためのTaskというモジュールが実装されている
つまりはこのTask自身もプロセスということになる(元のプロセスから生成される新たなプロセス)

最も気軽にTaskで並行処理をするための関数にTask.asyncというものがある
Task.asyncは非同期処理を行うため、実行結果を取得するためにawaitを行う
Task.async, Task.awaitの詳しい記述方法については公式ドキュメントを参照してもらうか、私が以前かいた記事を見て頂ければと思う

www.okb-shelf.work

まずはシンプルに受け取った数値に+2をする関数をTaskを使って並行処理で実行させてみる

# 無名関数の作成
iex(1)> func_ = fn num -> num + 2 end
#Function<7.91303403/1 in :erl_eval.expr/5>

# 作成した無名関数をasyncに渡す(fn -> end)の形でないとerrorになるため注意
iex(2)> task = Task.async(fn -> func_.(10) end)
%Task{
  owner: #PID<0.352.0>, # 生成元のプロセス認識子
  pid: #PID<0.2140.0>, # 生成されたのプロセス認識子
  ref: #Reference<0.3658657072.3843293185.212981>
}

# 実行結果を受け取る
iex(3)> Task.await(task)
12

# おまけ: 生成元のプロセス認識子
iex(4)> self()
#PID<0.352.0>

次に同じ処理を100個のプロセスを生成して実行させてみる
パイプ演算子を使って一気にawaitまでを実行する

iex(1)> res = Enum.map(0..100, fn num -> Task.async(fn -> num + 2 end) end) |> Enum.map(fn p -> Task.await(p) end)
[2, 3, 4, 5, 6, 7, 8, 9, 10,
 11, 12, 13, 14, 15, 16, 17,
 18, 19, 20, 21, 22, 23, 24,
 25, 26, 27, 28, 29, 30, 31,
 32, 33, 34, 35, 36, 37, 38,
 39, 40, 41, 42, 43, 44, 45,
 46, 47, 48, 49, 50, 51, ...]

# おまけ
iex(2)> Enum.sum(res)
5252

iex(3)> accurate = Enum.sum(2..102)
5252

iex(4)> Enum.sum(res) == accurate
true

これで基礎はバッチリ抑えた。次にAPIを並行処理でcallしてみる

mixプロジェクトの立ち上げと関数の用意

callするAPIはおなじみのジブリAPI
httpクライアントの外部ライブラリであるpoisonを使用したいためmixのプロジェクトを立ち上げる

$ mix new ghibli_app
$ cd ghibli_app

次に必要な外部ライブラリをmix.exsのdepsに記述する

defp deps do
    [
        {:httpoison, "~> 1.5"}
    ]
  end

もし使用しているElixirのversionが1.3もしくはそれ以下の場合にはmix.exsのapplicationに以下を追加

def application do
  [applications: [:httpoison]]
end

準備が整ったので、外部ファイルをダウンロードする

$ mix deps.get
Resolving Hex dependencies...
Dependency resolution completed:
New:
  certifi 2.5.1
  hackney 1.15.1
  httpoison 1.5.1
  idna 6.0.0
  metrics 1.0.1
  mimerl 1.2.0
  parse_trans 3.3.0
  ssl_verify_fun 1.1.4
  unicode_util_compat 0.4.1
* Getting httpoison (Hex package)
:
:
* Getting parse_trans (Hex package)

無事にダウンロードされたようだ。この時点でmix deps.compileというコマンドを使用して自身でcompileを行なってもいいが、次回のiexプロンプトの立ち上げ時に自動でcompileが実行されるため、行わなくても良い

一度、iexプロンプトを起動して./lib/ghibli_app.ex に標準で記述されているhello関数を実行してみる

$ iex -S mix
Erlang/OTP 21 [erts-10.2.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

===> Compiling parse_trans
===> Compiling mimerl
:
:
Generated test app

自動でコンパイルが実行される
さっそくhello関数を呼び出してみよう

iex(1)> GhibliApp.hello
:world

上手く動作されることが確認できたので./lib/ghibli_app.exにAPIをcallする関数と並行処理を行う関数を記述していく
今回使用するAPIのエンドポイントは以下だ。GETメゾットで脳死で使用できるものを選択した

https://ghibliapi.herokuapp.com/films

defmodule GhibliApp do
 @moduledoc """
 Documentation for GhibliApp.
 """
 @doc """
 Hello world.
 ## Examples
     iex> GhibliApp.hello()
     :world
 """

 # APIをcallする関数
 def call_ghibli_api do
   HTTPoison.get!("https://ghibliapi.herokuapp.com/films")
 end

 # 受け取った指定数のプロセスを実行してAPIをcallする
 def parallel_exec(total_process) do
   Enum.map(1..total_process, fn _num ->
     Task.async(fn -> call_ghibli_api() end)
   end)
   |> Enum.map(fn p -> Task.await(p) end)
   |> Enum.map(fn data -> data.status_code end) # 戻り値の構造体からstatus_codeを抽出(データ量が多く可読性のため追加)
 end
end

ファイルに編集を行なったので立ち上がっているiexプロンプトに再度compileをするようにコマンドを記述する

iex(2)> recompile
Compiling 1 file (.ex)
:ok

続いて、メインの処理(parallel_exec)を実行するが、大量のプロセスを生成して並行でcallするとサーバーに迷惑となるので常識ある数にする
今回は適当にジョジョのアニメ5部完結記念に4を選択した

iex(3)> GhibliApp.parallel_exec(4)
[200, 200, 200, 200]

いいね
非同期処理でDBにinsertしたり、複数ファイルをstreamで読み込んで編集したりと使い方を考えるだけでワクワクする

次回の勉強会について

特に希望がなければOTPのbehaviorの1つであるGenServerを使って、stateを持つサーバーを実装してみようと思う
皆さん、お気づきかと思うが勉強会のテーマにしているものはこのブログで自身が学んだものをアウトプットするような形で選択している

開催日は9月20日(金) 19:30 ~ 21:30を予定している

参考文献

【失敗しないプレゼン資料】プレゼン資料のアンチパターンとプレゼンに対する考え方について

なんだこの記事

f:id:takamizawa46:20190831143333p:plain
自分は大学生時代に研究室で先生から資料の作り方の極意たるものを叩き込まれた
専門分野の知識よりも、資料の作り方を意識することに大半の時間を費やしたと思う
(卒論のプレゼン資料に枚数制限(上限8枚, 他研究室は平均20枚ぐらい)とフォントサイズ制限があったりなど)

当初はなぜ自分の資料が良くないものなのかということを客観的に判断することが出来なかったが
今になって先生が仰っていたことが「こういうことだったのか」と完全に理解し始めた

こういう経緯があってLT・プレゼン資料に関しては、パッと見れば、ある程度ツッコめる人間になった

そして最近、LT・プレゼンをやらせてもらったり登壇させて頂く機会が多く、1週間まるまる資料を作るなんてことがよくあった
(markdownpowerpointだけを利用する1週間と成り果てる...トホホ...)

その中で元々、社内で用意されているプレゼン・LT資料を改めて意識して閲覧したのだが「何だこの資料は...」と感じる点がいくつもあった
自身の経験を元に良くない資料のパターンと資料作成の基本的な考え方についてまとめようと思い立ったわけなのです

良くない資料の典型例

以降、「コンテンツ」というのはあるページ内でのスライドの内容のことを指している(eg: 1ページ目の内容)

タイトル詐欺

ページのコンテンツとタイトルがまったく一致しないことが良くある
ページタイトルで「〇〇について」と記述しているのにコンテンツを見てみると
全く〇〇について触れておらず、単純に情報量過多となっていて収集がつかなくなっている事が多い

大半の原因はタイトルを先に決めてから、内容を作り込むため、当初の予定とコンテンツの内容に整合性がなくなってくるためだ
コンテンツを作り終えた時に「ページタイトルと内容がマッチしているか」と振り返る癖をつけないといけない

フォント問題

小さい。14px以下の文字なんて後ろの席からは見えないし、意識して読まない
補足で必要な情報があれば別途に資料を用意して会場で配布すれば良い

フォントは最低でも20px以上を意識した方が良い
あとは良く分からないフリーのフォントを使いたがる人が多いが、自己満足なのでやめたほうが良い
搭載されているデフォルトのフォントで十分

見た目やデザインを気にする前に、読んでもらえない資料になっていないかを確認するべきだ
美しさを気にするのは最後に余裕がある時だけにしたい

読むだけの資料

LT・プレゼンをする意味のない資料が非常に多い
「え、そこに全部書いてあるじゃん」と思われていることに気づいた方が良い

ただ読むだけの資料なら配布して終わりで良い。自分の好きな時間に好きなペースで読める
LT・プレゼンをすることでその場でしか触れられない付加価値を資料に付け足せないのなら
コンテンツに含めすぎている情報量を見直して、当日これは話そうかなとざっくりと計画を練る

不必要に情報を詰め込まないことは非常に重要で情報の取捨選択について考える癖がつく

導線が見えない

コンテンツに統一感がなく、パッと見た時にどの順番で話せばいいのか分からないような構図の資料が多い
これはすなわち、資料を見る側も同じ思いをするということになる

  • 矢印を使って、流れを見える化しておく
  • 説明したいと思っていることは下線や色文字にしておく
  • 上から下に情報が流れるようにしておく

など思いつく対策法はいくらでもある
当日の自分が「どこからだっけ」と困らない様にしておく

カラフルすぎる

デザイナーが作成した資料に多い
中身の情報量がカッスカッスなのに対して、やたらと凝っていたりする

全体の文字色のベースがパステルカラーであったりと見た目は映えるが、LT・プレゼンは
スクリーンに映し出す場合が多いため、薄い文字色は見えなくなる

色文字を使う時にはそれぞれの色に役割を持たせると良い

例:

  • 赤色 -> 説明すべき用語
  • 青色 -> 自分の意見や考察
  • 緑色 -> リスナーへの投げかけ

など、色に役割を持たせておくと自身の資料を作る際にも楽だし、早く作れる様になる
黄色は見づらいのでおすすめしない

意味のない画像やアニメーション

若い方や学生に多い。資料をインパクトあるものにしようとして一生懸命アニメーションやおもしろ画像が練りこまれている
画像は単純に情報量が多くなるだけなのでほどほどに。リスナー視点で本当に必要なのかを考えるべきだ

アニメーションを作り込みすぎると当日の自分が困る
全過程を記憶しているなら構わないが、当日リラックスしてLT・プレゼンをしたいので当日に自分が緊張状態かつ
頭真っ白になった状態で把握できないようなアニメーションは作るべきではない

質問時間の時にもスライドを戻るのにアニメーションが作り込まれていると面倒だ

良い資料を作るために心がけていること

ここまでよくある資料の典型例を見てみた
このような例にならないように普段、資料作成の際に留意している点をいくつか紹介する
上記の典型例がなぜダメなのかということがこの留意点から伝わればと思う

当日に記憶喪失になっても良いように

一番、大切にしていることはこれだ
「こいつバカか?」と思うだろうが、その通りだ

この言葉の意味することは、当日に緊張してガチガチになってしまう状態であっても
どの順にどの情報をどの層に説明するかということを資料をパッとLT・プレゼン時に見ただけで思い出せるよう・分かるようににしておくということにある

そうすることで極論、プレゼンの練習が不要になるし、頭に何も詰め込まなくて良い

この考え方をベースに資料を作るとすれば

  • タイトル詐欺
  • 導線が見えない
  • カラフルすぎる
  • 意味のない画像やアニメーション

といったことは自然としなくなるだろう

話を聞いてもらえないことが最悪

LT・プレゼンにおいて最も最悪なケースというのは何だろうか
もちろん「話を聞いてもらえない」ことだ

自己満足の資料を作って話しても聞いてもらえなければ意味がない
いかにユーザー視点で情報が明確な資料を用意するかということでユーザーの興味を惹きつけることが出来る
ただ、ジャパネットの高田さんのような話術のスペシャリストもいるので例外もある

ただ、僕の様な一般人が興味を持ってもらうために用意できるシンプルなことは「資料を明確化する」こと
そうすると

  • フォント問題
  • 読むだけの資料
  • 導線が見えない
  • カラフルすぎる

こういうことはするべきではないだろう

まとめと後語り

今回はあまりにも身近にある資料の内容がひどすぎたので自身の考え方をまとめてみた
最もベースにある考え方は2つで

  • 当日、記憶喪失になっても良い様に作っておく
  • 話を聞いてもらえないことが最悪と知る

ということ
そうすれば、自然とアンチパターンを避けていくことが出来る

ただ自分の作った資料には愛着が湧いてしまうので批判的に見ることは難しい
この点に関しては訓練が必要で、何度もリスナー視点で、何も知らない人の視点で資料を見返していると
「おや?」と思う様になってくるだろう

資料は作りゃあいいってもんじゃないということを留意したい

大学生の時に教えて頂いた事がこんなに役に立つとは思わなかった
改めてお礼をさせて頂きたいと思います