python コマンドラインインタフェース比較

目次

  • 概要
  • 環境
  • 調査対象ライブラリ
  • 比較
  • まとめ
  • おまけ

概要

今までpythonを使用してコマンドラインツールを作成する時は、docoptを使っていたのですが、ドキュメントを書いて、パースするタイミングでエラーが出た時の原因の特定に時間がかかって辛いので、他にいい方法がないか調べてみました。

環境

コードを動かした環境は以下。

調査対象ライブラリ

  • optparse
  • argparse
  • docopt
  • click

optparse

https://docs.python.jp/3.6/library/optparse.html

  • 標準ライブラリ
  • 現在は廃止予定となっており、argparseの利用が推奨(https://www.python.org/dev/peps/pep-0389/)
    • 別に使えないわけじゃないので、とりあえず今回の選定対象には入れてあります
  • ライブラリの機能として以下をサポートしていない(実装しだいで実現できなくはない)
    • 位置引数
    • サブコマンド

argparse

https://docs.python.jp/3.6/library/argparse.html#module-argparse

  • 標準ライブラリ
  • optparseの後継なので、optparseから乗り換えがし易い

docopt

http://docopt.org/

  • ドキュメントベースのコマンドラインインターフェース
  • 同じインターフェースが様々な言語で実装されている

click

http://click.pocoo.org/5/

比較

シンプルなインターフェイスの例としては ls コマンドのような command [option]インターフェイスを、サブコマンドをサポートするインターフェースの例としてgit コマンドのようにgit command [option]インターフェイスを実装して比較します。 実装したコードは下記になります。 https://github.com/replicity/compare_python_command_line_parse_libraries

実装したまとめと使ってみた所感

  • それぞれパースした結果をoptparseはマップとリスト、argparseはNamespace、docoptはマップ、clickはメソッドの引数として扱う
  • オプションの作成方法自体は異なるが、シンプルなインターフェイスの場合には機能的な大きな違いはオプション以外の引数がある時にoptparse以外はエラーになり、optparseはリストとして返すぐらい
  • シンプルなインターフェイスを作るだけなら、optparse以外ならどれを使っても問題はなさそう
  • サブコマンドをサポートするインターフェイスを作成しようとするとoptparseはライブラリの機能としてはサポートしていないので、サブコマンドを判断して処理する箇所を自前で実装することになる
  • どのサブコマンドでも使用するオプション(共有オプション)をサポートしているのはargparseだけ
  • clickは位置引数にhelpメッセージを指定できない : http://click.pocoo.org/6/documentation/

まとめ

結局どれを使うのがいいかというとこですが、機能についてまとめた表が下記になります。

ライブラリ 位置引数 オプション 型変換 ヘルプ自動生成 サブコマンド 共有オプション
optparse × × - (サブコマンドをサポートしていない)
argparse
docopt × - (ヘルプを元にインターフェイスを作成) ×
click △(helpメッセージは設定不可) ×

機能だけで見るならargparseが一番高機能です。
ただ、正直argparseでサブコマンドがあるインターフェイスを作るとそれだけでコード量が多くなって辛いです。
clickはサブコマンドの作成が非常に楽でよく考えられている印象でした。
docoptはドキュメントを最初に書くタイミングである程度実装のイメージが固まったり、設計を見直すきっかけになったりするので、ドキュメントを書くのが苦痛じゃないなら選択肢としては良さげだと思います。
ただ、docoptでエラーが出た時にエラーの原因を特定するのが結構困難なので、そこは注意が必要かと。

まとめると、基本的にはclickを使用して他の言語とインターフェイスを揃えたい時はdocopt、どうしてもargparseじゃないと実装できないものがある場合だけargparseを使うのが良さげ。

おまけ

invoke

コマンドラインインターフェイスを構築するものとして少し毛色が違うがinvokeというライブラリもあるので、使ってみました。 http://docs.pyinvoke.org/en/latest/index.html#

invokeはデコレーターベースのコマンドラインインターフェースで、複数のタスクを定義し、そのタスクを組み合わせて何かをする時に場合にはこちらが良さげでした。
具体的にはtask1の後にtask2を連続で実行するといったような場合。

inv task1 task2

また、オプションの解釈に関しては触ったライブラリの中ではinvokeが一番優秀な印象でした。
詳しくはこちらに書いてあります : http://docs.pyinvoke.org/en/latest/concepts/invoking-tasks.html#task-command-line-arguments

clickとの比較として下記。

ライブラリ 位置引数 オプション 型変換 ヘルプ自動生成 サブコマンド 共有オプション 複数のタスクの連続実行
click △(helpメッセージは設定不可) × ×
invoke △(複数の値を指連続で指定する方法にくせがある) △(boolとそれ以外) × - (サブコマンドをサポートしていない)

python-prompt-toolkit

インタラクティブコマンドラインツールを作成できるライブラリとしてpython-prompt_toolkitがあり、これを利用することで実行後にサブコマンドを選択したり、オプションをインタラクティブにセッティングしていくようなツールを簡単に作ることが可能になる(e.g. click-repl)。
https://github.com/jonathanslenders/python-prompt-toolkit/tree/master

なお、2018/5/5現在pipでいれると1.0.15がインストールされるが、githubには2.0のブランチがあり、リポジトリのREADMEに書かれているように1.0と2.0ではいろいろと変わっているようなので、これから使い方を覚えるなら2.0を使うほうが良さげでした。

Please notice that this is the 2.0 branch of prompt_toolkit. It is incompatible with the 1.0 branch, but much better in many regards. On Pypi you'll still find the 1.0 branch, but a release should follow soon. In the meantime, don't hesitate to test this branch and report any bugs.

ref

JVM周辺ツールについてまとめる

はじめに

JVMの周辺ツールをたまに使用するが、使うタイミングで毎回調べているので、一度しっかりとまとめようと思う。
実行結果はJava8のものになります。

jcmd

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/jcmd.html#CIHEEDIB
JDKをインストールした時に付属している。
JMVに対して診断コマンドを送ることができる。
フライトレコーダーの制御や、GCを強制的に起動させるいった操作が可能。
Java9ではコマンドが増えている。
主に使っている機能は下記

  • JFRの制御
  • スレッドダンプ
# プロセス番号の確認
$ jcmd    
5361 sun.tools.jcmd.JCmd
5146 info.repli.sample.mbeans.Hello

# 送信できる診断コマンドの一覧
$ jcmd 5164 help
5146:
The following commands are available:
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.run_finalization
GC.run
VM.uptime
VM.flags
VM.system_properties
VM.command_line
VM.version
help

For more information about a specific command use 'help <command>'.

# JFRの起動
## 商用機能のアンロック
jcmd 5146 VM.unlock_commercial_features

##起動
jcmd 5146 JFR.start filename=/tmp/`hostname`-`date -u +"%Y-%m-%d"`.jfr duration=180s settings=$JAVA_HOME/jre/lib/jfr/default.jfc

# スレッドダンプ
jcmd 5146 Thread.print

# GC実行
jcmd 5146 GC.run # System.gc()
jcmd 5146 GC.run_finallization  # System.runFinalization()

jconsole

JVMの情報や、提供されているMBean情報の取得、操作ができるGUIツール。
JDKをインストールすると付属している。
https://docs.oracle.com/javase/jp/7/technotes/guides/management/jconsole.html

# 起動
$ jconsole

f:id:replicity:20180213013905p:plain
f:id:replicity:20180213013907p:plain

リモートのプロセスに接続することも可能。

jmxterm

MBean情報の取得、操作がコマンドラインでできるツール。
JDKとは別にインストールする必要あり。
後述するSJKのmxコマンドでも同じことはできる。

ダウンロード
http://wiki.cyclopsgroup.org/jmxterm/download.html

使い方
ダウンロードした jmxterm-1.0.0-uber.jar をJMXコンソールにアクセスしたいサーバに持っていく。

インタラクティブに使用

# java -jar jmxterm-1.0.0-uber.jar -u <USER> -p <PASS> -l <hostname>
Welcome to JMX terminal. Type "help" for available commands.
$> beans #MBeanの一覧が出力される
...
$> get <取得したいMBeanのAttributeを指定>

JFRとJMC

JFR : https://docs.oracle.com/javacomponents/jp/jmc-5-4/jfr-runtime-guide/about.htm
JMC : https://docs.oracle.com/javacomponents/jp/jmc-5-5/jmc-user-guide/jmc.htm
※ JFR(Java Flight Recoder)とJMCは共に本番環境で使用するには商用ライセンスが必要になる。

そういえば、去年、JFRがOpenJDKに寄贈されるってブログにかかれて少し話題になっていたけど、その進捗どんな感じなんだろう。
https://blogs.oracle.com/java-platform-group/faster-and-easier-use-and-redistribution-of-java-se

jcmd等からJFRを起動し、出力されたファイルをJMCで読むことで、JFRを実行していた間のJVMの様々な情報が確認できる。
ちなみにJava9からはJMCのUIが大幅に変わっています。

f:id:replicity:20180213013910p:plain

SJK(Swiss Java Knife)

https://github.com/aragozin/jvm-tools
JVMトラブルシューティングに便利なツール群
付属しているコマンドは下記

  • ttop : topコマンドのJVMのようなもの、スレッド単位でのCPU使用率を表示する
  • jps : JVMのプロセス表示、jcmdを引数なしで実行した場合と同じだが、フィルタや表示のカスタマイズができる
  • hh : ヒープヒストグラム表示、jcmdのGC.class_histogramの高機能版、オプション次第ではフルGCが発生する
  • gc : GCの実行状態を監視して表示
  • mx : MBean情報の取得と操作、jconsoleやjmxtermと同じことができる
  • stcap : スレッドダンプと一定期間取得
  • ssa : stcapで取得したスレッドダンプを解析
  • stcpy : スレッドダンプファイルをまとめたり、フィルタしたりといった操作を実施

おわりに

基本的にはjcmdとSJKを抑えておけば問題ないと思います。
自分はSJKの存在を知る前にjconsoleとjmxtermを使っていたのでMBean系の操作はこの2つを使用し続けてます。

VimConf2017 行ってきた

Vimmerのお祭りである、VimConfに行ってきました。
http://vimconf.vim-jp.org/2017/

会場は秋葉原駅から徒歩2分のアキバホールで、席には電源完備されていて心置きなくvimができる環境。 スライド、動画が後日公開されるらしい。

感想

  • 便利そうな設定やプラグインについて知れてよかった
  • 以外とwindowユーザ多い
  • 会場がすごい人だらけですごかった
  • どのセッションもすばらしくて非常に濃いカンファレンスでした f:id:replicity:20171106013607j:plain

各セッションの感想とメモは下記。

Vim, Me and Community

https://docs.google.com/presentation/d/14pViuMI_X_PiNwQD8nuGRG72GUqSeKDqoJqjAZWS39U/edit
vimを使い初めてからの話が主で、最後にすこしvimのコミュニティについての話 。
プラグイン開発をどうやって初めて行ったか、何に気をつけて作っているかは興味深い話だった。

The Past and Future of Vim-go

https://speakerdeck.com/farslan/the-past-and-future-of-vim-go
vim-goの作者
vim本体でのgoのサポートが終わったので、vim-goを作った
なんでvim-goが有名になったか

  • 小さいpluginがありすぎていた
  • 他のpluginにロードマップがなかった
  • ドキュメントがない問題を解決した
    • この作業は面白くない
    • markdownサポートのPRをまっている
  • UXを向上していった

どうして必要なのかを説明してとても勉強になった。

Creating your lovely color scheme

カラースキームの話
Icebergの作者
人生の33%はcolor schemeらしい

  1. 最初にコンセプトを決めると、後々役に立つ時がある
  2. 代表的な色を決める
    • preferred groupってやつがあってこれに設定すると他のにも引き継がれる
    • HSB/HSLを使うのがおすすめ
    • カラーパレットを作る時は基準を決めて色相だけを変える
  3. lovelyにする
    • ポジティブを増やす、ネガティブを減らす
    • ポジティブ
      • file typeのサポートを増やす
      • pgment.vim と colorswatch.vimが便利
      • 有名なプラグインに対応していく
      • identitiesを少し足すのもいい
    • ネガティブ
      • 256環境をサポートする
      • 警告色を考える
      • 色覚についても考える
  4. 先人をどこから探すか

color schemeを作る時以外にもプレゼンのデザイン考える時とか参考になりそうな内容だった。

vim-mode-plus: The most ambitious vim emulator in the world

https://qiita.com/t9md/items/236d09fea9bcdfabdcea
atomvim-modeを作るプラグインの話。
単純にvim-modeがあるだけじゃなくて、便利な機能が追加されていて、それの紹介もあった。
特にmove-{up|down}-to-edgeが便利そうだった。

Vim and Compatibility

http://lamsh.github.io/slide/2017/20171104_VimConf2017/index.html
POSIX原理主義といろんな環境で動くvimrcを書く方法についての話。
OSと文字コードの対応が特に大変そうな感じで、実際には自分が触る環境は多くても3種類ぐらいなのでその中で動けばいいかなーって感想。

neosnippet.vim + deoppet.nvim

https://www.slideshare.net/Shougo/neosnippetvim-deoppetnvim-in-vim-conf-2017
暗黒美夢王さん!
テキストエディタは世界のすべて」

スニペットプラグインについての話。

  • neosnippet.vim
    • marker type snippet plugin
    • 日本では有名
    • snipMate syntax
  • snopMate.vim
    • 有名
    • 最近は開発が活発ではない
    • マーカータイプではない
  • UltiSnips
    • snipMateとは互換性がない
    • 外国ではおそらく一番有名
    • 多機能
    • マーカータイプではない
    • neovimでは遅い

マーカータイプのスニペットプラグインとは

  • カーソルの移動先にマーカーを作って、そこに移動する
  • 実装が楽

マーカーがないやつの特徴は

  • 実装が大変
  • パフォーマンスもよくはない
  • つねにバッファを監視してうごく
  • カーソルを戻す実装がしやすい

開発中のスニペットプラグイン 「Deoppet.nvim」 https://github.com/Shougo/deoppet.nvim

  • neosnippet互換のsyntax
  • マーカーがないタイプのスニペットプラグイン
  • extended Marks というneovimの新機能を使う予定(現在PRにある機能 )
  • 試すためにはneovimの特定のブランチを自分でビルドして黒魔術する必要がある

How ordinary Vim user contributed to Vim

https://speakerdeck.com/daisuzu/how-ordinary-vim-user-contributed-to-vim
vim本体にpatchを送る話。
「必要なものはコントリビューターになりたいという強いパッション」

送ったパッチの話

  • 辞書ファイルにパスを指定できなくなった問題を直した
    • ある朝vimをビルドするとエラーが
    • ちょうどvimconf2016のあとでpatch熱があった
    • パッチとテストを書いた、コミュニティの状態を調べた結果、送ったのはテストだけ
  • シソーラス補完のファイルのパスのチェックがなかった
    • 辞書ファイルのチェックをシソーラス補完にも追加した
  • インサートモードの補完がなかなかでない問題があった
    • 補完候補を表示するなかで不要な処理をしていた
    • 自分以外にも気づいた同僚がいたので、コントリビューターに仕立て上げようとした
    • 転職することになったので、同僚に任せずにパッチを書いた

patchを送る先

  • vim_dev
    • google group
    • gistにパッチを貼っておくとか
    • 承認制
  • github
    • PRを遅ればいい
    • ただし、Githubのマージボタンは使っていない

この発表が一番記憶に残っている。
淡々と話していましたが、実際はすごいことを成し遂げていてとても素晴らしいと思いました。

The new syntax highlighter for Vim

https://speakerdeck.com/pocke/the-new-syntax-highlighter-for-vim syntax highlighterの話。
iro.vim : 新しいsyntax-highlighterを作った、まだサポート言語は少ないがrubyはだいたいいける
言語自体のパーサーを使用して、間違ったハイライトをしない用に作っている(ASTベースでのsyntax-highlighter)。
パフォーマンスの問題はある、rubyなら10000行を超えると落ちる -> しかしパースの問題ではなくて、vimでmatchadpos()を大量にしようすると遅くなるらしい。

You've been Super Viman. After this talk, you could say you are Super Viman 2 -- Life with gina.vim

gina.vimの紹介

  • git用のプラグイン
  • 簡単なコマンド
  • TAB補完できる
  • . で繰り返しできる

めっちゃ便利そう

pythonでのdebug(pdb,pudb)のメモ

Pythonのdebugで使用するpdbとpudbについてのメモ

pdb

対話形式のデバッガ、標準ライブラなのでインストールなしで使える
https://docs.python.jp/3/library/pdb.html

主なコマンドは下記

コマンド 効果
b ブレークポイントを設定
c ブレークポイントまで移動
n 現在の行を実行し、次の行で停止。次の行が関数だった場合は関数の中では止まらない
s 現在の行を実行し、次の行で停止。次の行が関数だった場合は関数の中で停止
p 変数の値を表示
pp pretty-printで変数を表示
q デバッガ停止
r 関数を抜ける
list 実行中の行の周辺を表示
whatis 式の型を表示
# python -m pdb スクリプト名
$ python -m pdb sample.py
(Pdb) b 19 # <- 19行目にブレークポイントを設置
(Pdb) n # <- 1行実行
-> from __future__ import print_function
(Pdb) list # <- 現在の行の周辺を表示
  1     # vim: fileencoding=utf-8
  2
  3     from __future__ import division
  4  -> from __future__ import print_function
  5     from __future__ import unicode_literals
  6     from __future__ import absolute_import
  7     try:
  8         from future_builtins import ascii, filter, hex, map, oct, zip
  9     except ModuleNotFoundError:
 10         pass  # running python version is 3
 11
(Pdb) c # <- ブレークポイントまで移動
-> x = div(x, i)
(Pdb) list
 14         return x / y
 15
 16     def main():
 17         x = 6
 18         for i in [3, 2, 1]:
 19 B->         x = div(x, i)
 20         return x
 21
 22     if __name__ == '__main__':
 23         main()
[EOF]
(Pdb) c
-> x = div(x, i)
(Pdb) list
 14         return x / y
 15
 16     def main():
 17         x = 6
 18         for i in [3, 2, 1]:
 19 B->         x = div(x, i)
 20         return x
 21
 22     if __name__ == '__main__':
 23         main()
[EOF]
(Pdb) p i # <- iの値を表示、ループの2回目なので、i は現在2
2
(Pdb)

起動時に指定する以外にも、import pdb; pdb.sey_trace() を記述することで、コード中で起動することもできる。

# vim: fileencoding=utf-8

from __future__ import print_function

def main():
    print('hoge')
    import pdb; pdb.set_trace()  # ここでpdbを起動する
    print('fuga')

if __name__ == '__main__':
    main()

pudb

pdbよりも高機能なデバッガ、こちらはインストールが必要

$ pip install pudb

基本的な操作はpdbと同じ。 困った時は?でヘルプがでるのでそれを見ればいい。 <C-x>pythonを実行できるペインに移動でき、ここでreplのようにいろいろ試せるのが便利。

起動は下記

$ pudb3 sample.py

pyconjp 2017に参加して来ました

概要

9月7,8,9,10で開催されたpyconjp 2017に参加してきました。
カンファレンスは8,9でしたが、7にチュートリアル、10に開発スプリントが開催されています。
また、今年はCfPをだしたが採択されなかったトークを喋るRejectConも開催されたようです。
今年のテーマは「OUTPUT & FOLLOW」
公式サイト : https://pycon.jp/2017/ja/

セッション感想

セッションのスライドはだいたい公開されており、toggterにもまとまっていたので詳しくはそちら
セッションのメモは以下

野球を科学する技術〜Pythonを用いた統計ライブラリ作成と分析基盤構築

https://speakerdeck.com/shinyorke/ye-qiu-woke-xue-suruji-shu-pythontotong-ji-raiburaritofen-xi-ji-pan-number-pyconjp

  • 野球ユニフォームで登壇が正装
  • 分析基盤を構築した話をする
    • 気軽に作って壊せる
    • 使いたい・試したいライブラリを積極活用
    • OSSにする
  • DBはMySQL
  • データはスクレイピング(Scrapy)、時系列データとして使いたいのでAirflow
  • 実験はjupyter
  • scrapyはクローラー界のRoRと読んでいいぐらい高機能
  • airflow
    • ジョブ管理
    • 結構高機能
    • 意外と苦労した
  • 分析
    • 定期的に見るか、分析に使うかで使い分ける
    • 定期的はRedash
    • 実験とかはjupyter

Pythonで大量データ処理!PySparkを用いたデータ分析のきほん

https://speakerdeck.com/chie8842/pythondeda-liang-detachu-li-pysparkwoyong-itadetachu-li-tofen-xi-falsekihon

Kivyによるアプリケーション開発のすすめ

https://www.slideshare.net/JunOkazaki1/pyconjp2017-kivy

  • python3.5
  • mac,Linuxでも動くはず
  • MITライセンス、Qtとは違う!
  • UIはkvという独自言語
    • kvはCSSのBootstrapのようなもの
    • kvを使用するのが普通、その方がコードが簡潔
  • kivyを今使うのは結構たいへんそう
  • デモは良さげ

len()関数がオブジェクトの長さを手にいれる仕組み

https://www.slideshare.net/shimizukawa/how-does-python-get-the-length-with-the-len-function

  • len()が戻り値の型をintで保証
  • Adapter Patternで実装されている
  • スライドがすごく分かりやすかった
  • 公式ドキュメントの「デザインと歴史」を読めばいろいろわかりそう https://docs.python.org/ja/3/faq/design.html

Pythonistaで始めるiOSプロトタイプ開発

Python機械学習によるWebセキュリティの自動化

https://www.mbsd.jp/insight.html https://t.co/thfG8n2080

  • Webアプリケーション診断を自動化
  • 従来
    • アプリをクローリングして疑似攻撃
    • 判定
  • 少ない手数で脆弱性を検出する(いろいろするわけではない)
  • LSTM
  • 高度すぎてついていけなかった

2日目keynote

https://speakerdeck.com/sinhrks/pandasdefalseosshuo-dong-shi-li-tozui-chu-false-bu

  • pythonと科学計算
    • プログラマー以外でもpythonを使ってプログラムをする人が増えてる
    • SciPy2017に行ってきた話 9日間開催
      • クレータの解析をpythonでした話
  • pandasのOSS活動
    • pandas : データ分液のためのデータ構造を提供するパッケージ
      • 現実の汚いデータ(フォーマットがバラバラ)を効率的に扱えるのが便利
      • データの理解、データ準備に使用される
  • OSS活動 最初の一歩
    • コミッタの役割の話は興味深い
      • インフラの整備とかもやってる
      • プロダクトとコミュニティの品質の維持をやるのが大事
      • プロダクト品質の維持

Pythonで実現する4コマ漫画の分析・評論 2017

https://slideship.com/users/@esuji/presentations/2017/08/FSKS46VL9ivVSpUE4Gaamh/

  • 情熱駆動開発!!
  • OCR三国志の人と同じGoogle OCR
  • 誰が何処に出ているか等も分析したい
    • dlibの物体検出器で人物判定
    • 横向き、後ろ向き、顔の重なりがあるのでそれを調査
      • 期待した精度はでなかった
    • 検出人物の分類
      • CNNを利用
      • だめだった

The theory of Serverless development by Python (理論から学ぶPythonによるサーバレス開発)

https://slideship.com/users/@marcy-terui/presentations/2017/09/9PzXZzcJfBR1ENEMmMbGSf/ https://www.slideshare.net/acloudguru/ant-stanley-being-serverless

  • Serverlessとは
    • 管理すべきサーバがない
    • サーバOSがない
    • 常駐サーバプロセスがない
  • Function as a Service
    • 関数単位で独立した環境とリソースを提供
    • 関数単位で横にスケール
    • 高い抽象度
  • Function SaaS
    • 単独ではサービスとしては簡潔しない
      • Fully featured Saasはその逆で単独で簡潔するもの
      • APIエンドポイント,データストア,認証、検索
    • プラットフォームの一部だったり
  • Serverlessがどうして登場したのか
  • どのように実装するか
    • 実態はDockerが多い
      • 特定のイベントによってコンテナが起動されて関数が実行される
      • VMよりも起動のオーバーヘッドは小さいが、オーバーヘッド自体はある
      • オーバーヘッドがあるので、一度起動したコンテナを再利用することもある
    • イベント駆動
      • 何かがトリガーとなって起動する(タイマー、クラウドリソースの変化、画面操作etc)
      • 1イベントにつきふうくすうのFunctionを実行可能
    • pull型のPub/Subモデル
  • Serverlessに向いているもの
    • IoT
      • IN/OUTが明確で各ステップでスケール調整できるのがGood
    • ユーザ行動データ解析
    • 特定のイベントに連動していろんなSaaSが動くやつ
    • 普通のWeb APIアプリケーションには向いていない
      • RequestとResponseが一対一で同期的だから
  • DBAとのコネクションコストを考える

Clearer Code at Scale: Static Types at Zulip and Dropbox

英語セッションだったので聞くのに必死でメモはなし
英語スライドに機械翻訳ぽい日本語があった
- mypyの話 https://github.com/zulip/zulip/blob/master/docs/mypy.md http://qiita.com/t2y/items/2a1310608da7b5c4860b

Pythonをとりまく並行/非同期の話

https://tell-k.github.io/pyconjp2017/#1

  • python3.6リリースパーティーで聞いた話と大体同じ
  • asyncio のイベントループポリシーを差し替えることができるのは知らなかった

SREエンジニアがJupyter+BigQueryでデータ分析基盤をDev&Opsする話

https://speakerdeck.com/yuzutas0/20170909

カンファレンス感想

  • 初日は新大久保から歩いてみたら結構遠くて辛かったので、今後ここが会場になることがあれば素直に西早稲田駅からいくのがおすすめ
  • 無限コーヒーと無限オレンジジュースは嬉しい
  • 毎年pyconjpのおやつは美味しくて密かに楽しみにしてたが、今年も美味しかった
  • ゴミ箱のインデントは健在
  • ポスターセッションは楽しい、他のカンファレンスでも取り入れて欲しい
  • 1日目のkeynoteで同時翻訳レシーバーを手に入れそこねたがゆっくり喋ってくれていたのでなんとか聞き取れた(メモは取れてない)
  • ブースで話していたらいつの間にセッションが始まっていたので、アナウンスがあると嬉しい

Neovimでgolangを書く設定 備忘録

概要

Macgolangをインストールし、Neovimで書く環境を整えます。

インストール

まずはgolangをインストールします。

$ brew install go
$ nvim .zshrc #下記を追加
if [[ -d /usr/local/opt/go ]];then
    export GOPATH=$HOME/work.go
    export PATH=$PATH:$GOPATH/bin:/usr/local/opt/go/libexec/bin
fi

設定

設定するものは下記です。

GOPATH

golangで使用するディレクトリをGOPATHに指定する必要があるので、設定します。
GOPATH : https://github.com/golang/go/wiki/GOPATH

.zshrcに下記を追加します。
今回は~/devをGOPATHに指定しています。

#go
if [[ -d /usr/local/opt/go ]];then
    export GOPATH=$HOME/dev
    export PATH=$PATH:$GOPATH/bin:/usr/local/opt/go/libexec/bin
fi

GOPATHを指定すると、今後はgo getでインストールするgolangのライブラリがこの下にインストールされるようになります。

補完

deoplete.nvimを使っているので、deoplete-goをセットアップすればいいです。
設定も公式サイトのものをとりあえずそのまま使用しています。
deoplete-go : https://github.com/zchee/deoplete-go

依存しているものをインストールします。

$pip2 install --upgrade neovim
$pip3 install --upgrade neovim
$go get -u github.com/nsf/gocode #GOPATHで指定したディレクトリの下にインストールされる

dein_lazy.tomlに下記を追加します。

[[plugins]]
repo = 'zchee/deoplete-go'
on_ft = 'go'

vim-go

ちょっと前だとvim-goはneovimに対応してなかったようですが、今は使えるようになっているみたいなのでvim-goを入れます。
dein_lazy.tomlに下記を追加します。

[[plugins]]
repo = 'fatih/vim-go'
on_ft = 'go'

インストールしたら下記をneovimで実行すると、vim-goで使うライブラリがGOPATHの下にインストールされます。

:GoInstallBinaries

vim-go自体にチュートリアルがあるのでそれをやれば一通りの使い方はわかりそう
vim-go-tutorial : https://github.com/fatih/vim-go-tutorial

シンタックスチェック

neovimではSyntasticの代わりにneomakeでシンタックスチェックをするので、neomakeをインストールします。
dein.tomlに下記を追加します。

[[plugins]]
repo = 'neomake/neomake'

a tour of go をやりながら書いていたメモのまとめ

エントリーポイント

  • エントリポイントはmainパッケージのmain関数
package main

func main() {
    // write your some code
}

関数

  • 複数の値を返せる
  • 戻り値に名前をつけることができる
// func 関数名 (引数,...) 戻り値の型 { }
func add1(x int, y int) int {
    //引数がx,yでともにint型
    //戻り値の型はint
    return x + y
}

func add2(x, y int) int {
    //add1と同じだが、引き数の型をまとめて宣言
    return x + y
}

func swap(x, y string)(string, string) {
    //引数がx,yで共にstring型
    //戻り値が2つあり、共にstring型
    return y, x
}

//naked returnと呼ばれる方法で関数が短い場合にのみ使うべき
func split(sum int)(x, y int)  {
    //戻り値の指定部でx,yの変数を宣言している
    x = sum * 4 / 9    
    y = sum - x
    //関数の宣言で指定した値を自動的に返す
    return 
}

defer

  • 呼び出し元が終わるタイミングで実行される
  • 複数のdeferを書いた場合は後に書いてあるものが先に実行される(LIFO)
  • 引数の評価は実行時ではなく、deferが書いてある場所で行われる
  • 絶対開放しないと行けないリソースなどの処理に便利(ファイルのクローズ、ミューテックス(排他制御)のアンロック)
import (
    "fmt"
)

//これを実行すると3,2,1の順番で表示される
func main(){
    i := 1
    defer fmt.Println(i)
    i = 2
    defer fmt.Println(i)
    i = 3
    defer fmt.Println(i)
}

定数

  • constで定義
  • 定数は変数に代入する時や、式の中で型がきまる(宣言時は型が決まっていない)
const u = 1
const p = 2.0

変数宣言

  • varで宣言
  • 変数のスコープはfor,if,func,globalがある
  • 関数の中での宣言には := も使える、varを省略し、宣言と同時に初期化をする
var c,python,java bool //3つともbool型
var i, j int = 1,2  //2つともint型でi=1,j=2で初期化
var c,python,java = true,false,"no" //初期化時に型が決まるので、明示的に型を書かないでも大丈夫
func main() {
    k := 3 //関数の中なのでvarを省略する方法が使える
}

基本型

初期値 備考
bool false 論理値
string “” 文字列
int, int8, int16, int32, int64 0 整数、特別な用途以外ではintを使用
uint, uint8, uint16, uint32, uint64, uintptr 0 符号なし整数、unitptrはポインタの値を表現するようの整数型
byte 0 バイト単位でデータを扱う、alias for uint8
rune 文字単位のデータを扱う、alias for int32
float32, float64 0
complex64 , complex128 複素数

型変換

  • 暗黙の型変換はない
  • 変換後の型をTとした時、T(v)でvをT型に変換する
func main() {
    var x int = 3, 4
    var z uint = uint(x)
    var f float64 = float64(x)
}

ポインタ

  • *変数名で宣言した場合はポインタ
  • ポインタの初期値はnil
  • &オペレータはポインタを返す)
  • *オペレータはポインタが指す先を返す(dereferencingまたはindirecting)
