Software Engineer Blog

エンジニアブログです。技術情報(Go/Java/Docker)や趣味の話も書くかもです。

React.js 学習 (Soft Skillsフォーマット)

概要

React.jsに入門してみました。その際に、 Soft Skillsで言及されていたフォーマットに沿って学習しました

Soft Skills

SOFT SKILLS ソフトウェア開発者の人生マニュアル

SOFT SKILLS ソフトウェア開発者の人生マニュアル

書籍内にて、学習のフォーマットが提示されていたので、そのとおりにやってみています。
番号が振ってある部分が、フォーマットになります。

フォーマット

1. 日付

2018/08/27 ~ 2018/09/12 (=> 伸びて 2018/10/09になりました)

2. スコープを調べる

調査

メモ

  • viewの状態を動的に変えれる
  • 親と子のコンポーネントがあって、うけわたしする
  • 状態遷移があって、どの状態で何をするかを考える必要がありそう
  • Facebook
  • 入力した値を即座に反映したいときに便利
  • 仮想domは速いらしい
  • reactiveプログラミング

3. スコープを決める

  • React.jsを利用したシングルページアプリを作成
  • TypeScript+React.jsの構築/コンパイルができる
  • アプリを外部公開できる

4. 成功の基準

React.jsを利用した、シングルページアプリを公開してブログを書く

5. 参考資料を見つける

(できる限りたくさん見つけるのが良い模様)

Essence

  • Declarative
  • component base
    • render()Componentを記載
    • this.propsでpropertyにアクセス
  • Virtual Domは高速
    • Virtual Dom全更新→差分計算→Domへの全適用
    • Domの全更新より早い
  • props
    • 外部とのInteface
    • Immutableに扱う必要がある
  • state
    • Component内部の状態を扱う変数

6. 学習プラン作成

step title Goal describe
1 TS + React環境構築 Hello World !表示 - tsコンパイルができる環境作成
- webpackを利用する
2 公式チュートリアル実施 チュートリアル終了 - 公式チュートリアルをそのまま実施する
- ただし、TypeScript対応はする
3 シングルページ作成 ✅ローカルでページが見れる - 作成物は要検討
- 掲示
4 公開 ✅特定のIP/ドメインでリクエストしてページが見れる - Nowを利用して公開
5 このページをブログ公開 ブログとして公開される

7. リソースを絞り込む

8. 使い始められるようにする方法を学ぶ

環境構築

mkdir -p dist src/components
npm init

# react domと type packageを取得
npm install --save react react-dom @types/react @types/react-dom

# typescript loader(tsconfig.json) + sourcmap作成(debug時にtsから見れるようになる)
npm install --save-dev typescript awesome-typescript-loader source-map-loader

その他

# globalにinstallしたpackage list
npm ls -g

# webpack
npm install -g webpack
# webpack-cli or webpack-commandのどちらかが必要
npm install -g webpack-command

# --save-dev(-D) => 開発時のみ利用のオプション

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es5",
    "jsx": "react"
  },
  "include": [
    "./src/**/*"
  ]
}

webpack.config.js

module.exports = {
  entry: "./src/index.tsx",
  output: {
    filename: "bundle.js",
    path: __dirname + "/dist"
  },
  devtool: "source-map",
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".json"]
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: "awesome-typescript-loader"},
      { enforce: "pre", test: /\.js$/, loader: "source-map-loader"}
    ]
  },
  externals: {
    "react": "React",
    "react-dom": "ReactDOM"
  }
}

.gitignore

node_modules
dist

index.ts

import * as React from 'react'
import * as ReactDOM from 'react-dom'

ReactDOM.render(undefined, document.getElementById('root'))

HTML

<body>
  <div id="root"></div>
  
  <!-- Dependencies -->
  <!-- https://reactjs.org/docs/getting-started.html#development-vs.-production-builds -->
  <script src="./node_modules/react/umd/react.development.js"></script>
  <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
  <!-- Main -->
  <script src="./dist/bundle.js"></script>
</body>

tslint + perettier

npm install -D tslint prettier tslint-config-prettier tslint-plugin-prettier tslint-config-standard

tslint.json

