- 投稿日:2019-02-03T23:26:07+09:00
役にたつか立たないかわからないInteliJIDEの話 【随時更新していくよ!!】
はじめに
勢いでIntelliJ All Products Pack買いました!
衝動買い楽しい。
せっかくだしちゃんと勉強しながら色々と使いこなせるようにメモします。
この機能の紹介をぜひー!とかこれ忘れてないなどあればどんどんお教えいただけますと幸いです。追記していきます!!
あと、サムライズムさんとかIntelliJとかお金もらってかいているわけではないです。色々あるけど・・・
いろんな機能があって色々と扱えるものも違ってきます。
ちなみに最強はIntelliJ IDEA Ultimate Editionです。
plugin で追加さえすればめちゃめちゃ捗ります。僕がweb開発とアプリする人間なので、それ以外にどうするべきなのかはわからないです。
値段は年間ですが、3年目まで毎年安くなっていきます。JavaScripter
webStormで要件を満たせます。
年間6300円で、三年目だと3700円です。webStormは、HTML/CSS、js, ts, coffe scriptを扱うことができます。なので、フロントエンドだろうが、バックエンド(nodeでの話)だろうがこれ一個で大丈夫です。
goに関してはプラグインでなんとかできるけど辛いみたい・・・。
そして、WebStormはかなり安いです。ただし、こいつでDBを覗き見たりするのはpluginでできなくもないですが、Data Gripを買った方がいいかと。
二郎を食べたことない人が二郎の美味しさがわからないてきな感じでData Gripを触らない限りは他のフリーツールでも満足してやっていけます。
ただし、free trialでもData Grip触ろうものなら、もう戻ってこれなくなります。pythoner
pyCharm professional Edition(PE)で要件を満たせます。
年間9600円で三年目だと5700円です**pythonはCommunity Edition(CE)って無償のバージョンがありますが、これは基本的にはデータサイエンス向けでweb開発用に転用していくのは結構無理がでます。データサイエンスだけのためならCEでよいです!jupyterも動かせます!
そして、こいつはなんとHTML/CSS, js, ts, CoffeScriptも扱うことができます!
なのでこれを買えば、フロンエンドだろうが、バックエンド(python or node)だろうがこれ一個で大丈夫です。
ただし、値段はちょっと高いです。phper
phpStormです。
年間9600円で三年目だと5700円です
webStormの上位互換ににあたります。
そして、こいつもなんとHTML/CSS, js, ts, CoffeScriptも扱うことができます!
なのでこれを買えば、フロンエンドだろうが、バックエンド(php or node)だろうがこれ一個で大丈夫です。
上位互換なので値段はやっぱちょっと高いです。Rubyer
RubyMineです。
年間9600円で三年目だと5700円です**
僕はあんまりRubyに関して明るくないです・・・。
が、Rubyを扱うことができ、こいつもなんとHTML/CSS, js, ts, CoffeScriptも扱うことができます!
ということでこれもフロンエンドだろうが、バックエンド(ruby or node)だろうがこれ一個で大丈夫です。go langer
go landです。
年間9600円で三年目だと5700円ですかっこいいですね。
これはHTML/CSS, js, tsを扱うことができます!javer
IntelliJ IDEA ultimate Edition(UE)を使うことができます。
年間16100円で三年目だと9600円です一応無償のCommunity Edition(UE)もありますが、で開店としてはHTML/CSS, js, markdownとかに対応していない点です。
java, kotolin, androidとかならCEで十分ですが、フロント周りをやろうとするならこれだとできないです。
pulanginをいれることで、多少python、Goなどに対して耐性をつけることもできます。
これだけ機能がたくさんあるためややお高い値段設定になっています。swifter Objective-Cer
AppCodeがあります。
年間9600円で三年目だと5700円ですいままでXCode使ってきたので使用感はちょっと不明です・・・。というかReact Nativeエンジニアなので、そこまで必要なものでもない。
これは、swift, Objective-C/C++のほか、HTML/CSS、JavaScriptを扱えますが、ts、Coffieはできません。
なのでReact Nativeやる人からしてみてもAppCode買うかは微妙なところですね。SQLer
Data Gripがあります。
年間9600円で三年目だと5700円です
これはめっちゃ良くてかなり重宝しています。All Product Packは買いなの?
はっきし言ってAll Product Packは結構たかいです。
年間26900円で三年目だと16100円です普通に開発しててもなんだかんだで2言語ぐらいできたら結構十分かと思います。
基本的に元を取ろうと思ったら3製品はちゃんと使わないともったいないです。
ですが、js系のものはだいたいどれもあつかえるので3製品使おうとするのは結構難しいです。たとえば、僕の場合、
- フロントエンドやる
- アプリやるけどReact Nativeなのでswift、javaはそんなに触らない
- バックエンド node or python
- データサイエンスやる
- データベースみる
とかになってくると、pyCharmとDataGripあれば十分かなと言ったところです。
それと、基本的にアップデートの速さは固有の製品の方が早いみたいなので、前線をいく人は前線をいけるような製品チョイスを心がけた方がいいかもしれません。
なので中なかなかに絶妙な値段設定をしている感じがしますね・・・。値段のまとめ
26900円コース
All Product Pack
16100円コース
IntelliJ IDEA Ultimate Edition
9600円コース
phpStorm, RubyMine, PyCharm, DataGrip, Goland, AppCode
6300円コース
webStorm
と言った感じです。なので、ここから自分に何が必要なのかを吟味して選んでいくといいかと思います!
あと、僕みたいに心配性だったり考えるのめんどくさ買ったりする人はAll Product Packを買うといいと思います。intention Actions
option + enter
いろんなことができます。
例えば、勢いでコード書いたけどファイルつくってなかった時とか、ここからさささっと作ることができます。post completion
あと出しでコードを書く機能。
みてもらったほうが早い機能。そんなにいらないんじゃないって思うかもですが、なんだかんだ言って覚えれば便利な機能です。一覧はpreference > editor > general > Postfix Completionからみれます。
独自定義もできます。
割と便利な
console.log({ a, b, });live template
色々な機能がある。アロー関数をfarrowとして追加してみた。
こちらを参考にした。タブを押すたびに引数、処理と言ったことができる。
こんな感じで追加する。ワーニングっぽいのでてたらどの言語でできているかわからないっていっているので、どの言語で使えるかを設定してあげましょう。
($PARAMS$) => ({ $BODY$ })$END$;こんな感じで定義すると、param -> body -> endにtabを押すたびに遷移する。
一応、テンプレート的なのは、範囲を選択 -> tools -> save as livetemplateっていうのを押すと、選択範囲のコードが入った状態で始められる親切設計。expand selection
いい感じに選択範囲をする機能です。
option + ↑で回数に応じて範囲が広がります。逆に戻したい場合はoption + ↓です。変数化とインライン化
名前的に変数化したかったり、戻したりを一瞬でできます。
cmd + option + v 変数化します。
cmd + option + n で戻します。parameter hint name
こんな形でパラーメーター名を表示できます。
設定はここで切り替えられます。
ショートカット
効果 コマンド Undo cmd + z Redo cmd + shift + z 検索 cmd + f or cmd + shift + f (プロジェクト全体) 置換 cmd + r or cmd + shift + r (プロジェクト全体) 複製 cmd + d プロジェクトにwindowを開く閉じる cmd + 1 ファイル作成 cmd + n コードの折りみと展開 cmd + . preferenceを開く cmd + , help or Action cmd + shift + a expand selection option + ↑ (回数で変わる) 予測補完を出す(macだとデフォで行けないので後述。ここに書いたのは自分用メモ) cmd + 4 smart補完を出す(macだとデフォで行けないので後述。ここに書いたのは自分用メモ) cmd + 5 intention Actions option + enter 変数化 cmd + option + v インライン化 cmd + option + n パラメーターヒント cmd + p 定義元ジャンプ(とにかく素晴らしい) cmd + b
- 投稿日:2019-02-03T22:09:02+09:00
Goルーチンに入門してみた
酒井潤さんがUdemyで講師をしている以下の講座のGoルーチンに関するレクチャがとてもわかりやすかったので、学習したことをまとめています。
[講座名]
現役シリコンバレーエンジニアが教えるGo入門 + 応用でビットコインのシストレFintechアプリの開発
https://www.udemy.com/go-fintech/私もまだ全部を勉強したわけではなかったですが、文法から実際のアプリ開発まで非常によくまとまっていてわかりやすいです。
Goに関する書籍やスクールが日本で広く普及されていない中でのコスパは最高だと思ってます。
Goルーチン(というか非同期処理全般)は奥が深いので、必要に応じて使っていこうかと思う次第です。
もともとGoを学ぼうと思った動機が、簡単なコマンドラインツール(シェル)的にRubyでできるような少し凝ったことを型を使って出来たらいいぐらいの感じで使いたかったので、私はそんなに使わないかもしれません。Goルーチンで実行する関数に戻り値がない場合
基本的な実行
ダサいですが、
time.sleep関数を使っています。package main import ( "fmt" "time" ) // goルーチンで実行する関数 func asyncFunc(s string) { for i := 0; i < 5; i ++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } // メイン関数 func main() { // goルーチンの関数の実行 for i := 0 ; i < 3; i ++ { str := fmt.Sprintf("Go routine (no: %v)", i) go asyncFunc(str) } // time.sleepしないとgoルーチンの実行完了よりも先にメイン関数の処理が完了してしまう。 time.Sleep(1 * time.Second) }実行結果は以下のとおりです。
Go routine (no: 2) Go routine (no: 1) Go routine (no: 0) Go routine (no: 1) Go routine (no: 0) Go routine (no: 2) Go routine (no: 0) Go routine (no: 1) Go routine (no: 2) Go routine (no: 1) Go routine (no: 2) Go routine (no: 0) Go routine (no: 1) Go routine (no: 2) Go routine (no: 0)sync.WaitGroupを使って実行完了を待つ
time.sleep関数で待つやり方はどのくらいsleepすればいいかわからないので、sync.WaitGroupでGoルーチンで実行する関数の完了を待つ方法。package main import ( "fmt" "sync" "time" ) // goルーチンで実行する関数 func asyncFunc(s string, wg*sync.WaitGroup) { defer wg.Done() // WaitGroupを最後に完了しないといけない。 for i := 0; i < 5; i ++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } // メイン関数 func main() { // goルーチンで非同期に実行される処理を待つために // WaitGroupを使う。 var wg sync.WaitGroup // goルーチンの関数の実行 for i := 0 ; i < 3; i ++ { wg.Add(1) // goルーチンを実行する関数分だけAddする。 str := fmt.Sprintf("Go routine (no: %v)", i) go asyncFunc(str, &wg) } // goルーチンで実行される関数が終了するまで待つ。 wg.Wait() }実行結果は以下のとおりです。
Go routine (no: 2) Go routine (no: 0) Go routine (no: 1) Go routine (no: 1) Go routine (no: 2) Go routine (no: 0) Go routine (no: 0) Go routine (no: 2) Go routine (no: 1) Go routine (no: 0) Go routine (no: 1) Go routine (no: 2) Go routine (no: 2) Go routine (no: 1) Go routine (no: 0)Goルーチンで実行する関数に戻り値がある場合
チャネル
チャネルを介して結果を受け取るためにチャネルを使います。
goルーチンの関数にチャネルを渡し、処理を非同期で実行し、結果を受け取る際には、ブロッキングして結果を受け取っています。package main import "fmt" // goルーチンの関数 func asyncFunc(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum } func main() { // スライスの生成 s := []int{1, 2, 3, 4, 5} // チャネルの生成 // チャネルを介してgoルーチンの関数とやりとりします。 c := make(chan int) // goルーチンの関数実行。 // 引数にチャネルを渡している。 go asyncFunc(s, c) go asyncFunc(s, c) // 結果を受け取る x := <-c // ここで処理がブロッキングされる fmt.Println(x) y := <-c // ここで処理がブロッキングされる fmt.Println(y) }バッファーチャネル
チャネルにバッファサイズを定義することもできる。
goルーチンの関数側でチャネルをクローズする必要があります。package main import "fmt" func asyncFunc(s []int, c chan int) { sum := 0 for _, v := range s { sum += v c <- sum // チャネルに演算結果を渡している } close(c) // closeで閉じること! } func main() { s := []int{1, 2, 3, 4, 5} c := make(chan int, len(s)) // チャネルのバッファサイズ: 5 go asyncFunc(s, c) // チャネルのバッファサイズ分だけループ処理 for i := range c { fmt.Println(i) } }結果は以下のとおり。
1 3 6 10 15上記のコードでチャネルのクローズ処理
close(c)を忘れると、以下のようなデッドロックエラーが発生する。1 3 6 10 15 fatal error: all goroutines are asleep - deadlock!Goルーチンを使った代表的なメッセージングパターン
Producer/Consumerパターン
前述の知識を流用した応用的なパターンです。
sync.WaitGroupとチャネルを使ってProducer/Consumerパターンを実装。チャネルがメッセージキューのような役割を果たしています。
ひとつのチャネル(キュー相当)をProducerとConsumerで共有していることがポイントです。package main import ( "fmt" "sync" "time" ) // Producer func producer(ch chan int, i int) { ch <- i * 2 } // Consumer func consumer(no int, ch chan int, wg *sync.WaitGroup) { for i := range ch { func() { defer wg.Done() time.Sleep(100 * time.Millisecond) fmt.Printf("process no: %v result: %v\n", no, i*1000) }() } } func main() { var wg sync.WaitGroup ch := make(chan int) // Producerでメッセージを生成する for i := 0; i < 100; i ++ { wg.Add(1) go producer(ch, i) } // Consumerがメッセージをポーリング // consumerの数を3つに設定 for i := 0; i < 3; i ++ { go consumer(i, ch, &wg) } wg.Wait() // 10回Addされた分だけ待つ。 close(ch) // 閉じないとConsumerが待ち続ける fmt.Println("Done") }process 2000 process 0 process 8000 process 4000 process 6000 process 10000 process 12000 process 14000 process 16000 process 18000 Donefan-out/fan-inパターン
パイプライン(fan-out/fan-in)を使った並列処理も以下のように実装できます。
package main import "fmt" // stage1 fan-in // firstというチャネルにメッセージを詰める func producer(first chan int) { defer close(first) // firstチャネルにメッセージを詰め終わったらチャネルをクローズ for i := 0; i < 10; i ++ { first <- i } } // stage2 fan-out, fan-in // firstチャネルからメッセージを取り出し、 // 演算後にsecondチャネルにメッセージを詰める。 // <-はなくてもいい。 func multi2(first <-chan int, second chan<- int) { defer close(second) // secondチャネルにメッセージを詰め終わったらチャネルをクローズ for i := range first { second <- i * 2 } } // stage3 fan-out, fan-in // secondチャネルからメッセージを取り出し、 // 演算後にthirdチャネルにメッセージを詰める。 func multi4(second <-chan int, third chan<- int) { defer close(third) for i := range second { third <- i * 2 } } func main() { // チャネルの生成 first := make(chan int) second := make(chan int) third := make(chan int) go producer(first) go multi2(first, second) go multi4(second, third) // パイプラインの結果をthirdチャネルから取り出して表示する for result := range third { fmt.Println(result) } }結果は以下のとおりです。
0 4 8 12 16 20 24 28 32 36for-selectを使ったチャネルからのメッセージ受信
for-selectを使って同時にブロッキングせずにチャネルからメッセージを受信することができます。
処理の内容・結果の異なるgoルーチンの関数の結果を同時に処理するためのパターンかと思います。package main import ( "fmt" "time" ) func goroutine1(ch chan string) { for { ch <- "packet from 1" time.Sleep(2 * time.Second) } } func goroutine2(ch chan int) { for { ch <- 100 time.Sleep(1 * time.Second) } } func main() { c1 := make(chan string) c2 := make(chan int) go goroutine1(c1) go goroutine2(c2) // selectを使って同時にブロッキングせずに受信する。 for { select { case msg1 := <- c1: fmt.Println(msg1) case msg2 := <- c2: fmt.Println(msg2) } } }100 packet from 1 100 packet from 1 100 100 packet from 1 100 100 packet from 1 100 100 packet from 1 100 100 packet from 1 100 100 packet from 1 100 100 packet from 1 100 100 packet from 1 100 ...SyncのMutex
複数スレッドからの同時更新、同時参照時の不整合、デッドロックを回避するために、SyncのMutexを利用できます。
package main import ( "fmt" "sync" "time" ) // mutexを持つ構造体を定義する。 type Counter struct { v map[string]int mux sync.Mutex // mutex } // インクリメント func (c *Counter) Inc(key string) { c.mux.Lock() // ロックする。 defer c.mux.Unlock() // ロックを解除する。 c.v[key]++ } // 値をゲットする。 func (c *Counter) Value(key string) int { c.mux.Lock() defer c.mux.Unlock() return c.v[key] } func main() { /* 以下のgoルーチンは同時に書き込むとエラーが発生します。 c := make(map[string]int) go func() { for i := 0; i< 10; i++ {} c["key"] += 1 }() go func() { for i := 0; i< 10; i++ {} c["key"] += 1 }() time.Sleep(1 * time.Second) fmt.Println(c, c["key"]) */ // 以下はMutexを使って不整合が発生しないようにした書き方。 c := Counter{v: make(map[string]int)} go func() { for i := 0; i< 10; i++ { c.Inc("key") } }() go func() { for i := 0; i< 10; i++ { c.Inc("key") } }() time.Sleep(1 * time.Second) fmt.Println(c, c.Value("key")) }{map[key:20] {0 0}} 20
- 投稿日:2019-02-03T18:32:21+09:00
はじめてのgoa�
goaについて
Goを使ってREST APIを用意したいときに利用できるマイクロサービス用のフレームワークの一つです。
ビジネスロジック中心で開発ができる、swaggerのドキュメントが自動生成されるなどの利点があります。
goaのgithubはこちらにあります。サンプルについて
今回用意したサンプルはこちらです。
環境設定等
mercurialのインストール依存関係の解決にあたり、
depを使ったが、Macでdep ensureをすると、なぜかフリーズしたので、pstreeで確認すると下記の箇所で固まっていました。$ pstree 69752 -+= 69752 xxx dep ensure -v \-+= 71086 xxx /Applications/Xcode.app/Contents/Developer/usr/bin/git ls-remote ssh://git@bitbucket.org/pkg/inflect \--- 71087 xxx /usr/bin/ssh git@bitbucket.org git-upload-pack '/pkg/inflect'調べたところ、
mercurialが必要とのことだったので、brewを実行してインストールしました。
brew install mercurial
goaとdepのインストール
goaとdepをgo getする。$ go get -u github.com/goadesign/goa/... $ go get -u github.com/golang/dep/cmd/dep
goaを使ったREST API設計API、Resource、MediaTypeの定義
designの定義1つのgoファイルで集約しても問題ないのですが、便宜上複数のファイルの分割しました。
APIのベースとなる基本的な定義です。
./design/api_base.gopackage design import ( . "github.com/goadesign/goa/design/apidsl" ) var _ = API("goa-sample", func() { Title("The Sample API") Description("A simple goa service") Version("v1") Scheme("http", "https") BasePath("/api/v1") Consumes("application/json") Produces("application/json") Host("localhost:8080") Origin("http://localhost:8080/swagger", func() { Expose("X-Time") Methods("GET", "POST", "PUT", "PATCH", "DELETE") MaxAge(600) Credentials() }) })
swaggerの定義です。
この例では、http://localhost:8080/swagger-uiにアクセスすると、swaggerのドキュメントを閲覧できます。
なお、swagger-uiをダウンロードし、public/swagger-ui/distに配置する必要があります。./design/swagger.gopackage design import ( . "github.com/goadesign/goa/design/apidsl" ) // Swagger routing var _ = Resource("swagger", func() { Origin("*", func() { Methods("GET") }) Files("/swagger.json", "swagger/swagger.json") Files("/swagger.yaml", "swagger/swagger.yaml") Files("/swagger-ui/*filepath", "public/swagger-ui/dist") })続いて、レスポンスデータの型を定義します。
design/media_types.gopackage design import ( "time" . "github.com/goadesign/goa/design" . "github.com/goadesign/goa/design/apidsl" ) var MediaSamples = MediaType("application/vnd.samples+json", func() { Description("sample list") Attribute("id", Integer, "id", func() { Example(1) }) Attribute("name", String, "名前", func() { Example("サンプル1") }) Attribute("created_at", DateTime, "作成日", func() { loc, _ := time.LoadLocation("Asia/Tokyo") Example(time.Date(2019, 01, 31, 0, 0, 0, 0, loc).Format(time.RFC3339)) }) Attribute("updated_at", DateTime, "更新日", func() { loc, _ := time.LoadLocation("Asia/Tokyo") Example(time.Date(2019, 01, 31, 12, 30, 50, 0, loc).Format(time.RFC3339)) }) Required("id", "name", "created_at", "updated_at") View("default", func() { Attribute("id") Attribute("name") Attribute("created_at") Attribute("updated_at") }) }) var MediaSample = MediaType("application/vnd.sample+json", func() { Description("sample detail") Attribute("id", Integer, "sample id", func() { Example(1) }) Attribute("user_id", Integer, "user id", func() { Example(1) }) Attribute("name", String, "名前", func() { Example("サンプル1") }) Attribute("detail", String, "詳細", func() { Example("サンプル1の詳細") }) Attribute("created_at", DateTime, "作成日", func() { loc, _ := time.LoadLocation("Asia/Tokyo") Example(time.Date(2019, 01, 31, 0, 0, 0, 0, loc).Format(time.RFC3339)) }) Attribute("updated_at", DateTime, "更新日", func() { loc, _ := time.LoadLocation("Asia/Tokyo") Example(time.Date(2019, 01, 31, 12, 30, 50, 0, loc).Format(time.RFC3339)) }) Required("id", "user_id", "name", "detail", "created_at", "updated_at") View("default", func() { Attribute("id") Attribute("user_id") Attribute("name") Attribute("detail") Attribute("created_at") Attribute("updated_at") }) })最後にAPIの引数とレスポンスの内容を記載します。
package design import ( . "github.com/goadesign/goa/design" . "github.com/goadesign/goa/design/apidsl" ) var _ = Resource("samples", func() { BasePath("/samples") Action("list", func() { Description("複数") Routing( GET("/"), ) Params(func() { Param("user_id", Integer, "user id", func() { Example(1) }) }) Response(OK, CollectionOf(MediaSamples)) Response(NotFound) Response(BadRequest, ErrorMedia) }) Action("show", func() { Description("単数") Routing( GET("/:id"), ) Params(func() { Param("user_id", Integer, "user id", func() { Example(1) }) Param("id", Integer, "sample data id", func() { Example(123) }) }) Response(OK, CollectionOf(MediaSample)) Response(NotFound) Response(Unauthorized) Response(BadRequest, ErrorMedia) }) Action("add", func() { Description("追加") Routing( POST("/"), ) Payload(func() { Attribute("user_id", String, "user id", func() { Example("12345") }) Attribute("name", String, "name of sample", func() { Example("sample1") }) Attribute("detail", String, "detail of sample", func() { Example("sample1の詳細") }) Required("user_id", "name", "detail") }) Response(OK, CollectionOf(MediaSample)) Response(NotFound) Response(Unauthorized) Response(BadRequest, ErrorMedia) }) Action("delete", func() { Description("削除") Routing( DELETE("/:id"), ) Params(func() { Param("id", Integer, "sample id", func() { Example(1) }) }) Response(NoContent) Response(NotFound) Response(Unauthorized) Response(BadRequest, ErrorMedia) }) Action("update", func() { Description("更新") Routing( PUT("/:id"), ) Params(func() { Param("id", Integer, "sample id") }) Payload(func() { Param("name", String, "name of sample", func() { Example("sample1") }) Param("detail", String, "detail of sample", func() { Example("sample1") }) Required("name", "detail") }) Response(NoContent) Response(NotFound) Response(BadRequest, ErrorMedia) }) })
goagenの実行下記コマンドを実行すると諸々ファイルが自動生成されます。
$ goagen bootstrap -d github.com/hiroykam/goa-sample/design
sample.goとswagger.goを./controllerに移動させます。
Modelの定義自動生成する場合
まずは、
./design/models.goを用意します。
idをstringにしたい場合、gorma.UUIDとします。
deleted_atを指定すると、gormでdeleteを実行した際、論理削除になります。./design/models.gopackage design import ( "github.com/goadesign/gorma" . "github.com/goadesign/gorma/dsl" ) var _ = StorageGroup("goa-sample", func() { Description("Sample Model") Store("MySQL", gorma.MySQL, func() { Description("MySQL models") Model("Sample", func() { RendersTo(MediaSample) Description("sample table") Field("id", gorma.Integer) Field("user_id", gorma.Integer) Field("name", gorma.String) Field("detail", gorma.String) Field("created_at", gorma.Timestamp) Field("updated_at", gorma.Timestamp) Field("deleted_at", gorma.NullableTimestamp) }) }) })$ goagen --design=github.com/hiroykam/goa-sample/design gen --pkg-path=github.com/goadesign/gorma models/sample.go models/sample_helper.go実行すると下記ファイルが生成されますが、上位層にエラーをそのまま伝搬している、このサンプルの例ですと
user_idで絞り込むGetとListを別に用意する必要があるなどの手間は発生します。
またmodelを追加し、上記コマンドを実行しますとsample.goが新規に作成されてしまいますので、./model/sample_extention.goといったように別ファイルで定義する必要があります。models/sample.go// Code generated by goagen v1.3.1, DO NOT EDIT. // // API "goa-sample": Models // // Command: // $ goagen // --design=github.com/hiroykam/goa-sample/design // --out=$(GOPATH)/src/github.com/hiroykam/goa-sample // --version=v1.3.1 package models import ( "context" "github.com/goadesign/goa" "github.com/hiroykam/goa-sample/app" "github.com/jinzhu/gorm" "time" ) // sample table type Sample struct { ID int `gorm:"primary_key"` Detail string Name string UserID int CreatedAt time.Time // timestamp DeletedAt *time.Time // nullable timestamp (soft delete) UpdatedAt time.Time // timestamp } // TableName overrides the table name settings in Gorm to force a specific table name // in the database. func (m Sample) TableName() string { return "samples" } // SampleDB is the implementation of the storage interface for // Sample. type SampleDB struct { Db *gorm.DB } // NewSampleDB creates a new storage type. func NewSampleDB(db *gorm.DB) *SampleDB { return &SampleDB{Db: db} } // DB returns the underlying database. func (m *SampleDB) DB() interface{} { return m.Db } // SampleStorage represents the storage interface. type SampleStorage interface { DB() interface{} List(ctx context.Context) ([]*Sample, error) Get(ctx context.Context) (*Sample, error) Add(ctx context.Context, sample *Sample) error Update(ctx context.Context, sample *Sample) error Delete(ctx context.Context) error ListSample(ctx context.Context) []*app.Sample OneSample(ctx context.Context) (*app.Sample, error) } // TableName overrides the table name settings in Gorm to force a specific table name // in the database. func (m *SampleDB) TableName() string { return "samples" } // CRUD Functions // Get returns a single Sample as a Database Model // This is more for use internally, and probably not what you want in your controllers func (m *SampleDB) Get(ctx context.Context) (*Sample, error) { defer goa.MeasureSince([]string{"goa", "db", "sample", "get"}, time.Now()) var native Sample err := m.Db.Table(m.TableName()).Where("").Find(&native).Error if err == gorm.ErrRecordNotFound { return nil, err } return &native, err } // List returns an array of Sample func (m *SampleDB) List(ctx context.Context) ([]*Sample, error) { defer goa.MeasureSince([]string{"goa", "db", "sample", "list"}, time.Now()) var objs []*Sample err := m.Db.Table(m.TableName()).Find(&objs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } return objs, nil } // Add creates a new record. func (m *SampleDB) Add(ctx context.Context, model *Sample) error { defer goa.MeasureSince([]string{"goa", "db", "sample", "add"}, time.Now()) err := m.Db.Create(model).Error if err != nil { goa.LogError(ctx, "error adding Sample", "error", err.Error()) return err } return nil } // Update modifies a single record. func (m *SampleDB) Update(ctx context.Context, model *Sample) error { defer goa.MeasureSince([]string{"goa", "db", "sample", "update"}, time.Now()) obj, err := m.Get(ctx) if err != nil { goa.LogError(ctx, "error updating Sample", "error", err.Error()) return err } err = m.Db.Model(obj).Updates(model).Error return err } // Delete removes a single record. func (m *SampleDB) Delete(ctx context.Context) error { defer goa.MeasureSince([]string{"goa", "db", "sample", "delete"}, time.Now()) var obj Sample err := m.Db.Delete(&obj).Where("").Error if err != nil { goa.LogError(ctx, "error deleting Sample", "error", err.Error()) return err } return nil }models/sample_helper.go// Code generated by goagen v1.3.1, DO NOT EDIT. // // API "goa-sample": Model Helpers // // Command: // $ goagen // --design=github.com/hiroykam/goa-sample/design // --out=$(GOPATH)/src/github.com/hiroykam/goa-sample // --version=v1.3.1 package models import ( "context" "github.com/goadesign/goa" "github.com/hiroykam/goa-sample/app" "github.com/jinzhu/gorm" "time" ) // MediaType Retrieval Functions // ListSample returns an array of view: default. func (m *SampleDB) ListSample(ctx context.Context) []*app.Sample { defer goa.MeasureSince([]string{"goa", "db", "sample", "listsample"}, time.Now()) var native []*Sample var objs []*app.Sample err := m.Db.Scopes().Table(m.TableName()).Find(&native).Error if err != nil { goa.LogError(ctx, "error listing Sample", "error", err.Error()) return objs } for _, t := range native { objs = append(objs, t.SampleToSample()) } return objs } // SampleToSample loads a Sample and builds the default view of media type Sample. func (m *Sample) SampleToSample() *app.Sample { sample := &app.Sample{} sample.CreatedAt = m.CreatedAt sample.Detail = m.Detail sample.ID = m.ID sample.Name = m.Name sample.UpdatedAt = m.UpdatedAt sample.UserID = m.UserID return sample } // OneSample loads a Sample and builds the default view of media type Sample. func (m *SampleDB) OneSample(ctx context.Context) (*app.Sample, error) { defer goa.MeasureSince([]string{"goa", "db", "sample", "onesample"}, time.Now()) var native Sample err := m.Db.Scopes().Table(m.TableName()).Where("").Find(&native).Error if err != nil && err != gorm.ErrRecordNotFound { goa.LogError(ctx, "error getting Sample", "error", err.Error()) return nil, err } view := *native.SampleToSample() return &view, err }手動で頑張る場合
サンプルでは
modelsとserviceを用意しました。
diも用意すべきかもしれません。Controllerの実装
下記コマンドで雛形が作成されます。
コマンド再実行により、ここに追記したコードは削除されません。$ goagen controller -d github.com/hiroykam/goa-sample/design --out=./controllerResource、MediaTypeの変更
Resource、MediaTypeを変更した際、下記を実行します。
$ goagen app -d github.com/hiroykam/goa-sample/design $ goagen swagger -d github.com/hiroykam/goa-sample/design $ goagen controller -d github.com/hiroykam/goa-sample/design --out=./controller
goaの起動
makeサンプルで下記のコマンドで、
docker-compose buildします。$ make docker-buildビルド完了後、下記のコマンドで起動できます。
$ make docker-up
swagger-ui起動後、
http://localhost:8080/swagger-ui/にアクセスすると下記が表示されます。
REST API
Postmanを使いました。その他
goagenに失敗する
goagenを実行すると、下記のようなエラーが発生する時があります。exit status 1 missing API definition, make sure design is properly initialized make: *** [_controller] Error 1
githubのissueで散見されていましたが、簡単な解決方法はvendorディレクトリを一旦削除すると解決しました。
depを使っている場合、goagenに失敗するissueを拝見する限りですと、
depの問題といった記載があり、Gopkg.tomlに下記を追記すると解決できる。
depを使わないほうが良いのでしょうか・・・[[override]] name = "github.com/satori/go.uuid" revision = "master"エラーの内容が分かりづらい
例えば、
./design/media_types.goで下記のような属性を定義します。Attribute("created_at", DateTime, "作成日", func() { Example("2019-01-01") })そこで、
goagenしても、下記のようなエラーが表示されますが、内容からRFC3339フォーマットに準拠していないのが原因であることが分かりませんでした。$ goagen app -d github.com/hiroykam/goa-sample/design exit status 1 [design/media_types.go:24] example value "2019-01-01" is incompatible with attribute of type stringlog出力が扱いづらい
main.goでこのようにstodoutにJSON形式のログ出力をしたかったが、reqest IDなどの情報まで出力することができませんでした。
また、goa内部で出力するログは別のloggerが使われているため、思うようなログ出力が期待できませんでした。logger := log15.New(log15.Ctx{"module": "goa-sample"}) logger.SetHandler(log15.StreamHandler(os.Stdout, log15.JsonFormat())) service.WithLogger(goalog15.New(logger))そのため、サンプルでは
json出力するloggerを別途用意しました。trailing slashの扱いについて
URLの末尾に
/ありとなしでAPIを実行すると、意図していないAPIが呼び出されるケースがありました(調査中)。参考
こちらの記事をベースに勉強をさせていただきました。
今後
- テストコードを書く
- cicrleciなどの環境を整える
- jwtで認証
- Makefile、READMEの修正・更新
- React/ReduxからAPIを呼び出す
gorm.ErrRecordNotFoundが検出できない原因調査
- 投稿日:2019-02-03T15:19:28+09:00
何故Go言語を私は使いたいのか? 良い点と悪い点を雑にまとめる
前書き
選択肢は多いし、自分がやりたいことを実現するには"どちらが適切か"を踏まえると、
言語を選ぶことはなかなか難しいものです。Go言語で開発をやろうと思った時に、何故Go言語でやりたいのか、
また、どういう点でイケてないと思っているのかを
思考の整理のためにまとめた記事になります。この記事は、ベストなプログラミング言語だと主張する記事ではありません。
ご自身でで確かな情報に基づいて言語の調査をする際の助けになれば幸いです。良い点
1. パフォーマンス
- Ruby, pythonなどと比較すると良い
2. デプロイの容易さ
- 多くの場合、単一ファイルで良い。
ただしコンテナをデプロイできる時代なので優位性は(ry3. 多くの標準ライブラリ
- net, encoding, html, os, etc...
4. 周辺ツールの充実
- Gofmtによるコードフォーマットの強制
5. シンプルな言語仕様
- 学習コストが比較的少ない
6. スレッドモデルによる並列処理
- イベントモデルは辛い (主観)
7. コンパイルによるバグの見つけやすさ
- go vetでコードの静的解析を行うことができる(標準ツール)
8. コンパイルの速さ
- C++, javaと比較 するとコンパイルが早い
9. 移植性が良い
- 内部エンコーディングは
utf-810. Googleが作ってる安心感
悪い点
1. シンプルな言語実装だが、ハマりどころが少ないわけではない 1 2
- nilインターフェースがnilじゃない
- 変数のシャドーイングの挙動 etc...
2. シンプルすぎてプログラミング言語として色々不足を感じる(主観)
- 例外がない
- 継承がない
- Genericsがない(次のverで追加?) etc...
3. 学んだ所で言語として特に新しい発見があまりない 3
- 1970年代で止まった言語に見える
- 40年間のプログラミング言語の研究を排除した最新の言語
- Goはブルーカラー(普通)の開発者のための言語
- ただし開発者には2パターンいる
- できるだけ簡単かつ迅速に作業を進めたいという開発者
- 最新の「革新的」言語を追いかけている開発者
なぜGo言語は設計が悪いのか – Go愛好者の見地から https://postd.cc/why-go-is-a-poorly-designed-language/ ↩
7 common mistakes in Go and when to avoid them by Steve Francia (Docker) https://www.youtube.com/watch?v=29LLRKIL_TI ↩
Why Everyone Hates Go (https://npf.io/2014/10/why-everyone-hates-go/) (かなり古い) ↩
- 投稿日:2019-02-03T14:51:50+09:00
スターティングGo言語�7章のメモ - os/time
Goの標準パッケージを入門レベルで学ぼうと思って書き残したメモです。
スターティングGo言語の7章の以下のパッケージについて記載しています。
単なる写経に近いクソ記事ですw。
- osパッケージ
- timeパッケージ
osパッケージ
import "os"ホスト名の取得
func main() { host, _ := os.Hostname() fmt.Println(host) }環境変数
func main() { // 環境変数一覧を表示 for _, env := range os.Environ(){ fmt.Println(env) } // 環境変数の名前を指定して取得 fmt.Println(os.Getenv("HOME")) }プログラムの終了
func main() { // 終了コード1で終了する os.Exit(1) }プロセス情報
func main() { fmt.Printf("プロセスID: %v\n", os.Getpid()) fmt.Printf("親プロセスID: %v\n",os.Getppid()) fmt.Printf("ユーザID: %v\n", os.Getuid()) fmt.Printf("グループID: %v\n", os.Getgid()) }log.Fatal
func main() { _, err := os.Open("README.md") if err != nil { log.Fatal(err) } }コマンドライン引数
func main() { for _, v := range os.Args { fmt.Println(v) } }ファイル操作
読み込み専用ファイルのオープン
func main() { f, err := os.Open("README.md") if err != nil { log.Fatal(err) } defer f.Close() }os.File
func main() { f, err := os.Open("README.md") if err != nil { log.Fatal(err) } /* []byte型のスライスにファイルの内容を読み込む */ bs1 := make([]byte,128) n ,err := f.Read(bs1) // nは実際に読み込んだバイト数 if err != nil { log.Fatalln(err) } fmt.Println(n) /* ファイルのオフセットを指定して読み込む */ bs2 := make([]byte, 128) n2, err := f.ReadAt(bs2, 10) // 10バイト目から読み込む if err != nil { log.Println(err) } fmt.Println(n2) /* ファイルのステータス取得 */ fi, err := f.Stat() if err != nil { log.Fatal(err) } fmt.Println(fi.Name()) // ファイル名 fmt.Println(fi.Size()) // ファイルサイズ fmt.Println(fi.Mode()) // ファイルのモード fmt.Println(fi.ModTime()) // ファイルの最終更新時間 fmt.Println(fi.IsDir()) // ディレクトリかどうか }実際にファイルを読み込む処理を書くと、こんなかんじでしょうか。
func readFile(filepath string) (lines []string) { f, err := os.Open(filepath) if err != nil { log.Fatal(err) } buf := make([]byte, 1024) for { n, err := f.Read(buf) if err != nil { log.Fatal(err) } if err == io.EOF { // EOFの場合抜ける break } str := string(buf[:n]) fmt.Println(str) lines = append(lines, str) } return } func main() { filepath := "README.md" for _, line := range readFile(filepath) { fmt.Println(line) } }新規ファイルの作成
func main() { f, err := os.Create("NewFile.txt") // ファイル名を指定して新規作成 if err != nil { log.Fatal(err) } f.Write([]byte("Hello, World\n")) // ファイルに[]byte型の内容を書き込み f.WriteAt([]byte("Golang"), 7) // オフセットを指定して書き込み f.Seek(0, io.SeekEnd) // ファイルの末尾にオフセットを移動 f.WriteString("Year") // 文字列をファイルに書き込み }ファイルオープン詳細
os.OpenFileを使う。func main() { f, err := os.OpenFile("README.md", os.O_RDONLY, 0666) if err != nil { log.Fatal(err) } fmt.Println(f.Name()) defer f.Close() }
フラグ 意味 O_RDONLY 読み込み専用 O_WRONLY 書き込み専用 O_RDWR 読み書き可能 O_APPEND ファイルの末尾に追記 O_CREATE ファイルが存在しなければ新規作成 O_TRUNC 可能であればファイルの内容をオープン時に空にする ファイルの削除
func main() { if err := os.Remove("NewFile.txt"); err != nil { log.Fatal(err) } }ファイル名の変更と移動
func main() { if err := os.Rename("NewFile.txt", "BackupFile.txt"); err != nil { log.Fatal(err) } }ディレクトリ操作
カレントディレクトリ
func main() { dir, err := os.Getwd() if err != nil { log.Fatal(err) } fmt.Println(dir) }ディレクトリの読み込み
func main() { f, err := os.Open(".") if err != nil { log.Fatal(err) } defer f.Close() // カレントディレクトリ下のディレクトリ名を列挙 fis, err := f.Readdir(0) for _, fi := range fis { if fi.IsDir() { fmt.Println(fi.Name()) } } }ディレクトリの作成
func main() { // カレントディレクトリ配下にディレクトリを作成 if err := os.Mkdir("foo", 0775); err != nil { log.Fatal(err) } // カレントディレクトリ配下にディレクトリを一括作成 // mkdir -p に相当 if err := os.MkdirAll("foo/bar/baz", 0755); err != nil { log.Fatal(err) } }ディレクトリの削除
func main() { if err := os.Remove("NewFile.txt"); err != nil { log.Fatal(err) } if err := os.RemoveAll("foo") err != nil { log.Fatal(err) } }その他のファイル操作
シンボリックリンクの操作
func main() { // シンボリックリンクbar.txtを作成 if err := os.Symlink("foo.txt", "bar.txt"); err != nil { fmt.Println(err) } // シンボリックリンクのリンク先を読み込む path, err := os.Readlink("bar.txt") if err != nil { log.Fatal(err) } fmt.Println(path) }timeパッケージ
import "time"現在の時刻取得
func main() { t := time.Now() fmt.Println(t) }指定した時刻の生成
func main() { t := time.Date(2019, 2, 3, 11, 50, 0, 0, time.Local) fmt.Println(t) fmt.Println(t.Year()) fmt.Println(t.Month()) fmt.Println(t.Day()) fmt.Println(t.Weekday()) fmt.Println(t.Hour()) fmt.Println(t.Minute()) fmt.Println(t.Second()) fmt.Println(t.Nanosecond()) fmt.Println(t.Zone()) }
メソッド 戻り値の型 意味 Year int 年 YearDay int 1~366 Month time.Month 月 Weekday time.Weekday 曜日 Day int 1~31 Hour int 0~23 Minute int 0~59 Second int 0~59 NanoSecond int ナノ秒 Zone string, int タイムゾーンとオフセット秒 時刻間隔の表現
func main() { fmt.Println(time.Hour) // 1h0m0s fmt.Println(time.Minute) // 1m0s fmt.Println(time.Second) // 1s fmt.Println(time.Millisecond) // 1ms fmt.Println(time.Microsecond) // 1µs fmt.Println(time.Nanosecond) // 1ns }文字列からtime.Durationを生成
func main() { // 文字列からtime.Durationを生成 duration, err := time.ParseDuration("2h30m") if err != nil { log.Fatal(err) } fmt.Printf("%v : %T", duration, duration) // 2h30m0s : time.Duration }time.Durationの計算
func main() { t := time.Now() fmt.Println(t) // 2019-02-03 12:01:38.307595 +0900 JST m=+0.000568779 t = t.Add(2*time.Minute + 15*time.Microsecond) fmt.Println(t) // 2019-02-03 12:03:38.30761 +0900 JST m=+120.000583779 }時刻の差分を取得
func main() { t0 := time.Date(2020, 2,1,0,0,0,0,time.Local) t1 := time.Now() d := t0.Sub(t1) fmt.Println(d) // 8699h55m41.548192s }時刻の比較
func main() { t0 := time.Now() t1 := t0.Add(24 * time.Hour) fmt.Println(t0) // 2019-02-03 12:09:01.320207 +0900 JST m=+0.000428654 fmt.Println(t1) // 2019-02-04 12:09:01.320207 +0900 JST m=+86400.000428654 // 時刻の比較 fmt.Println(t1.Before(t0)) // false fmt.Println(t1.After(t0)) // true }年月日の増減
func main() { t0 := time.Date(2019,2,1,0,0,0,0,time.Local) fmt.Println(t0) // 2019-02-01 00:00:00 +0900 JST // 1年増やす t1 := t0.AddDate(1, 0,0) fmt.Println(t1) // 2020-02-01 00:00:00 +0900 JST // 1ヶ月減らす t2 := t0.AddDate(0,-1,0) fmt.Println(t2) // 2019-01-01 00:00:00 +0900 JST }文字列からの時刻生成
func main() { // 第一引数にフォーマットを指定 // 第二引数がパース対象の文字列 t, err := time.Parse(time.RFC822, "02 Jan 06 15:04 MST") if err != nil { log.Fatal(err) } fmt.Println(t) // 2006-01-02 15:04:00 +0000 MST }第一引数のフォーマットは以下の通り定義されている。
const ( ANSIC = "Mon Jan _2 15:04:05 2006" UnixDate = "Mon Jan _2 15:04:05 MST 2006" RubyDate = "Mon Jan 02 15:04:05 -0700 2006" RFC822 = "02 Jan 06 15:04 MST" RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone RFC850 = "Monday, 02-Jan-06 15:04:05 MST" RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone RFC3339 = "2006-01-02T15:04:05Z07:00" RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" Kitchen = "3:04PM" // Handy time stamps. Stamp = "Jan _2 15:04:05" StampMilli = "Jan _2 15:04:05.000" StampMicro = "Jan _2 15:04:05.000000" StampNano = "Jan _2 15:04:05.000000000" )自分でフォーマットを定義する場合は、例えば以下のようにする。
注意することとして、フォーマット指定のタイムスタンプは2006年1月2日 15:04:05で統一されていないといけない。このフォーマット指定に疑問を抱いたので調べたところ、アメリカの時刻の順番らしい。”1月2日午後3時4分5秒2006年”でとなっていて、1,2,3,4,5,6と並んでいる。
func main() { t, err := time.Parse("2006年1月2日 15時04分05秒", "2019年2月3日 21時15分00秒") if err != nil { log.Fatal(err) } fmt.Println(t) // 2019-02-03 21:15:00 +0000 UTC }時刻からの文字列生成
func main() { t := time.Now() f1 := t.Format(time.RFC822) f2 := t.Format(time.RFC3339) f3 := t.Format("2006年1月2日 15時43分28秒") f4 := t.Format("2006/01/02") fmt.Printf("f1: %v (%T)\n", f1, f1) // f1: 03 Feb 19 14:05 JST (string) fmt.Printf("f2: %v (%T)\n", f2, f2) // f2: 2019-02-03T14:05:05+09:00 (string) fmt.Printf("f3: %v (%T)\n", f3, f3) // f3: 2019年2月3日 14時52分38秒 (string) fmt.Printf("f4: %v (%T)\n", f4, f4) // f4: 2019/02/03 (string) }時刻のUTC変換
func main() { t := time.Now() utc := t.UTC() // UTCに変換 fmt.Printf("utc : %v (%T)\n", utc, utc) // utc : 2019-02-03 05:07:07.324682 +0000 UTC (time.Time) }時刻のローカルタイム変換
func main() { t := time.Now() jst := t.Local() // ローカルタイムに変換 fmt.Printf("jst : %v (%T)\n", jst, jst) // jst : 2019-02-03 14:08:24.109525 +0900 JST (time.Time) }UNIX時間との相互変換
func main() { t := time.Now() unix := t.Unix() fmt.Printf("unix : %v (%T)\n", unix, unix) // unix : 1549170553 (int64) }指定時間のスリープ
func main() { for i := 0; i < 10; i ++ { fmt.Println(i) time.Sleep(100 * time.Millisecond) // 100msecスリープ } }time.Tick
time.Tickは、指定した時間間隔ごとに現在時刻を表すtime.Time型の値が送信されるチャネルを生成する。func main() { // 3秒間隔で現在の時刻を送信するチャネルを定義 ch := time.Tick(3 * time.Second) // 無限ループ for { t := <-ch fmt.Println(t) // 3秒間隔で表示される } }time.After
time.Afterは、チャネルに対して指定した時間間隔後に一度だけ現在時刻を表すtime.Timeを送信する。func main() { // 5秒後に時刻を送信するチャネル ch := time.After(5 * time.Second) // 5秒後に表示される v := <-ch fmt.Println(v) }A Tour of Goのサンプルでは
time.Tickとtime.Afterを組み合わせてselect-for文で以下のような使い方をしていた。func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case <-tick: // tickチャネルから受信したの場合 fmt.Println("tick.") case <-boom: // boomチャネルから受信した場合はreturnでfor文から抜ける。 fmt.Println("BOOM!") return default: // デフォルトの場合は50msecスリープ fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } }今週は週3回のクソ記事投稿目標をなんとか達成。。
- 投稿日:2019-02-03T11:46:55+09:00
PerlとGolangの対応表っぽいもの
「PerlではこうだけどGolangだとこう書く」のリストです。
これまでPerlを使ってきて、Golangに入門した方向け。すごくニッチです。スクリプトとコンパイル言語、動的型付けと静的型付けと全く違う言語であり本来比較にならないかと思います。
しかし基礎構文レベルの細かいことでPerlだとこうだけどGolangだとどうやるんだろ?なんて調べることが多く、自分の勉強がてらまとめてみました。公式リファレンス
手元確認バージョン
- Perl v5.16.3
- Golang 1.11.1 (Playground)
表の項目は細かい方に合わせています。数値と文字列で比較演算子が異なるPerlや、データ型が多いGolangなど。
コメント
意味 Perl Golang 単行 # コメントですよ// コメントかな?複数行 =podドキュメントなんかで使います=cut/*こっちのがみなれてるかも*/算術演算子
意味 Perl Golang 足す $a + $ba + b引く $a - $ba - b掛ける $a * $ba * b割る $a / $ba / b余りを求める $a % $ba % bインクリメント $a++a++デクリメント $a--a--文字列結合 $a . $ba + bほぼ一緒。
比較演算子
意味 Perl Golang 等価 数値 $a == $ba == b等価 文字列 $a eq $ba == b不等価 数値 $a != $ba != b不等価 文字列 $a ne $ba != b小なり $a < $ba < b以下 $a <= $ba <= b大なり $a > $ba > b以上 $a >= $ba >= bデータ型
意味 Perl Golang 真偽値 真=1、偽=0
#perlに真偽値は無い真=true、偽=false 整数 符号なし8bit my $a = 255var a uint8 = 255var a byte = 'A' //65整数 符号なし16bit my $a = 65535var a uint16 = 65535整数 符号なし32bit my $a = 4294967295var a uint32 = 4294967295整数 符号なし64bit use bigint;my $a = 18446744073709551615var a uint64 = 18446744073709551615整数 符号あり8bit my $a = -128var a int8 = -128整数 符号あり16bit my $a = -32768var a int16 = -32768整数 符号あり32bit my $a = -2147483648var a int32 = -2147483648var a rune = 'あ' //12354整数 符号あり64bit use bigint;my $a = -9223372036854775808var a int64 = -9223372036854775808浮動少数 32bit my $a = 3.14var a float32 = 3.14浮動少数 64bit my $a = 0.000314159265358979var a float64 = 0.000314159265358979文字列 my $a = "hoge"var a string = "hoge"配列 my @a = (1, 2)var a [2]int = [2]int{1, 2}
//配列は桁固定スライス my @a = (1, 2)var a []int = []int{1, 2}
//スライスは可変ハッシュ/マップ my %a = ("hoge" => 1, "fuga" => 2)var a map[string]int = map[string]int{"hoge": 1, "fuga": 2}
- Perlの数値はおおよそ15桁までで、限界値はOSのライブラリに準じるとの事。それ以上はbigintモジュールを入れます。
- Golangの配列は値で、スライスは配列へのポインタ(+サイズなどの情報を持つ構造体)です。配列はメモリ使用効率に優れる点で、スライスと使い分けされます。
- Golangのbyteとruneは数値型のエイリアスですが慣例的に文字コード(Perlで言うord結果)として使われ、例えば'A'で65が、'あ'で12354が入力されます。文字列として扱うには後述の型変換が必要です。
- 後半Golangの宣言がやたら長いですが、実際はvarを省略したスコープ限定の型推論による代入(:=)を使うことが多いです。
文字列フォーマット
意味 Perl Golang 型 -%T文字 %s%s文字 スペース埋め %2s%2s2進数 %b%b8進数 %o%o10進数 %d%d10進数 少数 %f%f10進数 0埋め %02d%02d16進数 小文字 %x%x16進数 大文字 %X%Xポインタアドレス %p%p元がCなので、一緒ですね。
printf '%02d', 1; > 01import "fmt" fmt.Printf("%02d", 1) > 01ヒアドキュメント(複数行の文字列)
Perl
my $a = <<__QUERY; SELECT hoge FROM hage; __QUERY任意の識別子から識別子まで。(上記例は"__QUERY")
Golang
a := `SELECT hoge FROM hage;`バッククォート(`)で囲む。
リファレンス/ポインタ
意味 Perl Golang 無名のスカラ変数のアドレス my $a = \'hoge'my $a = \1-無名の配列のアドレス my $a = [1, 2]//スライス = 配列のポインタ var a []int = []int{1, 2}無名のハッシュのアドレス my $a = {hoge => 1, fuga => 2}//マップは参照型 var a map[string]int = map[string]int{"hoge": 1, "fuga": 2}無名の関数のアドレス my $a = sub {my ($in) = @_; print $in};&$a('hoge');var a func(string) = func(in string) {fmt.Print(in)}a("hoge")スカラ変数のアドレス my $a = \$b
# "\"でアドレス取得var a *int = &b
// "&"でアドレス取得配列のアドレス my $a = \@b//bはスライス var a []int = b
//bは配列var a *[2]int = &bハッシュ/マップのアドレス my $a = \%bvar a map[string]int = bスカラ変数のデリファレンス print $$afmt.Print(*a)配列のデリファレンス print $$a[0]print $a->[0]
#どちらでも良いfmt.Print(a[0])
//配列のポインタはデリファレンスの必要なし
- Golangのスライスとマップはもともと参照型です。interfaceという多態性の為の仕組みがあり、Print時にStringerインターフェイスが実装されていれば実行されます。なのでPerlのように、printしたらアドレスが見れるわけではないです。
- Golangの配列のポインタはスライスかと思ったんですが、あくまで配列のポインタのようです。(配列は固定サイズなのでappendするとエラーになる)
制御構造
条件分岐 if
Perl
# if文 if ($a == 0) { print "0だよ"; } elsif ($a == 1) { print "1だよ"; } else { print "なんだこれ"; } # 三項演算子 print ($a == 0) ? "0だよ" : "なんだこれ";Golang
if a == 0 { fmt.Print("0だよ") } else if a == 1 { fmt.Print("1だよ") } else { fmt.Print("なんだこれ") } // スコープ限定の変数定義 if err := SelectData(); err != nil { fmt.Print("あかん") }
- Golangは括弧が不要です。また、スコープ限定の変数が使えます。
- Golangに三項演算子はありません。
条件分岐 switch
Perl
given ($a) { when ($_ == 1) { print "1だよ"; } default { print "なんだこれ"; } }
- Perlには元々switchはありません。
新機能のgivenは「実験的機能」とされています。私は一度も使ったことがありません。Golang
// aを判定 switch a { case 1: fmt.Print("1だよ") default: fmt.Print("なんだこれ") } // 各条件内で判定 switch { case a == 1: fmt.Print("1だよ") default: fmt.Print("なんだこれ") }
- Golangのswitchはデフォルトでbreakします。
逆に以降の条件も判定したいときは、fallthroughと書きます。
また、いわゆるswitch trueな使い方が正式に実装されています。ループ for
Perl
for (my $i=0; $i<$a; $i++) { print "$iです"; }Golang
for i:=0; i<a; i++ { fmt.Printf("%dです", i) }特に言うことなし。
ループ while
Perl
# 条件に合う限りループ while($a > 1) { print "やばいよループしてますよー"; $a--; } # 無限ループ while(1) { print "∞"; }Golang
// 条件に合う限りループ for ;i<a; { fmt.Printf("%dです", i) } // 無限ループ for { fmt.Print("∞") }
- Golangのループの制御構造は全てforで書きます。
- Golangのforは「初期化; 条件判定; 後処理」の全てが任意に省略できます。
条件判定を省略すれば無限ループになります。イテレータ 配列
Perl
@values = ("a", "b", "c"); foreach my $v (@values) { print $v; } > a, b, cGolang
values := []string{"a", "b", "c"} for i, v := range values { fmt.Printf("%d->%s, ", i, v) } > 0->a, 1->b, 2->c,イテレータ ハッシュ/マップ
Perl
%hash = (a=>1, b=>2, c=>3); foreach my $k (keys %hash) { print "$k\->$hash{$k}, "; } > c->3, a->1, b->2, # ハッシュのキー順序は不定Golang
values := map[string]int{"a": 1, "b": 2, "c": 3} for i, v := range values { fmt.Printf("%s->%d, ", i, v) } > a->1, b->2, c->3, // マップのキー順序も不定型変換
Perl
Perlの場合、スカラ値は強いて言えば文字列か数値かの違いしかありません。# 文字列を示す「"」で囲っても、数値計算できる my $a = "3.14"; print $a * 3; > 9.42 # JSONに渡す時などにたまに使うint my $int = int($str); # 「"」で囲めば文字列扱いになる my $str = "$int";Golang
数値型については、型(値)で変換可能です。a := 1.15 fmt.Printf("%T, %d", int(a), int(a)) > int, 1 b := int(a) fmt.Printf("%T, %f", float64(b), float64(b)) > float64, 1.000000文字列⇔数値はstrconvパッケージを使います。
https://golang.org/pkg/strconv/import "strconv" // 文字列→数値 i, err := strconv.Atoi("-42") // 数値→文字列 s := strconv.Itoa(128)後述しますが以下は数値がアスキーコードと見なされてしまい、
Perlで言うchrの挙動になってしまいますので
「数値をそのまま文字列として表示したい」場合には使えません。i := 65 fmt.Println(string(i)) > A文字⇔アスキーコード変換
Perl
# 文字→コード print ord('A'); > 65 # コード→文字 print chr(65); > A print chr(0x3042); > あGolang
Golangのここが私にとっては特殊(初見)でした。
byteとruneという数値型のエイリアスがあり、文字をシングルクォート「'」で囲むとbyteまたはruneとして、ダブルクォート「"」で囲むと文字列として認識されます。
個人的に最初にここでちょっと詰まり、こういうまとめを作ろうと思ったきっかけでもあります。// この場合byte型 a := 'A' fmt.Print(a) > 65 // この場合string型 b := "B" fmt.Print(b) > B // stringは内部的にはbyte(rune)の配列 fmt.Print(b[0]) > 66 // 文字→コード c := "あいうえお" for _, v := range c { fmt.Printf("%x,", v) } > 3042,3044,3046,3048,304a, // コード→文字 d := 65 fmt.Print(string(d)) > A f := 0x3042 fmt.Print(string(f)) > あ全然足りない気がするけど、とりあえずここまで。