import(
    "fmt"
)

func main(){
    var *p int //int型ポインタ
    i := 1
    q := &i //qもint型ポインタになる
    fmt.Println(*q) //1が表示される
}

構造体

type 構造体名 struct {
    変数1
    変数2
    変数n
}

var v 構造体名{初期値}
v.変数1 = 10 
  • goにはクラスはない
  • 構造体はフィールドの集まり
  • 構造体のフィールド名は大文字で始めるのが多い
import(
    "fmt"
)

type Vertex struct {
    X int
    Y int
}

func main() {
    v1 := Vertex{1, 2}
    v2 := Vertex{x: 1} //yは初期値になるので0
    v3 := Vertex{} //x,y共に初期値は0
    p := &Vertex{1, 2} //構造体のポインタもある
    q := &v1 //構造体のポインタになる
    q.x = 1e9 //本来なら (*q).vで参照するが、syntax sugerがある
    fmt.Println(q)

}

array

//宣言
var 変数名 [要素数]型
var 変数名 [要素数]型{初期値,...}
//代入、参照
変数名[要素] = 値
fmt.Print(変数名[要素])
  • 配列の長さは変更不可
import (
    "fmt"
)

func main() {
    var a [2]string
    a[0] = "Hello"
    fmt.Println(a[0])
    fmt.Println(a)
    primes := [6]int{2, 3, 5, 7, 11, 13}
}