{
  "rulesDirectory": ["tslint-plugin-prettier"],
  "extends": ["tslint-config-standard", "tslint-config-prettier"],
  "rules": {
    "prettier": [
      true, {
        "singleQuote": true,
        "semi": false
      }
    ]
  }
}

テスト環境構築

# jest
npm install -D jest babel-jest babel-preset-env babel-preset-react react-test-renderer ts-jest
# 型ファイル
npm install -D @types/react-test-renderer @types/jest

# package.json
"scripts": {
  "test": "jest"
},
"jest": {
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js"
    ],
    "testMatch": [
      "**/__tests__/*.+(ts|tsx|js)"
    ]
}

9. 遊び回る

チュートリアル実施

  • JavaScriptではclassのconstructorで、super(props)を最初に呼ぶ必要がある
  • 子Component同士でやりとりするのではなく、親Componentを介してstateを共有する
  • immutable
    • Complex Features Become Simple
    • Detecting Changes
    • Determining When to Re-render in React
  • It’s strongly recommended that you assign proper keys whenever you build dynamic lists
  • チュートリアル完了
    • +α で押されたボタンをハイライトする機能を追加

10. 役に立つことができるところまで学ぶ

掲示板作成

- ソースコード

github.com

  • 機能
    • 入力した文字をそのまま下に表示
    • clear

Nowへデプロイ

  • expressでサーバーを立てる
    • index.js
  • webpack/webpack-commandをdevdependenciesに追加
    • サーバー上でbuildする際にwebpackが必要になるため
npm install -D webpack webpack-command
  • package.jsonにbuildとstartを追加
{
   "scripts": {
       "build": "./node_modules/.bin/webpack",
        "start": "node index.js"
   }
}

11. 教える

ブログ書く

所感

(後日記載)

AWSコンソールからShellを実行できる機能を試してみた (AWS Systems Manager Session Manager for Shell Access)

概要

AWSGCPのCloud Shell的な機能が出たとあり、ちょっと面白そうなので触ってみました。

aws.amazon.com

やったこと

  • ログインIAMユーザーに AmazonSSMFullAccess ポリシーをアタッチ
  • AmazonEC2RoleforSSM ポリシーをアタッチしたロールを作成
  • 作成したロールをアタッチしたEC2を作成
  • AWS Systems Manager > Run Command から AWS-UpdateSSMAgent を実行してssm agentをアップデート
  • AWS Systems Manager > Session Manger から起動

スクリーンショット

起動画面 (200%拡大)

f:id:midori5:20180912231128p:plain

デフォルト

試して見た感想

普通にコンソールとして動作する印象です。

  • 起動は高速で待たされることなく画面がすぐ開く
  • 動作はけっこう重め
  • Ctrl + xxx のコマンドは利用可能
  • マウススクロールで画面スクロールができる
  • vi 開ける

所感

GCPのCloud Shellが便利で、AWSにもこういった機能が欲しかったので待望のリリースでした。
ただ立ち上げてみただけですが、sshポートを開けることなく接続できて、セキュアだと思うので、
動作が軽くなれば、こちらを積極利用していけると良さそうです。

参考

builderscon tokyo 2018に参加してきました

概要

ブログを書くまでが以下略 のため、ブログ書きます。

はじめに(スタッフの方々)

準備/当日運営含めて、大変ありがとうございました + お疲れ様でした。はじめての参加でしたが、大変楽しかったです。 テーマになっていた「知らなかったを聞く」に満たされた、2日間でした。
普段、ごった煮のイベントはあまり行かないので、他の分野についての知見が広まりました。
来年も是非参加させていただきたいです。

良かった点/気になった点だけ記載しておきます。(Feedbackの意味も込めて)

良かった点

  • 運営が基本的にとてもスムーズ
    • 変な引っかかり等なく、トークを聞くことに集中できました
  • 翻訳の用意がありがたかった
  • (注意説明が、ムービーになっており面白かった)

気になった点

  • 会場のキャパ問題
    • 今回は? 人が多かったからなのか満席/立ち見が多かった気がします
    • また、ランチセッションも早く並べた人限定という部分が少し気になりました。(ちなみに2日ともいただきました。)
  • Webページのタイムテーブルで会場名がヘッダー固定ではなかった点
    • 場所が何度かわからなくなりました..
  • スライドが見づらいことが多かった
    • 光の問題やスライド自体の問題?等で、文字が見えないことが何度かありました..
    • ただ、事前に共有いただけていたので手元で見ながら聞けました

