- 投稿日:2020-02-11T20:46:50+09:00
Go mapのkeyにインターフェースを使う場合に気をつけるべきこと
小ネタです。
GoのMapのkey
goはmapの初期化時以下のように「keyの型」と「valueの型」を両方宣言しますが、
m := make(map[string]string)この「keyの型」には当然interface(空interface含む)を指定することもできる。
m := make(map[io.Reader]string) // これとか m := make(map[interface{}]string) // これこのような場合、実際に値を入れるときに型が違えば当然keyも違う。
要するにこういうことです
type dog int type cat int m := make(map[interface{}]string) m[dog(1)] = "dog dayo" m[cat(1)] = "cat dayo" fmt.Printf("%v", m) // map[1:dog dayo 1:cat dayo] // 拡張int同士、同じ数字だがkeyは違う同じintを拡張したdog,cat型ですが、keyの見た目は数字の1で重複しているのに
型が違うので上書きではなく新規登録になっていますね。structで全く新しい型を作った場合は「まぁそうだろうな」と直感でわかりますが、
同じintを拡張した型同士でもしっかりと別型として扱われます。さすがにinterface{}ほどゆるいkeyを使うことってないかもしれないですが、
(というか殆どの場合keyはintやstringといったプリミティブ型だと思いますが)
一応このような仕様になってますよということで。
- 投稿日:2020-02-11T20:42:07+09:00
バッファなしチャンネル(unbeffered channel)のブロッキング(blocking)について
バッファなしは同期通信する
バッファなし
ch1 := make(chan int) ch2 := make(chan int, 0)バッファあり
ch := make(chan int, 1)バッファなしの場合は同期通信となるのでチャンネルに送信すると同時に受信しなければずっとそこでブロックしてしまう。そのため下記のようなコードは実行前にfatal errorとなってしまう。
func main() { ch := make(chan int, 0) ch <- 1 // ここでブロックし続けてしまう fmt.Println("finish") }解決するにはどこかで受信しておかなければならない
func main() { ch := make(chan int, 0) go func() { <-ch }() ch <- 1 fmt.Println("finish") }バッファありはcapacityまではブロックしない
下記は正常に動作しfinishが出力される
func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println("finish") }capacityを超えるとブロックするため下記はエラーとなる
func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 ch <- 3 fmt.Println("finish") }selectの動作(バッファなしのチャネルに対して)
selectはチャンネル通信が起きた時の条件を複数登録してそれぞれの動作を記述することができるが、チャンネルのブロックには気をつけておく必要がある。
func main() { ch := make(chan int, 0) heartBeat := make(chan struct{}, 0) i := 0 go func() { pulse := time.Tick(1000 * time.Millisecond) for { fmt.Println("==select==") select { case ch <- i: fmt.Println("send ch finish") i++ case <-pulse: fmt.Println("send heartBeat start") heartBeat <- struct{}{} fmt.Println("send heartBeat finish") } } }() // mainのgoroutineでは何もしない for { select { } } }上のプログラムは下記の出力の後、動作しなくなる。
$ go run sample.go ==select== send heartBeat startheartBeatチャンネルはバッファなしのため、送信と受信が同時に起こらなければその場でブロックし続けるからだ。goroutineでのselectのloopは先に進むことなくheartBeatが受信されるまでブロックし続ける。
chへの送信は受信のロジックが書かれていないのでcaseでマッチしない。
selectの動作(バッファあり)
今度はheartBeatのチャンネルのcapacityを2にする
func main() { ch := make(chan int, 0) heartBeat := make(chan struct{}, 2) i := 0 go func() { pulse := time.Tick(1000 * time.Millisecond) for { fmt.Println("==select==") select { case ch <- i: fmt.Println("send ch finish") i++ case <-pulse: fmt.Println("send heartBeat start") heartBeat <- struct{}{} fmt.Println("send heartBeat finish") } } }() // mainのgoroutineでは何もしない for { select { } } }上のプログラムは次の出力の後、動作しなくなる。
$ go run sample.go ==select== send heartBeat start send heartBeat finish ==select== send heartBeat start send heartBeat finish ==select== send heartBeat start想定通りcapacityの数だけブロックしない。
selectループ同士のdeadlockについて
バッファなしチャンネルを使ってメインルーチンとワーカールーチンでやりとりすると、簡単にデッドロックしてしまうモノが書けてしまった。。
func SuccWorker(ctx context.Context, srcCh chan int) (chan struct{}, chan int) { heartBeatCh := make(chan struct{}) succResultCh := make(chan int) pulse := time.NewTicker(1000 * time.Millisecond) go func() { defer close(heartBeatCh) defer close(succResultCh) defer pulse.Stop() // not close channel for { select { case <-ctx.Done(): return case i, ok := <-srcCh: if !ok { return } i++ succResultCh <- i case <-pulse.C: fmt.Println("\tsend heartbeat start") heartBeatCh <- struct{}{} fmt.Println("\tsend heartbeat end") } } }() return heartBeatCh, succResultCh } func main() { fmt.Println("Start") ctx := context.Background() srcCh := make(chan int) heartBeatCh, succResultCh := SuccWorker(ctx, srcCh) // kick go func() { <-time.After(3 * time.Second) srcCh <- 0 }() mainloop: for { select { case <-heartBeatCh: fmt.Println("HeartBeat: ", time.Now().Format("15:04:05")) case i, ok := <-succResultCh: if !ok { break mainloop } <-time.After(1000 * time.Millisecond) fmt.Println("succResult: ", i) fmt.Println("send srcCh start") srcCh <- i fmt.Println("send srcCh end") } } fmt.Println("Finish") }上記を動かすと以下のような出力が出て以降は止まってしまう。
Start send heartbeat start send heartbeat end HeartBeat: 17:39:16 send heartbeat start send heartbeat end HeartBeat: 17:39:17 send heartbeat start send heartbeat end HeartBeat: 17:39:18 send heartbeat start succResult: 1 send srcCh startワーカーからはheartbeatを送信しようとしたが、mainloopではワーカーにsrcChを介して送信しようとしており、両方とも相手の受信待ちでブロックが発生してしまう。
deadlockの解消
解消方法は自分が調べた範囲では下記2つが有効だった。
- selectループをなるべく止めない
- バッファありチャネルを使う
selectループを止めない方法
mainloopの中でsuccResultChから受信してからの処理をgoroutineで実行してすぐにselectループに戻るようにして解消した。ように見えた。
func SuccWorker(ctx context.Context, srcCh chan int) (chan struct{}, chan int) { heartBeatCh := make(chan struct{}, 0) succResultCh := make(chan int, 0) pulse := time.NewTicker(1000 * time.Millisecond) go func() { defer close(heartBeatCh) defer close(succResultCh) defer pulse.Stop() for { select { case <-ctx.Done(): return case i, ok := <-srcCh: if !ok { return } i++ succResultCh <- i case <-pulse.C: fmt.Println("\tsend heartbeat start") heartBeatCh <- struct{}{} fmt.Println("\tsend heartbeat end") } } }() return heartBeatCh, succResultCh } func main() { fmt.Println("Start") ctx := context.Background() srcCh := make(chan int, 0) heartBeatCh, succResultCh := SuccWorker(ctx, srcCh) // kick go func() { <-time.After(3 * time.Second) srcCh <- 0 }() mainloop: for { select { case <-heartBeatCh: fmt.Println("HeartBeat: ", time.Now().Format("15:04:05")) case i, ok := <-succResultCh: if !ok { break mainloop } go func(){ <-time.After(100 * time.Millisecond) fmt.Println("succResult: ", i) fmt.Println("send srcCh start") srcCh <- i fmt.Println("send srcCh end") }() } } fmt.Println("Finish") }バッファありチャネルを使う方法
使っているチャンネルを全てcapacity 1のバッファありに変更するとひとまずうまく動いているように見えた。こちらのmainLoopのselectループの中ではgoroutineでの処理はしていない。
func SuccWorker(ctx context.Context, srcCh chan int) (chan struct{}, chan int) { heartBeatCh := make(chan struct{}, 1) succResultCh := make(chan int, 1) pulse := time.NewTicker(1000 * time.Millisecond) go func() { defer close(heartBeatCh) defer close(succResultCh) defer pulse.Stop() for { select { case <-ctx.Done(): return case i, ok := <-srcCh: if !ok { return } i++ succResultCh <- i case <-pulse.C: fmt.Println("\tsend heartbeat start") heartBeatCh <- struct{}{} fmt.Println("\tsend heartbeat end") } } }() return heartBeatCh, succResultCh } func main() { fmt.Println("Start") ctx := context.Background() srcCh := make(chan int, 1) heartBeatCh, succResultCh := SuccWorker(ctx, srcCh) // kick go func() { <-time.After(3 * time.Second) srcCh <- 0 }() mainloop: for { select { case <-heartBeatCh: fmt.Println("HeartBeat: ", time.Now().Format("15:04:05")) case i, ok := <-succResultCh: if !ok { break mainloop } <-time.After(100 * time.Millisecond) fmt.Println("succResult: ", i) fmt.Println("send srcCh start") srcCh <- i fmt.Println("send srcCh end") } } fmt.Println("Finish") }最後に
selectループ同士で通信する時はなるべくselectループを止めないのと、基本的にはバッファありチャンネルを利用する、ことで全てではないだろうがデッドロックの可能性を少なくすることができるように見えた。
- 投稿日:2020-02-11T18:58:06+09:00
ABC049C - 白昼夢を簡単に導く方法が知りたい(Golang)
はじめに
ABC049C - 白昼夢を検索文字列の特性上、文字列をリバースして導く方法で解きました。
解きかたはさておき、検索能力の低さゆえ、Goに以下標準関数が見つけられず手作りとなってしまったので、検索能力向上した際には改良したいと思いメモです。
- 文字列のリバース
- 開始〜終了桁数を指定した文字列の抜き出し(例:abcを1文字目から2文字目で抜き出して「b」を求める)
コード
package main import ( "fmt" ) func main() { var s string annwer := "YES" divide := [4]string{"dream", "dreamer", "erase", "eraser"} fmt.Scanf("%s", &s) // リバース for i :=0; i < len(divide); i++ { divide[i] = getReverse(divide[i]) } reverse := getReverse(s) // 後ろから一致するものを抜き出す j := 0 // 開始位置 for j < len(reverse) { annwer = "NO" for i := 0; i < 4; i++ { // 比較する文字列を必要な範囲で抜き出す var str string for k := j; k < j + len(divide[i]); k++ { if k >= len(reverse) { continue } str += string(reverse[k]) } // 比較 if str == divide[i] { // 一致した分開始位置をずらす j += len(divide[i]) annwer = "YES" break } } // ここにNOで来た時点でアウトなので終わり if annwer == "NO" { break } } fmt.Println(annwer) } func getReverse(s string) string { rs := []rune(s) // 今回マルチバイト文字ではないが想定してrune for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { rs[i], rs[j] = rs[j], rs[i] } return string(rs) }おわりに
冗長すぎておかしくなりそうでした。
あまりニッチではないライブラリで実現可能でしたら教えていただけると嬉しいです。
追伸:別解としてReplaceで一致したら""と置換してしまい、最後0文字になったらYESという方法は認識しております。
- 投稿日:2020-02-11T18:12:48+09:00
GoのサンプルWebアプリをローカルで実行した時にはまったことのメモ
背景
- Goプログラミング実践入門に記載されているサンプルWebアプリのコードをローカルで実行しようとしたときにはまったことのメモ
やっていたこと
- ホームディレクトリに
git clone
してそのままgo build
をした- すると以下のエラーが
route_main.go:5:2: cannot find package "chitchat/data" in any of: /usr/local/Cellar/go/1.13.4/libexec/src/chitchat/data (from $GOROOT) /Users/XXXXXX/go/src/chitchat/data (from $GOPATH)
- route_main.goを見てみると、importで自プロジェクトのパッケージを呼び出しており、そこが悪さをしていそう
import ( "github.com/mushahiroyuki/gowebprog/ch02/chitchat/data" )解決策
- 今回のように、単純に動かすだけなら
go get
コマンドでソースを取得すれば良い$ go get github.com/mushahiroyuki/gowebprog/ch02/chitchat/data
go get
コマンドによって、以下の処理が行われる
$GOPATH/bin
に実行ファイルが置かれる$GOPATH/src
にパッケージのソースコードが置かれる
$GOPATH/src/github.com/mushahiroyuki/gowebprog/ch02/chitchat
というパスに置かれる実行するだけなら、
$GOPATH/bin
配下に取得された実行ファイルを起動させればよい
- つまり、自分でbuildする必要がなかった
ソースを変更しても、
go build
で新しい実行ファイルが生成されるので問題なさそうなぜエラーになっていたのか
- 実行できるにしても最初に発生していたエラーは何なのか
- それは、importに指定する
github.com/mushahiroyuki/...
というパスは、$GOPATH/src
からのパスに対応しているためgo get
でソースを取得すると、$GOPATH/src
配下に作成されるパスがimportに記載したパスと一致することになる
- importへの記載は公式ドキュメントに書かれているように、同じプロジェクト内であっても
github.com/...
という絶対パスでの書き方が推奨されている- 何も考えずに
git clone
でローカルに落としてくると、importに記載したパスに合わないため、パッケージが見つからずエラーになるGitで管理したい場合はどうするのか
- 個人的にローカルで実行させるだけなら上記の方法で問題無いが、チームで開発するなどの理由でGitで管理したい場合はどうするのか
- 正解は無さそうだが、以下の2パターンがありそう
- importのパスに合うように
git clone
する- 大元のリポジトリをforkし、
go get
で取得したパスにgit remote add
でforkしたリポジトリへの参照を追加
- fork先へプッシュし、originへのプルリクエストを作成する
参考
- 投稿日:2020-02-11T14:56:36+09:00
【Go】VSCodeの自動フォーマットが効かないときにやったこと
はじめに
goenv経由でGo 1.11.13をインストールしたが
VSCodeの自動フォーマットが効かず
インデントも揃わない、パッケージも自動で入ってくれない。
この問題解決のためにやったことの備忘録。The "gopls" command is not available.
Use "go get -v golang.org/x/tools/cmd/gopls" to install.っていうポップアップが、VSCodeを開くたびに表示されて
「Install」 →→→「SUCCESS! You are ready to Go!」
実際入ってないんですけど。gopls(発音はGo Please)というLanguage serverが入らないから
goimports(フォーマッタの正体)も動いてくれない(らしい)。環境
MacBook Air macOS Catalina ver.10.15.2 Visual Studio Code ver.1.42.0 Go 1.11.13 (これが問題の原因のひとつ)解決策
1.goenvとGoをアンインストール。
// goenvのアンインストール $ brew uninstall goenv // Goをインストールすると自動で作成されるGoディレクトリ削除 $ rm -rvf /usr/local/go/2.Goの環境構築
// goenvの再インストール $ brew install goenv // インストール可能なGoのバージョンを調べる $ goenv install -l // Go1.13.7をインストール $ goenv install 1.13.7ここでのGoのバージョンは1.12.x以上であればOK。
というか、1.12.x以上じゃないとこのあとインストールするgoplsが入ってくれない。3.VSCodeに必要なモジュールをインストール
VSCodeを開いて、Cmd + Shift + P、「Go」とか打つと
Go Install/Update Tools
と表示されるので、それを選択。
18個(2020年2月11日時点)のモジュールすべてを選択してインストール。でも、goplsだけやっぱり入ってくれない。
gopls
↑↑この方法でインストール。
ここには『gopls入れるならGo1.12.x以上』って書いてある。gopls is a go module project, so you need install Go 1.12 or above, to install the gopls, please run ...
$ git clone -b bingo https://github.com/saibing/tools.git $ cd tools/gopls $ go installこのあとVSCodeに戻って設定をsettings.jsonから変更。
このsettings.jsonは以下のパスからでも直接開くことができる。
~/Library/Application Support/Code/User/settings.json
settings.json{ "go.useLanguageServer": true, "go.alternateTools": { "go-langserver": "gopls" }, "go.languageServerExperimentalFeatures": { "format": true, "autoComplete": true }, "[go]": { "editor.snippetSuggestions": "none", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true }, }, }これで自動フォーマットやコード補完が動いた。
そして、もともと使っていたGo1.11.13をインストールしても
コード補完が効くことが確認できた。さいごに
今回使用したBingo
Bingo is a Go language server that speaks Language Server Protocol.
これもgoplsと同様のLanguage Server Protocolだけど、
今後のサポートやメンテを行わないようです。Note
I am very sorry that I have planned not continue to maintain this project.今回紹介した方法もいつかはできなくなるかも。
参考サイト
- 投稿日:2020-02-11T14:53:29+09:00
Scannerを使って、任意の文字列で区切りながら読み込む方法(golang初心者の備忘録)
Scannerを使って、任意の文字列で区切りながら読み込む方法
動機
Goならわかるシステムプログラミングの3章を読んでいて疑問に思ったところ。
Scannerを使って読み込むとき
- 改行を区切り文字とするときは何もしなくてよい
- 単語単位(空白区切り)はbufio.ScanWordsをSplitに渡せばよい
が、任意の文字列の場合はどうすればよいかわからないため、調査した。
また、
- 区切り文字列を含む読み込み
- 区切り文字列を含まない読み込み
についても違いを調査した。
実装方法
func(data []byte, atEOF bool) (advance int, token []byte, err error)
- advance: 次回の読み込みが始まる開始位置
- token: 今回の読み込まれるデータ
- err: エラー
を実装して、Scanner.Splitに渡す。
コード例
区切り文字を含めて読み込む
"行"を区切り文字としてScannerの読み込みをする。
package main import ( "bufio" "fmt" "strings" ) var source = `1行め 2行め 3行め` func main() { scanner := bufio.NewScanner(strings.NewReader(source)) delim := []byte("行") var splitFunction = func(data []byte, atEOF bool) (advance int, token []byte, err error) { for i := 0; i < len(data); i++ { if data[i] == delim[0] && data[i+1] == delim[1] && data[i+2] == delim[2] { return i + 3, data[:i+3], nil //tokenをdata[:i+3]としているので、区切り文字は含まれる } } return 0, data, bufio.ErrFinalToken } scanner.Split(splitFunction) for scanner.Scan() { fmt.Printf("%#v\n", scanner.Text()) } }実行結果
"1行" "め\n2行" "め\n3行" "め"区切り文字を含めないで読み込む
"、","。"を区切り文字としてScannerの読み込みをする。
package main import ( "bufio" "fmt" "strings" ) var source = `真夏の宿場は空虚であった。ただ眼の大きな一疋いっぴきの蠅だけは、薄暗い厩うまやの隅すみの蜘蛛くもの巣にひっかかると、後肢あとあしで網を跳ねつつ暫しばらくぶらぶらと揺れていた。` func main() { scanner := bufio.NewScanner(strings.NewReader(source)) delim1 := []byte("、") delim2 := []byte("。") var splitFunction = func(data []byte, atEOF bool) (advance int, token []byte, err error) { for i := 0; i < len(data); i++ { if data[i] == delim1[0] && data[i+1] == delim1[1] && data[i+2] == delim1[2] { return i + 3, data[:i], nil//tokenをdata[:i]としているので、区切り文字は含まれない } if data[i] == delim2[0] && data[i+1] == delim2[1] && data[i+2] == delim2[2] { return i + 3, data[:i], nil//tokenをdata[:i]としているので、区切り文字は含まれない } } return 0, data, bufio.ErrFinalToken } scanner.Split(splitFunction) for scanner.Scan() { fmt.Printf("%#v\n", scanner.Text()) } }実行結果
"真夏の宿場は空虚であった" "ただ眼の大きな一疋いっぴきの蠅だけは" "薄暗い厩うまやの隅すみの蜘蛛くもの巣にひっかかると" "後肢あとあしで網を跳ねつつ暫しばらくぶらぶらと揺れていた" ""入力文は青空文庫、蝿(横光利一)から持ってきた
参考
- 投稿日:2020-02-11T12:59:17+09:00
HackerRankの30Days of Codeで学んだことメモ
HackerRankとは
- 課題を通じてプログラミング言語を学習できるサイト
- 無料
- 扱っている言語は多数
- その中の30Days of Codeというカリキュラムに挑戦
背景
- Goの基本的な書き方を学ぶために始めます
- 初心者です
- 自分の解答やEditorialの内容を基に書きます
Day1 Data Types
ポイント
- 標準入力から整数、浮動少数点数、文字列を取得する
メモ
- 標準入力の取得にはfmtパッケージのScanf関数が使える
- 引数にアドレスを指定することで、スペース区切りで順繰りに値を格納してくれる
入力12 4.0//標準入力取得用変数 var stdin_i uint64 var stdin_f float64 //標準入力から整数と浮動少数点数を取得 fmt.Scanf("%d\n%f\n", &stdin_i, &stdin_f)参考
- 文字列をscanf関数で扱うとき
- C言語を扱っているが考え方は同じ
- 投稿日:2020-02-11T12:08:41+09:00
Golang - Knuth-Durstenfeld Shuffle with crypto/rand
package main import ( "crypto/rand" "fmt" "math/big" ) func shuffle(array []int) { for i := len(array) - 1; i >= 0; i-- { result, err := rand.Int(rand.Reader, big.NewInt(int64(i+1))) if err != nil { panic(err) } j := int(result.Int64()) array[i], array[j] = array[j], array[i] } } func main() { a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} fmt.Println(a) shuffle(a) fmt.Println(a) }[1 2 3 4 5 6 7 8 9 10] [7 2 1 10 3 8 9 6 5 4] [1 2 3 4 5 6 7 8 9 10] [9 2 1 5 3 10 7 4 6 8] [1 2 3 4 5 6 7 8 9 10] [8 1 6 7 2 3 10 9 4 5]
- 投稿日:2020-02-11T11:19:04+09:00
ABC081B - Shift onlyを2で割れる回数の最小値から導く方法(Golang)
はじめに
ABC081B - Shift onlyを実際の操作のシミュレーションでのカウントではなく、
2で割れる回数の最小値から導く方法です。package main import ( "fmt" ) func main() { var n int min := 1000000000 // Aiの取りうる値の最大値 fmt.Scanf("%d", &n) nums := make([]int, n) // n個の値を取得 for i := 0; i < n; i++ { fmt.Scan(&nums[i]) } for i :=0; i < n; i++ { lcnt := 0; // 2で割り切れる限り回し続ける for nums[i]%2 == 0 { nums[i] = nums[i]/2 lcnt++ // 割った数をカウント } // 最小値より小さいカウントの場合入れ替え if min > lcnt { min = lcnt } } fmt.Println(min) }おわりに
説明文の操作の通り上から順序よく処理したくなりますが、選択肢として持っておきたいため書きました。
- 投稿日:2020-02-11T09:34:52+09:00
文系プログラマーがAtCoderをする前に確認するメモ
はじめに
AtCorderを開始する前に確認する自分用メモです。
こちらの記事は、習慣的に過去問をこなしていくときのハードルをなくすことを目的としています。なお、私はJAVA、PHPの経験が長いですが、
現在はQAを経て、外資系コンサルティングファームに所属しており日常コードに触れる機会はありません。前提(以下には触れていません)
- AtCorderへのアカウント登録
- コンテストが対象の方は、参加したいコンテストの参加登録と参加日の到来
参加対象
例です。コンテストに参加する習慣がついている方はTOPから遷移できるかと思いますが、
過去問をこなしていこうと思っている方はどこからいくんだっけ、どこまで消化したっけと迷うくらいなら直リンクで飛びましょう。
URL:https://atcoder.jp/contests/abs/tasks
やっていることメモ:AtCoder Beginners Selectionの10問目
参考:競技プログラミングって何? (はじめての高校生向け)コンテスト(過去問や練習含む)の「参加」ボタンを押す前の準備
1. 必ず必要となる構文をすぐにコピーできる状態にしておく
競技プログラムでは、標準入力がインプットとされます。特に複数回にわたって与えられる標準入力を取得する方法がイメージつかない方はかならず準備しておいてください。
Goの例
入力a b c s上記入力を受け取って出力するための最低限の記述package main import ( "fmt" ) func main() { var a, b, c int var s string fmt.Scanf("%d", &a) fmt.Scanf("%d %d", &b, &c) fmt.Scanf("%s", &s) fmt.Printf("%d %s\n", a+b+c, s) }参考(Go以外の言語もこちらから):https://practice.contest.atcoder.jp/tasks/practice_1
2.よく出てくる計算や構文も同様のメモ
構文に関しては、日常業務でコーディングやられている方はソラで書けると思いますが、
業務で使わないようなニッチな計算方式(公約数など)はメモしておいたほうがいいと感じました。パッと出ないものの例
文字列の分割取り出し(配列として扱う)s = "abc" string(s[0]) // "a"入力数がnの場合の取得方法nums := make([]int, n) for i := 0; i < n; i++ { fmt.Scan(&nums[i]) }各桁の和// 83の例 // 1回目:83/10=8あまり3(1桁目の数字) // 2回目:8/10=0あまり8(10桁目の数字) sum := 0 for n > 0 { sum += n % 10 n /= 10 }ソートsort.Ints(nums) // 照準 sort.Sort(sort.Reverse(sort.IntSlice(nums))) // 降順for文、range// 普通のforの繰り返し for i := 0; i < 10; i++ { } // while文的にも作成可能 for n < 10 { n++ } // range for i, v := range s { fmt.Println(i, v) }if文if score := 52; score > 80 { fmt.Println("Great!") } else if score > 60 { fmt.Println("Good!") } else { fmt.Println("soso") }switch文switch signal { case "red": fmt.Println("Stop") default: fmt.Println("wrong") }スライスa := [5]int{2, 10, 8, 15, 4} b := a[2:4] // [8, 15] // make関数でいきなりスライスを作成する s1 := make([]int, 3) // [0 0 0] // いきなり値を割り当てたスライスを作成する s2 := []int{1, 3, 5} // 配列の宣言と似ている // appendでスライスの末尾に要素を追加 s3 := append(s2, 8, 2, 10)配列// 宣言と代入を分ける var a [5]int a [2] = 3 // 宣言と代入を同時にする b := [3]int{1, 3, 6} // 配列の個数が未定の場合 c := [...]int{2, 4, 7, 5, 5}マップ// 値を指定しながら宣言する m := map[string]int{"fujimoto": 100, "arita": 200} // キーの存在を調べる v, ok := m["fujimoto"] // 要素を削除する delete(m, "fujimoto")参考:他言語プログラマがgolangの基本を押さえる為のまとめ
3.macに置けるバックスラッシュの入力の仕方を思い出す
optionキーを押しながら、¥キーを押す。(Mac mini 等で Windows用のキーボードを接続している場合は Alt キー)
※IME「ことえり」の設定を変えることで「¥」を「\」にすることは可能提出が終わったらすること
2のメンテ
自身の不足点やパッと出なかったものを追加します。
ソラで書けるようになったものがあれば削除します。次の参加対象URLを更新
これをしておくことで、次にPCを開いたとき、何をするのか考えることなく画面遷移して始められます。
おわりに
日常的にコードに触れていないことと過去の経験言語と微妙に違う書き方(for文、if文が特に)でコンパイルエラーが出てしまうことや間隔が開いたときの「何するんだっけ(どこまでしていたんだっけ)」でハードルが上がっていたので書きました。
まずは、増える一方の2のメモを0個にすることを目標とします。
- 投稿日:2020-02-11T00:44:22+09:00
WSL上でgolangを使ってMP3の情報を取得する
go環境の用意
$ git clone https://github.com/syndbg/goenv.git ~/.goenv
~/.bashrc
に追記export GOENV_ROOT=$HOME/.goenv export PATH=$GOENV_ROOT/bin:$PATH eval "$(goenv init -)"go のバージョン設定、確認
$ goenv install 1.13.7 $ go version go version go1.13.7 linux/amd64id3-go を使ってMP3の情報を取得する
GitHub - mikkyang/id3-go: ID3 library for Go
$ go get github.com/mikkyang/id3-go
サンプルコード
package main import ( "fmt" id3 "github.com/mikkyang/id3-go" ) func main() { mp3File, _:= id3.Open("sample.mp3") defer mp3File.Close() fmt.Println(mp3File.Artist()) }同じディレクトリに sample.mp3 を用意して実行
$ go run mp3test.go SampleArtist
参考にしたサイト