slice

var 変数名 []型 
  • 可変長
  • リストのようなもの
  • sliceは配列への参照をもっているようなもの
  • sliceは参照渡し
  • sliceには下記がある
    • 長さ(length) : 含まれる要素の数
    • 容量(capacity) : sliceのサイズ
  • ゼロ値はnil、長さと容量は0となる
  • 組み込み関数makeを使用して作成できる
  • sliceの入れ子もできる
  • sliceへの要素の追加はappend、追加時にsliceの容量が足りない場合は自動的に良い大きいサイズのsliceが作られ、それを返す
  • forループ時はrangeを利用
import  (
    "fmt"
)

func main(){
    primes := [6]int{2, 3, 5, 7, 11, 13}

    var s1 [] int = primes[1:4] //sはint側のスライスで3,5,7が入ってる
    var s2 []bool{true, true, false} //最初からスライスを作ることもできる
    len(s1) //要素数は3
    cap(s1) //サイズも3

    //下記はすべて同じ意味になる、下限は0、上限は配列のサイズがデフォルト値になるため 
    var s3 []int = primes[0:6]
    var s4 []int = primes[:6]
    var s5 []int = primes[0:]
    var s6 []int = primes[:]

    //nil slice
    var s7[]int //s7 == nil がtrueになる

    a := make([]int, 5) //sliceのcapが5でlenも5、各要素は0で初期化されている
    b := make([]int, 0, 5) //sliceのcapが5でlenは0のsliceを返す
    //要素の追加
    c := append(a, 1,2,3,4,5,6) //aの容量は5ですでにlenも5なので大きいサイズのsliceが作られる
    //戻り値は新しいsliceで元のslice自体は変化していないので注意
    //上記の場合はcは大きいサイズのsliceでaはappend前と変化なし

    //入れ子のslice
    board := [][]string{ 
        []string{"_", "_", "_"},
        []string{"_", "_", "_"},
        []string{"_", "_", "_"},
    }
    fmt.Println(board[0][0])

    //forループ
    for i, v := range s2 {
        // i は index, v はvalue, pythonのenumerate関数と同じような動き
        fmt.Println(i)
        fmt.Println(v)
    }

    for i := range s2 {
        //indexだけ使う場合
        fmt.Println(i)
    }

    for _, v := range s2 {
        //valueだけ使う場合
        fmt.Println(i)
    }

}