トーク一覧

下記のGistに各トークのメモ書きを記載しております。
(諸事情により、しっかりメモを取っています。)
https://gist.github.com/midorigreen/7409399469c39eeb0f96214d33b76eae

TimeTable

1日目

Envoy internals deep dive

Matt Klein@mattklein123 さんが直々に、Envoyの設計についてお話されていました。 所々、わからなくなっていきましたが、「観測可能性」と「ロックを起こさない設計」の2点について印象に残っています。 プログラムの設計をする際に見返したいスライドだと思いました。

ランチセッション (サイボウズ株式会社): Kubernetes で実現するインフラ自動構築パイプライン

日毎に、VPCから作り直している話は、以前一度聞いたことがありましたが、驚きです。

Algorithms in React

Reactほとんど書いたことないのですが、内部実装が気になったので聞きました。 LinkedList + byteをうまく利用して、高速化を図っている部分が印象的です。

事前知識なしで理解する、静的検査のいろは

早足で進むのがもったいないくらい、まとまってて勉強になる内容でした。あと、スライドが好みで真似したくなりました。
静的検査の教科書的な網羅性で、やりたくなったら読み返すと思います。

機械学習を用いず数学でゲーム内の需要予測をする

数学ががっつり?というほどではないですが、出てきて講義感があって面白かったです。 分析に携わる仕事をしていることもあり、分析の原則について知れてよかったです。(知らなかったのがまずそうです..)

JavaCardの世界

2日間の中での個人的ベストトークでした。(※ 実際のベストトークもこちらでした!!)
Java Cardという全く知らない世界の、めったに外に出てこない知見が山盛りでした。
また、発表されていた方のプレゼンスキルが高く感じました。
特に、このあたりが気になりました。

  • 各仕様を正確に理解した上での、網羅的な説明
  • 質問への正確の返答のための聞き返し
  • (随所で笑いを生み出す力..)

あそこまで、ある技術に対して細かい点まで抑えられている事自体がすごいと感じましたし、詳しい分野を作りたいなと思いました。
(ここまでニッチではなくても良いですが...)

lld − 開発ツールの主要コンポーネントの1つをスクラッチから作成した話

tcfmの @rui314 さんの lld に関する発表でした。 実際に作られたlldに関しても面白い話でしたが、「勇気を持って自分が正しいと思う設計を突き通す」というお話がとても印象的です。 あと、速度を上げたければ データ構造 をうまく設計する必要があるとおっしゃっていた点も勉強になりました。

Conference Dinner

何人かの方々とお話できました!! (珍しい)

2日目

全てのエンジニアに知ってもらいたいOSの中身について

OSの中身? ≒ CPUのお話でした。CPUの歴史的背景がゴロゴロ出てきて、面白かったです。

高集積コンテナホスティングにおけるボトルネックとその解法

Linuxボトルネックをガツガツ潰していくお話でした。手になじませるツールをいくつか持っておくという部分が印象的です。 Linux系 + Go系でツールをゴリゴリ使えるようになりたいです。

ランチセッション (株式会社VOYAGE GROUP)

AjitoFmの公録が聴けて、純粋に楽しかったです。普段Podcastで聞いている声が目の前から聞こえてくる、不思議な感覚でした。

ブログサービスのHTTPS化を支えたAWSで作るピタゴラスイッチ

聞いてきた中で、最も仕事感のある内容で、ちょっと胃が痛くなりそうでした。高要求に対して、AWSのツールを活用して、 要件を満たしていました。その結果、ユーザに対して素晴らしい機能を提供できており、こういったことができる職業エンジニアになりたいと思いました。

業務時間で書いたパッチは誰のもの? OSS 活動にまつわる罠

とても珍しい、会社のOSS Policyに対するお話でした。基本的には、各社員が自由にやれるとい方向に倒れており、すごいなと思いました。

Building Self-Hosted Kubernetes

k8sをself hostするお話です。self host自体がよくわかっていなかったので、そこの知識を埋められてよかったです。 実際に、self hostを動かしてみてどうだったかとのお話がまた聞きたいです。

Extending Kubernetes with Custom Resources and Operator Frameworks

全セッションの中で最も難しかったです。(途中からさっぱりわからなくなっていきました..)
k8s周りについての理解が浅かった部分が大きかったです。ただ、k8sの設計思想(あのロゴの意味)等が知れました。

Lightning Talks

なんでもありのトークで、LTっぽくて楽しかったです。 下記が印象に残っています。

  • Macがいやだからブラウザで動くエディタを作成(https://nedi.app/)
    • めちゃめちゃ速くてびっくりしました
  • 画像加工 (kaoriyaさん)
    • 衝撃でした
  • カオスエンジニアリングの文脈でネットワークの検証
  • ファミコンエミュレータでBest Practiceを目指す
  • 登壇するための背中を押せていただける話

感想

とにかく、面白いイベントでした。もともと、他の分野の話を聞いて知見を広めたいとの目的で参加したので、完全に達成された気でいます。 あとは、聞くだけではなくて手を動かしていくことが大切だと思うので、ゴリゴリコード書いていきます。 (発表もできたらいいなぁともちょっと思っています。)

Go-Cloudで利用されているDIツールWireについて

概要

Go-Cloud Projectで利用されている、 Wire と呼ばれる
Dependency Injection Tool について触ってみました。
(README.md を試した形です)

Install

github.com

wire は go get でインストールします。

go get github.com/google/go-cloud/wire/cmd/wire

実装

github.com

基本的な使い方

ProviderとInjecterという2つの概念を持ちます

Provider

Providerは、依存させたいstructの実体を返却します。

例)
依存させたいstructの実態を定義します。

// foo.go
type Foo struct{
    Name string
}

Provider関数を定義します。

func ProviderFoo(name string) Foo {
    return Foo{
        Name: name,
    }
}

Providerの利用元も実装します。
(今回は1つですが) ProviderSetを定義することで、複数のProviderをまとめて処理します。
main.go

// ProviderSet
var SuperSet = wire.NewSet(ProviderFoo)

func main() {
    // flagで名前を切り替えられるようにしてみます
    n := flag.String("n", "foo", "foo name")
    flag.Parse()

    foo, err := setUp(context.Background(), *n)
    if err != nil {
        log.Fatalln(err)
    }

    // fooの名前を出力
    fmt.Println(foo.Name)
}

Injector

Injectorは、Provider関数を利用して実体を実装へinjectします。
injectorのコードは仮実装を元に、wire が生成してくれます。

例)
Injectorの仮実装を定義します。
コード生成の元コードのため、buildタグを付与して build時に利用されないようにします。 injector.go

// buildタグを付与
//+build wireinject

// 実装
func setUp(ctx context.Context, name string) (Foo, error) {
    // ProviderをBuildする
    wire.Build(SuperSet)
    // 特に意味はない
    return Foo{}, nil
}

ここで、injectorの実装を生成していきます。 下記コマンドを実行します。
(go getで入れているので、バイナリが$GOPATH/bin配下に配置されてます。)

% wire

wire コマンドを実行すると、下記のような wire_gen.go が生成されます。
参考) wire_gen.go

// Code generated by Wire. DO NOT EDIT.

//go:generate wire
//+build !wireinject

package main

import (
        context "context"
)

// Injectors from inject.go:

func setUp(ctx context.Context, n string) (Foo, error) {
        foo := ProviderFoo(n)
        return foo, nil
}

実行

wire_gen.go が生成された段階で、buildして実行してみます。

% go build -o wire-sample

(補足)
buildタグで、!wireinject となっているため go build 時は、 injector.goの代わりにwire_gen.go が利用されます。

実行

% ./wire-sample
foo

# ちゃんとフラグで渡した値が利用されている
% ./wire-sample -n bar
bar

ちなみに、一度 wire_gen.go を生成したあとで、修正したい場合は、 go generate を利用します。

# 再ビルド
% go generate && go build -o wire-sample

Tips

① 独自型を定義してProviderへ渡す