map

var 変数名 map[キーの型]値の型 
  • キーと値を関連づける
  • mapのゼロ値はnil,nilはキーを持っておらず、キーを追加することもできない
  • make関数を使用して初期化する
  • 新しいキーは代入するだけで作れる
  • 値を指定して初期化もできる
  • キーの削除はdelete関数
  • キーの存在確認は参照時の戻り値で判定する
import (
    "fmt"
)

//緯度経度を持つ構造体
type Vertex struct {
    Lat, Lon float64 
}

func main() {
    var m map[string]int //キーがstring,値がint,この時は型が決まっているが値はnil
    m = make(map[string]int) //map型のインスタンスを作ったような状態
    //m := make(map[string]int) //上の2行をまとめてこれでもOK
    m["age"] = 26 //代入
    fmt.Println(m["age"]) //参照

    //宣言と初期化と同時に
    var n map[string]Vertex {
        "Bell Labs" : Vertex{
            40.68433, -74.39967,
        },
        "Google" : Vertex{
            37.42202, -122.08408,
        },
    }

    //値の型が単純な型名の場合は値の型から推測できるので、省略可能
    var o map[string]Vertex {
        "Bell Labs" : { 40.68433, -74.39967, },
        "Google" : { 37.42202, -122.08408, },
    }

    //要素の削除
    delete(n, "Bell Labs") //Bell Labsを削除

    //キーの存在確認
    elem, ok := n["Bell Labs"] //Bell Labsは削除済みなのでokはfalse, elemは要素の型のゼロ値なので今回はnil
    elem, ok = n["Google"] //Googleはまだあるのでokはtrue,elemは要素の値を返す


}

関数型(function values)

  • 関数も変数として扱える
  • 他の関数に関数を渡すことができる
  • 関数の戻り値として関数を返すこともできる
import (
    "fmt"
    "math"
)

//compute関数は引数としてfloat64型の値を2つとり,float64型を返す関数をとり、float64型を返す
func compute(fn func(float64, float64) float64) float64 {
    //引数として受け取ったfloat64型の値を2つとり,float64型を返す関数に引数3と4を指定して呼び出す
    return fn(3, 4)
}

func main() {
    //hypotは関数型
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }
    //hypotに格納されている関数を呼び出す
    fmt.Println(hypot(5, 12))

    //compute関数にhypot関数を引数として渡す
    fmt.Println(compute(hypot))
    //compute関数にmath.Pow関数を引数として渡す
    fmt.Println(compute(math.Pow))
    }

closure

  • 関数の外の値を関数に対して紐付ける(bind)する仕組み
  • closure関数を返す関数を作成し、closure関数を返す前にclosure関数に紐付ける変数を宣言する
import "fmt"

//adderはint型と引数としてとり、int型を返す関数を返す
func adder() func(int) int {
    sum := 0 //adderで返す下記の関数からはsumを参照でき、adderを呼び出して新しい関数を返すたびにそれぞれの関数に別々のsumが紐づく
    return func(x int) int { //closure関数 
        sum += x
        return sum
    }
}