Providerに、一般的な型(string, int, etc..)を利用すると、コードを生成する際に一意に定まらず、期待しない動作になる可能性があります。
そのため、独自型で定義してsetUpの引数に渡してあげることで、確実に狙ったProviderの引数に渡すことができます。

例)
injectしたいstruct

type Foo struct {
    Name: string
}

type Hoge struct {
    Hoge: string
}

type FooHoge struct {
    Foo Foo
    Hoge Hoge
}

providerの実装

// 型定義
type fooName string

func ProviderFoo(name fooName) Foo {
    return Foo{
        Name: string(name),
    }
}

func ProviderHoge(name string) Hoge {
    return Hoge{
        Name: name,
    }
}

func ProviderFooHoge(foo Foo, hoge Hoge) FooHoge {
    return FooHoge{
        Foo: foo,
        Hoge: hoge,
    }
}
var SuperSet = wire.NewSet(ProviderFoo, ProviderHoge, ProviderFooHoge)

func main() {
    fh, _ := setUp(ctx, "foo", "hoge")
    fmt.Println(fh.Foo.Name)
    fmt.Println(fh.Hoge.Name)
}

injector.go

func setUp(ctx context.Context, fn fooName , n string) (FooHoge, error) {
    wire.Build(SuperSet)
    return Foo{}, nil
}

↓ go generate

// generateした際に、stringのままだとFoo/Hogeの
// どちらの名前かわからなくなってしまう
func setUp(ctx context.Context, fn fooName , n string) (FooHoge, error) {
    foo := ProviderFoo(fn)
    hoge := ProviderHoge(fn)
    fooHoge := ProviderFooHoge(foo, hoge)
    reutnr fooHoge, nil
}

所感

GoでDIする際に、あまりツールを利用できていなかったので、 利用が増えてくれば、実コードに入れるのもありかと思いました。 DIに関しては、 Guice を一度触れていたので、比較的すんなり入ってきました。 テスタビリティがきちんとあがっているかは、テスト書いてみて確認したいです。

参考

実践 Python データサイエンス受講 (No.1)

概要

【世界で5万人が受講】実践 Python データサイエンス | Udemy
をぼちぼち受講していっています。
序盤のあたりまで、受講したので内容についてまとめておきます。
データサイエンス周りの基礎知識をつけることがゴールかなと思っています。

内容

はじめに

基本的にはJupyter Notebook上にサンプルコードを書いて実装しています。
data-kaggle/learn at master · midorigreen/data-kaggle

Jupyter Notebook

(記載するまでもないですが)

  • ブラウザ上で、プログラムを実行できるツール
  • プログラムだけでなく、グラフの可視化やMarkdownの記載等を利用して、実行した処理を記録
  • Machine Learningにてデファクトで利用されているツール
  • IPython(Pythonのinteractive shell)をベースに、様々な言語(40程度)に対応
  • ipynb拡張子で保存され、GitHub上で見ることも可能

numpy

Pythonの行列計算用のライブラリで、作成・計算をはじめ様々な機能を持っています。

import

import numpy as np

行列の作成

# array([0, 1, 2, 3, 4])
arr = np.array([0, 1, 2, 3, 4])
arr = np.arange(5)
# ランダム
arr = np.randam.randn(5)

# array([[0, 1, 2, 3, 4])
#         [0, 1, 2, 3, 4]])
arr2 = np.array([0, 1, 2, 3, 4],[0, 1, 2, 3, 4])

# array([[0, 1, 2],
#        [3, 4, 5],
#        [6, 7, 8]])
arr3 = np.arange(9).reshape((3, 3))

行列の操作

# (行, 列)
arr.shape

# コピー
arr_copy = arr.copy()

# 転置
arr3.T

# 行列入れ替え
arr3.swapaxes((0, 1))

# 条件式 (条件, trueのとき, falseのとき)
np.where(a > 0, 0, arr)

行列の計算

# 平方根
np.sqrt(arr3)

# 掛け算
arr1 * arr2

# 内積
np.dot(arr1, arr2)

# 平均
arr.mean()

# 標準偏差
arr.std()

# 分散
arr.var()

# ソート
arr.sort()

# 逆順
arr[::-1]

# 重複
np.unique(arr)