func main() {
    //posとnegにはそれぞれ別のsumが紐づく
    pos, neg := adder(), adder()
    for i := 0; i < 10; i++ {
        fmt.Println(
            pos(i),
            neg(-2*i),
        )
    }
    }

if

if 条件式 {
} else if 条件式 {
} else {
}

if 式; 条件式 {
}
  • ()はいらない
  • 条件の前に簡単な式を書くことができる
import (
    "math"
)

func main() {
    x := 1
    if x < 0 {

    } else if x >= 5 {

    } else {

    }

    n := 2
    lim := 10
    if v := math.Pox(x, n); v < lim {
        //v := math.Pox(x, n);を実行したあとにv < limが評価される
        //vのスコープはこのifの中だけ
    } else {
        //vはelseでも使える
    }

ref : https://gobyexample.com/if-else

switch

  • breakがなくても次のcaseは実行されない
  • 他言語でbreakを書かなかった場合と同じ動作をさせたい場合はfallthroughを書く
  • case式に関数を指定できる
  • switchに式を書かない場合は、case式を評価し、trueになったcaseを実行する
func main() {
    i := 10
    switch i {
        case 0:
        case 1:
            fallthrough 
        case 2:
        case f():
            //f()を呼び出した結果の戻り値をiを評価する、f()の前のcaseで実行する箇所が決まった場合はf()も呼ばれない
        default :
            //今回はこれが実行される
    }

    now := 1
    switch { //一番最初にcase式がtrueになったcaseが実行される
        case now < 12:
            //今回はこれが実行される
        case now < 18:

        default:
    }
}

for

for 初期化部; 条件式; 増分 {
}

for 条件式 { //whileの代わり
}

for { //無限ループ
}
  • ()はいらない
  • 条件の前に簡単な式を書くことができる
  • whileはないので、whileの動作を実現したい時はforで初期化部と増分を書かない
  • break,continueはある
  • goにはpythonのrange的なのものはない
import (
    "fmt"
)

func main(){
    for i := 0; i < 10; i++ {
        //iのスコープはforの中だけ
    }

    sum := 1
    for sum < 100 { //whileの代わり
        sum += sum
    }

    x := 0
    for { //無限ループ
        if x >= 3 {
            break
        }
        x++
    }

    for i := 0; i < 10 ; i++ { 
        if i % 2 == 0 { 
            continue
        }
        fmt.Println("奇数:", i) 
    }

メソッド

//func レシーバ- 関数名 戻り値の型 
func (i Vertex) Abs() float64{}
  • golangにはクラスはないが、型にメソッドを定義できる
  • 特定の型にメソッドを追加する場合はレシーバーを使う
  • レシーバーは同じパッケージにある型にだけ追加できる
  • 組み込み型にレシーバを追加したい場合はtypeで型に別名をつける
  • ポインタに対してもレシーバを追加できる
  • ポインタに対してレシーバを追加した場合は参照渡し、それ以外は値渡しになる
  • ポインタレシーバ(ポインタに対してレシーバを追加したもの)と変数レシーバ(ポインタ以外にレシーバを追加したもの)を混在させるべきではない
  • ポインタレシーバの場合はメソッド呼び出し毎に変数のコピーを作らないので、こちらを使うのがおすすめ
  • 関数の場合は引数で渡すの値がポインタかそうでないかを意識しないと行けないが、レシーバの場合はgoが良しなに変換してくれる
import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

//型VertexにAbs()を紐付ける
func (v Vertex) Abs() float64 {   
    //vの値を参照できる
    return math.Sqrt(v.X * x.X + v.Y * v.Y)
}

//ポインタレシーバ
func (v *Vertex) Scale(f float64) {
    v.X = v.X * f 
    v.Y = v.Y * f
}

type MyFloat float64 

//float64にMyFloatという別名をつけ、MyFloatに対してレシーバーをつける
func (f MyFloat) Abs() float64 {
    if f < 0 { 
        return float64(-f)
    }
    return float64(f)
}


func main(){
    v := Vertex{3, 4}
    //Vertex型のvはAbs関数を呼び出せる
    fmt.Println(v.Abs())
    v.Scale(5)
    fmt.Println(v)
    f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}

インターフェイス

type 型名 interface {
    //実装すべき関数
    //実装すべき関数
    //実装すべき関数
}
import (
    "fmt"
)

type Abser interface{ Abs() float64 }

type MyFloat float64

//Absを実装したのでMyFloatはAbser型にもなる
func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

type Vertex struct { X, Y float64 }

//Absを実装したのでVertexはAbser型にもなる
func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y)
}