# contains
np.in1d([0, 10, 20], arr)

行列の外部処理

# 保存
np.save('my_arr', arr)

# 読み込み
np.load('my_arr.npy')

# zip保存
np.savez('my_arr2', x=arr, y=arr2)
zarr = np.load('my_arr2')
# 添字アクセス
zarr['x']
zarr['y']

matplotlib

  • Pythonの2Dのプロットライブラリ
  • グラフを描画することができる

Import

import matplotlib.pyplot as plt

pandas

  • データ構造と演算を提供するPythonのライブラリ

import

import pandas as pd

Series

ラベル付きの配列(1次元)

# 生成
se = Series([0, 1, 2, 3])

# ラベル付き
se1 = Series([0, 1, 2, 3], index=['A', 'B', 'C', 'D'])
se1['A']

# 辞書型変換
se1.to_dict()

DataFrame

ラベル付きの行列(2次元)

# 生成
# Clipboardから(Wikipedia等より表形式をコピー)
df = pd.read_clipboard()
df2 = {'City': ['LA', 'SF', 'NYC'], 'Population':[30000, 20000, 8000]}

環境

# Anaconda利用
% python -V
Python 3.5.2 :: Anaconda 4.1.1 (x86_64)

所感

まずは序盤として、Jupyterの利用方法から、numpyの基本までを記載しました。感想としては、行列の扱いが簡単にできて、Pythonが利用される理由の一端に触れた気がしました。pandasについては、もう少しあるので編集する形にしようかと思います。

参考

Go実装の最適化ゲームをしてみた

概要

同僚が「このコード書ける Java のライブラリない?」 と言ってきたので、
Go で実装し返しました。(遊び)
書いたコードがひどそうだったので、最適化をするゲームをしてみました。
結論、あまりいい感じではないですね。

お題

配列が与えられた際に、 指定した最小/最大サイズの文字列を前から順に結合して取得したい とのことでした。(何言ってるかわからない)

例を挙げるとわかりやすいかと思います。

input: [a, b, c, d, e]  
=> output: [ab, abc, abcd, abcde, bc, bcd, bcde, cd, cde, de]  

実装

パート1

まず、最初に思いついて書いたコードが下記になります。
なんかこう色々まずそうです。
Go Playground

func initialShingle(min, max int, arr []string) []string {
    res := []string{}
    if min > max {
        return res
    }

    for i := 0; i < len(arr); i++ {
        cmin := min
        for j := i; j+cmin < len(arr)+1; j++ {
            if len(arr[i:j+cmin]) > max {
                break
            }
            var s string
            for _, v := range arr[i : j+cmin] {
                s += v
            }
            res = append(res, s)
        }
    }
    return res
}

閑話休題

とりあえず、動作確認のためテストコードを書きます。
min/maxの値の境界値等を Table Drive Test で記載しました。 https://github.com/midorigreen/shingle/blob/master/main_test.go#L35

var cases = []struct {
    in  in
    out []string
}{
    {
        in:  in{min: 2, max: 1, arr: []string{"a", "b", "c", "d", "e"}},
        out: []string{},
    },
    {
        in:  in{min: 2, max: 2, arr: []string{"a", "b", "c", "d", "e"}},
        out: []string{"ab", "bc", "cd", "de"},
    },
    {
        in:  in{min: 2, max: 3, arr: []string{"a", "b", "c", "d", "e"}},
        out: []string{"ab", "abc", "bc", "bcd", "cd", "cde", "de"},
    },
    {
        in:  in{min: 2, max: 5, arr: []string{"a", "b", "c", "d", "e"}},
        out: []string{"ab", "abc", "abcd", "abcde", "bc", "bcd", "bcde", "cd", "cde", "de"},
    },
    {
        in:  in{min: 2, max: 100, arr: []string{"a", "b", "c", "d", "e"}},
        out: []string{"ab", "abc", "abcd", "abcde", "bc", "bcd", "bcde", "cd", "cde", "de"},
    },
}

func TestInitialShingle(t *testing.T) {
    for _, c := range cases {
        res := initialShingle(c.in.min, c.in.max, c.in.arr)
        equal(t, res, c.out)
    }
}