type I interface {
    M()
}

type T struct {
    S string
}
func (t *T) M() {
    //tがnilの場合でも適切に処理をする
    if t == nil {
        fmt.Println("<nil>")
        return
    }
    fmt.Println(t.S)
}

func main() {
    var a Abser
    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}

    a = f  // a MyFloat implements Abser
    fmt.Println(a.Abs()) //MyFloatのAbs()が呼ばれる
    a = &v // a *Vertex implements Abser
    fmt.Println(a.Abs()) //VertexのAbs()が呼ばれる

    var i I

    var t *T
    //この時のTはまだnil
    i = t
    i.M()

    i = &T{"hello"}
    i.M()
}

nil インターフェイス

import "fmt"

type I interface {
    M()
}

func main() {
    var i I
    //ここのiの型はIインターフェイスだが、具体的な型(実装を持っている)が代入されていないので、ランタイムエラーになる
    i.M()
}

emptyインターフェイス

  • ゼロ個のメソッドを定義したインターフェイス
  • 任意の型を代入できる
  • 未知の型を扱うコードで使用する(fmt.Println()が代表例)
package main

import "fmt"

func main() {
    //iはemptyインターフェイス型なので任意の型を代入可能
    var i interface{}
    describe(i)

    i = 42
    describe(i)

    i = "hello"
    describe(i)
}

func describe(i interface{}) {
    fmt.Printf("(%v, %T)\n", i, i)
}

type assertions

import "fmt"

func main() {
    var i interface{} = "hello"

    //この時iがstring型じゃなければここでpanicが引き起こされる
    s := i.(string)
    fmt.Println(s)

    //sにはiの値が入る,okにはtrue
    s, ok := i.(string)
    fmt.Println(s, ok)

    //fはfloat64のゼロ値になりokはfalse
    f, ok := i.(float64)
    fmt.Println(f, ok)

    f = i.(float64) // panic
    fmt.Println(f)
}

type switch

  • type assertionsを直列に使用する方法
import "fmt"

func do(i interface{}) {
    //iの型で分岐するswitch文
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default: //一致する型がcaseにない場合はdefaultが呼ばれる
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    do(21)
    do("hello")
    do(true)
}

よく使われるインターフェイス

  • Stringer : stringとして表現できる型
type Stringer interface {
    String() string
}
import "fmt"

type Person struct {
    Name string
    Age  int
}

//PersonはこれでStingerインターフェイスを実装した
func (p Person) String() string {
    return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
    a := Person{"Arthur Dent", 42}
    z := Person{"Zaphod Beeblebrox", 9001}
    fmt.Println(a, z) //ここでPersonのString()が呼ばれる
}
  • Errors : エラーの状態を表現する
    • goでは関数がよくerror型の値を返す、この値がnilの場合は処理が成功したことを示す
type error interface {
    Error() string
}
import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

//errorインターフェイスを実装
func (e *MyError) Error() string {
    return fmt.Sprintf("at %v, %s", e.When, e.What)
}

//errorインターフェイスを返す
func run() error {
    return &MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}
  • Readers : データストリームを読む
//byte型を引数にとり、int型とerrorインターフェイス型を返す
//bにデータをいれ、入れた数をnとして返す
func (T) Read(b []byte) (n int, err error)
import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Hello, Reader!")

    b := make([]byte, 8)
    for {
        //rの値をbに入れる
        //入った値がnに、失敗した場合はerrがnil以外になる
        n, err := r.Read(b)
        fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
        fmt.Printf("b[:n] = %q\n", b[:n])
        if err == io.EOF {
            break
        }
    }
}
  • Images : 画像を表現する
package image

type Image interface {
    ColorModel() color.Model
    Bounds() Rectangle
    At(x, y int) color.Color
}
import (
    "fmt"
    "image"
)

func main() {
    //imageインターフェイスが実装されているクラスを使ってみる
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    fmt.Println(m.Bounds())
    fmt.Println(m.At(0, 0).RGBA())
}

goroutine

go 関数名()
変数名 := make(chan 型) //指定した型のchannelを作成
channel変数 <- 値 //値をchannel変数に書き込み
変数 := <- channele変数  // channel変数の値を読み込む
  • メモリ空間は共有
  • 通常の関数をgo 関数名で呼び出せばgoroutineとして呼び出せる
  • goroutine間でデータを渡す時はchannelsを使用
  • channelの読み書きの時にchannelのバッファに対して処理ができない場合は、他のgoroutineの処理を待つ
  • channelにはバッファサイズを指定できる
  • channelにこれ以上値を送信することがない時はchannelをcloseする
  • channelに対してrange でループをしている時はchannelがcloseされるまでループする
  • channelはgoroutine以外からもいじれる
  • 使用できるchannelで分岐したい時はselectを使用する
  • ブロックしないでchannelを使用したい時はdefault付きのselectを使用する
import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c
}

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x //書き込みの毎に読み込みがあるまでここでロックされる
        x, y = y, x+y
    }
    close(c) //cに対してもう書き込まないのでそれを明示する
}

func fibonacci2(c, quit chan int) {
    x, y := 0, 1
    for {
        select { //cに書き込めるかquitが読み込めるようになるまでここでロック
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    go say("world") //sayをgoroutineとして起動
    say("hello")

    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int) //chanel作成
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // cに値が入るまでここでロックされる

    fmt.Println(x, y, x+y)

    ch := make(chan int, 2) //channelのバッファサイズを2で宣言
    ch <- 1
    ch <- 2 //バッファサイズが2なので、連続して書き込める
    fmt.Println(<-ch)
    fmt.Println(<-ch)

    cf := make(chan int, 10)
    go fibonacci(cap(cf), cf)
    for i := range cf { //cfがcloseされるまでは読み込みを繰り返す
        fmt.Println(i)
    }

    cf2 := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-cf2)
        }
        quit <- 0
    }() //宣言と同時に呼び出し
    fibonacci2(c, quit)

    tick := time.Tick(100 * time.Millisecond)
    boom := time.After(500 * time.Millisecond)
    for {
        select {
        case <-tick:
            fmt.Println("tick.")
        case <-boom:
            fmt.Println("BOOM!")
            return
        default: //tickとboomの両方が読み込めない場合はdefaultを実行
            fmt.Println("    .")
            time.Sleep(50 * time.Millisecond)
        }
    }
}

mutex

import (
    "sync"
)

変数名 sync.Mutex 
変数名.Lock()
//LockとUnLockの間が排他制御になる
変数名.Unlock()
  • goroutine間でコミュニケーションを取る必要がないが、データのロックが必要な時に使用
import (
    "fmt"
    "sync"
    "time"
)

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
    v   map[string]int
    mux sync.Mutex
}

//SafeCounter型にメソッドをつける
func (c *SafeCounter) Inc(key string) {
    c.mux.Lock()
    //LockとUnlockで挟んでいるここが排他制御
    c.v[key]++
    c.mux.Unlock()
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
    c.mux.Lock()
    defer c.mux.Unlock() //deferでUnlockを実行することもできる
    return c.v[key]
}

func main() {
    c := SafeCounter{v: make(map[string]int)}
    for i := 0; i < 1000; i++ {
        go c.Inc("somekey")
    }

    time.Sleep(1 * time.Millisecond)
    fmt.Println(c.Value("somekey"))
}