ベンチマークも書いたので、この結果が元になります。

% go test -benchmem -run=^$ github.com/midorigreen/shingle -bench ^BenchmarkInitialShingle$

goos: darwin
goarch: amd64
pkg: github.com/midorigreen/shingle
BenchmarkInitialShingle-4              3         388369200 ns/op        354791952 B/op   4621681 allocs/op
PASS
ok      github.com/midorigreen/shingle  1.513s

パート2

alloc回数を減らすために、返却値の初期化を1回にします。
Go Playground

func shingle2(min, max int, arr []string) []string {
    // 省略

    // for alloc array
    maxLen := len(arr)
    resLen := 0
    for maxLen > 0 {
        resLen += maxLen
        maxLen--
    }
    res := make([]string, resLen)

    // 省略
}

ベンチマーク結果

% go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/midorigreen/shingle
BenchmarkInitialShingle-4              3         364788315 ns/op        354790757 B/op   4621679 allocs/op
BenchmarkShingle2-4                    3         484458838 ns/op        355455082 B/op   4621651 allocs/op
PASS
ok      github.com/midorigreen/shingle  4.093s

悪くなってますね..

パート3

slice->stringの処理で文字列結合をしている部分を修正しました。
strings.Join([]string, string) を使ってます。
Go Playground

func shingle3(min, max int, arr []string) []string {
    // 省略

            res = append(res, strings.Join(arr[i:j+cmin], ""))

    // 省略
}

ベンチマーク

% go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/midorigreen/shingle
BenchmarkInitialShingle-4              5         330830989 ns/op        354791379 B/op   4621680 allocs/op
BenchmarkShingle2-4                    5         332415947 ns/op        355455014 B/op   4621651 allocs/op
BenchmarkShingle3-4                   20          59744819 ns/op        27479328 B/op     186132 allocs/op
PASS
ok      github.com/midorigreen/shingle  7.958s

速度もalloc回数も改善されました。

(補足) strings.Join()関数

実際の実装を見ると、以下の形になってます。

  • len(array)<=3 までは+で結合している
  • それ以降は、まずスライス長を確保している
    • 結合するのではなくstringをbyte配列にcopyしている
  • copyはstring->[]byteへコピーできる模様 doc
(As a special case, it also will copy bytes from a string to a slice of bytes.) 
// Join concatenates the elements of a to create a single string. The separator string
// sep is placed between elements in the resulting string.
func Join(a []string, sep string) string {
    switch len(a) {
    case 0:
        return ""
    case 1:
        return a[0]
    case 2:
        // Special case for common small values.
        // Remove if golang.org/issue/6714 is fixed
        return a[0] + sep + a[1]
    case 3:
        // Special case for common small values.
        // Remove if golang.org/issue/6714 is fixed
        return a[0] + sep + a[1] + sep + a[2]
    }
    n := len(sep) * (len(a) - 1)
    for i := 0; i < len(a); i++ {
        n += len(a[i])
    }

    b := make([]byte, n)
    bp := copy(b, a[0])
    for _, s := range a[1:] {
        bp += copy(b[bp:], sep)
        bp += copy(b[bp:], s)
    }
    return string(b)
}

感想

あまりいい感じの改善にはならなかったですね。
結局、string結合がボトルネックになっていたのでそこを改善すれば、
速度的にはマシになりました。
strings.Join()の挙動とcopyの挙動を知れたのが収穫です。
(よく考えると、どこがネックになっているかの計測を第一にしないといけなかったですね)

書いたコード

github.com

Write Code Every Day (1年目)

概要

Write Code Every Dayを1年間達成しましたので、感想云々を記載します。
一応、日付ずらし等はやらずにいけました。
(ユーザーミスが1日だけあったくらいですかね..)

Write Code Every Dayとは?

毎日コード書いて commit して、GitHubに芝を生やすことです。
昨年のこの時期に、 @t_wada さんのスライドを見てはじめてみました。

(元ネタはこれかな) John Resig - Write Code Every Day

@t_wada さんのスライド Write Code Every Day // Speaker Deck

結果

f:id:midori5:20171203175733p:plain

(色薄..)

作ったもの

主にGolangの勉強系で作ったものが多いです。 (90%くらい..)

gosns

github.com

  • Messaging Sever です
  • Amazon SNSを初めて触れて、書いてみたかったのでGoで書いてみました
  • Pub/Sub Modelをイメージして作成してます
  • mercari/go-httpdoc を使ってテストコードから、API Docを生成したことをよく覚えてます

gtrello

github.com

f:id:midori5:20171203175808p:plain

  • 日報作成ツールです
  • 毎日簡単に書きたくて作りました (まだ使ってます)
  • Trelloの情報をSlackへ送信する流れです
    • $ gtrello → editor open(所感記入) → #times_midori へ配信
  • Trello用のClient ( BurntSushi/toml) をガッツリ使ってます
    • こういうClient利用をきれいにテスト書かねばというのが課題です
  • Go標準のtemplateを利用して、Markdown生成とかしてました
    • (Slack投稿にしたので、最終的にはあんまり残ってないです..)

gmd

github.com

  • コマンド保存ツールです
  • 同じコマンド打つの辛いなぁと思って作りました
  • コマンド自体をまるっと保存します
  • できること
    • 保存
    • 実行
    • 一覧

gchat

github.com

  • チャットサーバーです
  • websocket ってどんな感じなんだろうと思って作りました
    • (まだよくわかってないですね..)
  • gorilla/websocket をwrapしているだけって感じでサクッと作れました

その他

  • 作りかけ放置多数

原則の達成度

  • 毎日コード書くこと
    • 達成度 70%
      • NG Point
        • README.md 更新
        • メモ等のcommit
  • 意味のあるコードを書くこと
    • 達成度 70%
  • 深夜24時前に終わらせること
    • 達成度 80%
      • NG Point
        • 24時超えのcommitは少しはあった
  • 書いたコードをGitHubOSS化すること
    • 達成度 100%

やってみての感想

  • 毎日続けることが大事
    • 毎日やるという制約がちょうどいい
  • 続けたことが見える化できることが大事
    • やったことが見える化できていないと達成度が味わいづらい

メリット

  • 毎日コード書く(commitする) 習慣 は完全に身についた
    • ほぼストレスなくできるようになった
    • 頭の片隅にいつも、今日のcommitはちらつく
    • (勉強する習慣を社会人1年目から作れたのは良かった)
  • Goがとても好きにになった
  • 新しいものに触れるととりあえず手を動かすようになった
    • 芝生えるしちょうどいいと思って、コード書いてみる
      • gRpc
      • GraphQL
      • Eclipse Collection
      • ML
      • etc..

デメリット

  • インプットが少なくなりがち
    • 本読んだり等が二の次になってしまう
  • つなげるための苦肉の策
    • REAMD.md 更新
      • (正直結構やりましたね、 README.md だけの更新..)
    • 家に帰るのが間に合わずスマホからcommit
      • 飲み会で遅くなり、commit → pushできるツールを探しました..
      • source (https://source.ianmcdowell.net/)
        • これでできましたが、pushするためには課金(500円?)が必要で..
        • 泣く泣く課金しました…
        • (後日、一緒に飲み会に行っていた同期が謎に500円くれました)
    • 24時超えcommit
      • 次の日できなさそうな時、やってました

今後

習慣化しているので、続けていこうかと思います。
エンジニアである以上、日々手を動かしていけるようにしていきたいです。

2年目

  • README.md 更新頻度の削減
    • ちゃんとコード書く
    • コード書くための時間づくりも大事にする
  • インプット → アウトプットの習慣化
    • 本をもっと読みたい
      • 基礎知識が低すぎる課題
    • 本を読んだことを活かしてコードを書く
  • 続けること
    • 2年目もがむしゃらにとにかく続ける
  • OSSへcommitする
  • Libraryもしくは有用なツールを公開する
    • ちゃんと使えるものを作りたい
    • 設計からしっかり実施したもの
  • テストコードを書く

まとめ

1年続けてみて、個人的には良い習慣かなと思いました。
基本サボり症である程度の強制力があるものがないと続かない人には、
向いているかと思います。(自分はこれです)
作ったもの等は、稚拙なものが多いですがこれも実力の内なので、
2年目はまた違った結果がみせれると良いかなと思います。