- 投稿日:2021-03-01T13:11:50+09:00
Goで月(Month)をint型にしたい
Goでは月を取得するとint型ではなく、
March
みたいな値が出力される。
JavaScriptでは月を取得すると、number型(Goでいうint型)になるので少し驚いたのでメモ。
(しかも、JSは0から始まるから厄介...)そのまま月(Month)を取得しようとすると
package main import ( "fmt" "time" ) func main() { t := time.Now() month := t.Month() fmt.Printf("type=%T value=%v\n", month, month) }結果type=time.Month value=March
結果として、
time.Month型
という型のMarch
が返される。月(Month)をint型にするには
これをint型で扱うにはどうすれば良いか?
今回の例の場合はMarch
を3
と扱うにはどうすれば良いか?これは簡単で、
time.Now().Month()
をint()
で「time.Month型
=>int型
」に型変換してあげればOK!package main import ( "fmt" "time" ) func main() { t := time.Now() month := t.Month() // 型変換でint型へ monthToInt := int(month) fmt.Printf("type=%T value=%v\n", monthToInt, monthToInt) }結果type=int value=3
これで
March
ではなく3
というint型で扱える。
以上。
- 投稿日:2021-03-01T07:04:39+09:00
A Tour of GoのExercise: Mapsを解く
考えたこと
まず設問を理解します。
string s で渡される文章の、各単語の出現回数のmapを返す必要があります。
ふむふむ、どうやら単語の出現回数を数えるのがこの問題の趣旨らしい。
Note: このテストスイートで何を入力とし、何を期待しているかについては、golang.org/x/tour/wcを見てみてください。
とあるので、golang.org/x/tour/wcをみてみると、テストケースがわかります。
{"I am learning Go!", map[string]int{ "I": 1, "am": 1, "learning": 1, "Go!": 1, }},ここまでくると全容が見えてきます。
"I am learning Go!"のような形式で入力が与えられるので、スペースで区切って、I とか am とかが何回出現するか数える、
というのがこの設問の答えということがわかります。さて、どうやってスペースで区切るかですが、
strings.Fields で、何かヒントを得ることができるはずです。
と書いてあるのでstrings.Fields をみてみましょう。
まさに求めていたスペースで区切る関数でした。
引数で与えられたstringをスペースで分割して、stringのスライスを返す関数のようです。ここまできたら、材料は揃ったのであとは解くだけです。
コード
Mapspackage main import ( "golang.org/x/tour/wc" "strings" ) func WordCount(s string) map[string]int { m := make(map[string]int) // stringをスペースで分割する splited := strings.Fields(s) for _, val := range splited { // 出現回数を数える m[val]++ } return m } func main() { wc.Test(WordCount) }
- 投稿日:2021-03-01T05:53:37+09:00
Goでapiを叩く
はじめに
最近Nvimを使ってみたんですが、Nvimの設定ファイルであるinit.vimを編集するのに使い慣れているvsCodeVimを使いました。いつかNvimを使いこなしたいですまる
さて、バックエンドの勉強をしたくて最近Go言語に入門してみました。購入したGOの書籍を読み終えて何か書きたくなったところで、JavascriptでやってたことをGoではどうやっているのか確かめてみようと思いました。
ということでGoで簡単なApiを叩いてみました。書いてあること
- Unmarshalでjsonを解析する方法
- encoderで解析する方法
叩くApi
定番のOpen Weather Map です。
まず、どんなJsonが返ってくるか確認します。curl http://api.openweathermap.org/data/2.5/weather?q=nagano -H "X-API-KEY: YOUR API KEY" | jqこんなん↓が返ってくると思います。
{ "coord": { "lon": 138.1811, "lat": 36.6514 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" } ], "base": "stations", "main": { "temp": 276.93, "feels_like": 274.22, "temp_min": 275.37, "temp_max": 278.15, "pressure": 1024, "humidity": 67 }, "visibility": 10000, "wind": { "speed": 0.69, "deg": 182 }, "clouds": { "all": 100 }, "dt": 1613130091, "sys": { "type": 3, "id": 19237, "country": "JP", "sunrise": 1613079554, "sunset": 1613118238 }, "timezone": 32400, "id": 1856215, "name": "Nagano", "cod": 200 }Unmarshalを使う
まずは全体のコードをぱっとみ↓
package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "log" "net/http" ) var ( Key string ) // 構造体のタグによりjsonと結びつける type Data struct { Weather []Weather `json:"weather"` Main Main `json:"main"` } type Weather struct { ID int `json:"id"` Main string `json:"main"` Description string `json:"description"` ICON string `json:"icon"` } type Main struct { Temp float32 `json:"temp"` FeelsLike float32 `json:"feels_like"` TempMin float32 `json:"temp_min"` TempMax float32 `json:"temp_max"` Pressure int `json:"pressure"` Humidity int `json:"humidity"` } // コマンドラインの引数を取得 func init() { flag.StringVar(&Key, "key", "", "Api key") flag.Parse() } func main() { url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=nagano&appid=%s", Key) resp, err := http.Get(url) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } var data Data if err := json.Unmarshal(body, &data); err != nil { log.Fatal(err) } fmt.Println(data) }全体の流れはほしいjsonデータに対応する構造体をつくり、その構造体にUnmarshalやdecoderで解析した値を入れていきます。
今回はさきほど返ってきたjsonのweatherとmainの値がほしいとします。{ "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" } ], "main": { "temp": 276.93, "feels_like": 274.22, "temp_min": 275.37, "temp_max": 278.15, "pressure": 1024, "humidity": 67 }, }このjsonをGOの構造体で表現しタグで結びつけます。
type Data struct { Weather []Weather `json:"weather"` Main Main `json:"main"` } type Weather struct { ID int `json:"id"` Main string `json:"main"` Description string `json:"description"` ICON string `json:"icon"` } type Main struct { Temp float32 `json:"temp"` FeelsLike float32 `json:"feels_like"` TempMin float32 `json:"temp_min"` TempMax float32 `json:"temp_max"` Pressure int `json:"pressure"` Humidity int `json:"humidity"` }あとはUnmarshalに渡すだけです。Marshalは構築するみたいな意味があり、その反対のUnmershalはjsonを崩すみたいなイメージを勝手に持っています。jsonのMarshalメソッドは構造体からjsonを構築するときに使います。(余談ですがMarshalという名前の微妙な性能を持った武器がバロラントというゲームに存在していて何か変な親近感を持ってます)
var data Data if err := json.Unmarshal(body, &data); err != nil { log.Fatal(err) }コードを実行
go run main.go -key OpenWeatherMapのApiKeyこんなんが返ってくれば成功です。
{[{804 Clouds overcast clouds 04n}] {276.93 274.22 275.37 278.15 1024 67}}decoderを使う
package main import ( "encoding/json" "flag" "fmt" "io" "net/http" ) var ( Key string ) type Data struct { Weather []Weather `json:"weather"` Main Main `json:"main"` } type Weather struct { ID int `json:"id"` Main string `json:"main"` Description string `json:"description"` ICON string `json:"icon"` } type Main struct { Temp float32 `json:"temp"` FeelsLike float32 `json:"feels_like"` TempMin float32 `json:"temp_min"` TempMax float32 `json:"temp_max"` Pressure int `json:"pressure"` Humidity int `json:"humidity"` } func init() { flag.StringVar(&Key, "key", "", "Api key") flag.Parse() } func main() { url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=nagano&appid=%s", Key) resp, err := http.Get(url) if err != nil { fmt.Println("Error get api:", err) return } defer resp.Body.Close() // デコーダ作成 decoder := json.NewDecoder(resp.Body) for { var data Data err := decoder.Decode(&data) if err == io.EOF { break } if err != nil { fmt.Println("Error decode json:", err) return } fmt.Println(data) } }decoderを作成してそれによってjsonを一行ずつ解析しています。
jsonの最後の行が解析できたらdecoderがio.EOFを返すので、それが返されたら無限ループから抜け出しています。構造体を使ったプログラミングはほぼ初めてだったので新鮮ですごく楽しいと感じました。
また、少しGoを触ってみてjavascriptとCの中間みたいな書きごごちだと感じました。javascriptを触ったことがある方はきっとGoも楽しめると思うのでぜひ!!
- 投稿日:2021-03-01T03:40:52+09:00
For-range ループで意図した値を Goroutine に渡す
参考
こちらの動画 (Concurrency Made Easy (Practical Tips For Effective Concurrency In Go) を参考にしています。
* コードを少し変えています。問題
下記コードで出力される結果は何でしょうか?
package main import ( "fmt" "sync" ) func main() { repos := []string{"aaa", "bbb", "ccc", "ddd"} sem := make(chan int, 4) var wg sync.WaitGroup wg.Add(len(repos)) for _, repo := range repos { go func() { defer wg.Done() sem <- 1 fmt.Println(repo) <-sem }() } wg.Wait() close(sem) }答えは、
ddd ddd ddd dddです。
原因
スコープが "今" の
range
値 となっているから。
結果的に、全ての Goroutine が for-range の最後の "ddd" を取得することになっている。改善方法
無名関数の引数に Goroutine を 実行するタイミングの
range
値 を渡す。package main import ( "fmt" "sync" ) func main() { repos := []string{"aaa", "bbb", "ccc", "ddd"} sem := make(chan int, 4) var wg sync.WaitGroup wg.Add(len(repos)) for i := range repos { go func(rep string) { defer wg.Done() sem <- 1 fmt.Println(rep) <-sem }(repos[i]) } wg.Wait() close(sem) }For-range ループ利用時の注意
for-range
では 取り出す順番は保証されていません。
また、Goroutine も処理が終了したタイミングで戻ってくるので、意図した通りの順番にはならないケースが多いです。ちなみに改善後のコード実行結果は、下記のようになりました。
bbb aaa ccc ddd
- 投稿日:2021-03-01T03:40:52+09:00
【よくある間違いを避ける】For-range ループで意図した値を Goroutine に渡す
参考
こちらの動画 (Concurrency Made Easy (Practical Tips For Effective Concurrency In Go) を参考にしています。
* コードを少し変えています。問題
下記コードで出力される結果は何でしょうか?
package main import ( "fmt" "sync" ) func main() { repos := []string{"aaa", "bbb", "ccc", "ddd"} sem := make(chan int, 4) var wg sync.WaitGroup wg.Add(len(repos)) for _, repo := range repos { go func() { defer wg.Done() sem <- 1 fmt.Println(repo) <-sem }() } wg.Wait() close(sem) }答えは、
ddd ddd ddd dddです。
原因
スコープが "今" の
range
値 となっているから。
for ループが最速すぎて、結果的に、全ての Goroutine が for-range の最後の "ddd" を取得することになっている。
(注意:for-range
で返ってくる値は、配列の順番通りであることは保証していないので、環境によっては変わる可能性あり)改善方法
無名関数の引数に Goroutine を 実行するタイミングの
range
値 を渡す。package main import ( "fmt" "sync" ) func main() { repos := []string{"aaa", "bbb", "ccc", "ddd"} sem := make(chan int, 4) var wg sync.WaitGroup wg.Add(len(repos)) for i := range repos { go func(rep string) { defer wg.Done() sem <- 1 fmt.Println(rep) <-sem }(repos[i]) } wg.Wait() close(sem) }For-range ループ利用時の注意
for-range
では 取り出す順番は保証されていません。
また、Goroutine も処理が終了したタイミングで戻ってくるので、意図した通りの順番にはならないケースが多いです。ちなみに改善後のコード実行結果は、下記のようになりました。
bbb aaa ccc ddd
- 投稿日:2021-03-01T00:15:00+09:00
【Go】ポインタと配列と文字列と
Goでプログラミングの基礎を学ぶシリーズ
スクールでは教えてくれないプログラミングの基礎、データ構造とアルゴリズムをGoで学んでいくシリーズです。
そのデータ構造がどのようなものであるかは、理解を助けてくれるサイトを紹介しつつ、簡単に説明に留めさせていただきます。(ご自身でも調べてみてください!)
筆者自身、Exerciseに取り組みながら理解を深めていったので、コードの解説を中心に記事を書いていきたいと思います。
タイトル #0 はじめに (環境構築と勉強方法) #1 Pointer, Array, String (ポインタと配列と文字列と) ☜ here ~~ coming soon ~~ #2 File operations (ファイル操作) #3 Linked List (連結リスト) #4 Stack & Queue (スタックとキュー) #5 Search algorithms (検索アルゴリズム) #6 Tree (木構造) #7 Sorting algorithms (ソートアルゴリズム) #8 String pattern matching algorithms (文字列検索アルゴリズム) #9 Mapping & Hashing (ハッシュテーブルなど) ※上記予告になりますので、2回に分けたりするかもしれないです...
初回である今回は、#1 Pointer, Array, String, Pointer (ポインタと配列と文字列と) です。
これらがわからないと、#2以降のコードが全く書けないのです。A Tour of Goでいうところとの
Basics
まで、
【Go】基本文法シリーズ (筆者も大変お世話になっている記事シリーズで、A Tour of Goをかなり噛み砕いて解説してくれています)でいうところの、
1. 基礎
2. フロー制御文
3. ポインタ・構造体
4. 配列・スライス
まではコードが書けるよ!という状態で、データ構造とアルゴリズムに臨みましょう。メモリとアドレス
Pointer, Array, String 全てに通じることなので、少し説明します。
かなりざっくりとした言い方になりますが、コンピュータはCPUがメモリ上の命令を実行することで動いています。ビット(bit)とは、
コンピューターの中で扱うデータの最小単位。1ビットで2進数の1桁が0か1かを表せる。
文字や画像など、すべての情報はビットの組み合わせで表現されている。
ただし、実際にコンピューターが扱う情報の単位は8ビットを1組にしたバイト単位で表すことが多い。1バイト=8ビットである。
引用: コトバンク1byteごとに区分番号が割り振られており、それが
アドレス
です。
メモリ空間は1byteごとに番号で区分されているとも言い換えることができます。「0と1なのはわかったけれど、数字ではない"a"とか"b"とかはどうやって表すの?」
と疑問に思う方は、ASCIIコード表をみるとイメージしやすいかもしれません。
参考: 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典 アスキーコード (ASCIIコード)Pointer / ポインタ
ポインタとは、何かの位置を指し示すための仕組みや道具などのこと。
プログラミングでは、変数や関数などが置かれたメインメモリ上の番地などを格納する特殊な変数のことをポインタという。
引用: IT用語辞典 e-Wordsポインタは、メモリにある値を読み書きする時に使います。
メモリにある値が直接読み書きできることの何が嬉しいかというと、関数をまたいでメモリにアクセスすることが可能になります。
参考: C言語 ポインタのメリットと必要性【なぜなぜから真相に迫る】どういうことか。
package main import "fmt" func main() { a, b, c := 1, 1, 1 // main関数内で a に 2 をかける a *= 2 fmt.Printf("main() aの値: %d\n\n", a) // 2 // multiply2関数内で b に 2 をかける. 引数には b の値を渡す. multiply2(b) fmt.Printf("main() bの値: %d, bのアドレス: %p\n\n", b, &b) // multiply2pointer関数内で c に 2 をかける. 引数には c のアドレスを渡す. multiply2pointer(&c) fmt.Printf("main() cの値: %d, cのアドレス: %p\n\n", c, &c) } func multiply2(b int) { b *= 2 fmt.Printf("multiply2() bの値: %d, bのアドレス: %p\n", b, &b) } func multiply2pointer(c *int) { *c *= 2 // c のアドレスに格納されている値: 1 に 2 をかける. fmt.Printf("multiply2pointer() cの値: %d, cのアドレス: %p\n", *c, c) } // output > // main() aの値: 2 // multiply2() bの値: 2, bのアドレス: 0xc000016120 // main() bの値: 1, bのアドレス: 0xc0000160f8 // multiply2pointer() cの値: 2, cのアドレス: 0xc000016100 // main() cの値: 2, cのアドレス: 0xc000016100Goでのポインタの書き方などは、以下の記事がとてもわかりやすかったので、こちらを見ていただきたいです。
参考:
Goで学ぶポインタとアドレス
【Go】基本文法③(ポインタ・構造体)ざっとコードにしてしまいましたが、
b
のケースは値渡し
と呼ばれるもので、main()
内のb
と、multiply2()
内のb
は、1
という値は同じですが、異なるメモリに格納されているデータを参照しています。
c
のケースはポインタ渡し
と呼ばれるもので、main()
内のc
のアドレスをmultiply2pointer()
に渡しているので、同じアドレスに格納されているデータを参照しています。これから学ぶデータ構造とアルゴリズムのコードでも、ポインタをよく使います。
main()
にすべての処理を書いてしまうと、同じ処理を何度も書かなくてはいけなくなってしまったり、main()
自体が長くなり可読性が下がってしまったりする場合、ある機能に特化した関数を切り出していきます。(上の例だとmultiply2()
のような感じです)
異なる関数で同じデータを参照する必要がある場合、それらのデータはポインタで扱っています。(あくまでポインタの一使用例です)? Exercise
3つの変数a,b,cについて、a=b, b=c, c=aとなるような関数をポインタで実装しましょう。
☟ 解答例
package main import "fmt" func main() { a, b, c := 1, 2, 3 swap1(a, b, c) fmt.Println(a, b, c) // 1 2 3 swap2(&a, &b, &c) fmt.Println(a, b, c) // 2 3 1 } func swap1(a, b, c int) { a, b, c = b, c, a } func swap2(a, b, c *int) { *a, *b, *c = *b, *c, *a }Array / 配列
Array(配列)
はGoでは固定長のものを指します。
可変長のものはSlice(スライス)
といいます。コードの書き方は、以下の記事をご覧ください。
参考:
【Go】基本文法④(配列・スライス)
The Go Blog/ Go Slices: usage and internals
Go Slicesのブログにもある通り、Sliceは、配列へのポインタptr
、セグメントの長さlen
、およびその容量cap
で構成されます。
配列へのポインタは、通常先頭の要素slice[0]
を指します。
参考: Try Golang! Sliceってポインタなの?それともポインタじゃないの?? Exercise
任意の英語の文章(すべて小文字)で、各文字が出現する回数を出力するプログラムを作成しましょう。
例: word: hello world output > letter: h count: 1 letter: e count: 1 letter: l count: 3 letter: o count: 2 letter: count: 1 letter: w count: 1 letter: r count: 1 letter: d count: 1☟ 解答例
package main import "fmt" func main() { word := "hello world" var elements []string // 文字を入れておくSlice var elementCount []int // 文字が何回出現したかを入れておくSlice for _, w := range word { letter := fmt.Sprintf("%c", w) // w は rune型(後述) なので、 string型 に変換 if !isExist(letter, elements) { // 初めて出現した文字について実行 elements = append(elements, letter) elementCount = append(elementCount, count(letter, word)) } } for i := 0; i < len(elements); i++ { fmt.Printf("letter: %s count: %d\n", elements[i], elementCount[i]) } } // 文字が word のなかに何回出現するかカウントする func count(letter string, word string) int { count := 0 for _, w := range word { l := fmt.Sprintf("%c", w) if l == letter { count++ } } return count } // すでにカウントした文字かどうかの判定 func isExist(letter string, elements []string) bool { for _, e := range elements { if letter == e { return true } } return false } // output > // letter: h count: 1 // letter: e count: 1 // letter: l count: 3 // letter: o count: 2 // letter: count: 1 // letter: w count: 1 // letter: r count: 1 // letter: d count: 1ポイントは、
word
を頭から見ていき、初めて出現した文字の場合には、その文字がword
の中に何回出現するかカウントしてしまうというところですかね。
!isExist
で、すでにカウントした文字かどうかを判定し、カウントが重複しないようにしています。上記の例では
Slice
のみで実装していますが、map
を使えばもっとスタイリッシュに書けそうな気がします。
参考: 【Go】基本文法⑤(連想配列・ Range)package main import "fmt" func main() { word := "hello world" elements := make(map[string]int) for _, w := range word { key := fmt.Sprintf("%c", w) if _, ok := elements[key]; ok { elements[key]++ } else { elements[key] = 1 } } for k, v := range elements { // elements ▶︎ map[ :1 d:1 e:1 h:1 l:3 o:2 r:1 w:1] fmt.Printf("letter: %s count: %d\n", k, v) } }2021/03/09追記 : @kts_h さんのコメントのコードの方がよりスタイリッシュでわかりやすいので、そちらも参考にしてください!
String / 文字列
実はArrayのExerciseでも出てきていますが、Goにはcode pointを単位として文字を扱う
rune型
が用意されています。
詳しくはこちらの記事をご覧ください。
参考: Goのruneを理解するためのUnicode知識メモリへの格納は配列と同じようなイメージになりますが、stringは
terminator
(終端null文字)があります。
? Exercise
単語と2つの文字を入力させ、単語に含まれる入力した1つ目の文字を、入力した2つ目の文字に変換して出力するプログラムを作成しましょう。
例: input > papa p m output > mama☟ 解答例
package main import ( "fmt" ) func main() { var word string var b, a rune fmt.Println("Please input word and letters to convert. ex) papa p m\n> ") fmt.Scanf("%s %c %c\n", &word, &b, &a) for _, w := range word { if w == b { w = a } fmt.Printf("%c", w) } } // Please input word and letters to convert. ex) papa p m // > // papa p m // output > // mamaおわりに
Exerciseの解答例はあくまで例なので、もっといい書き方あるよ!という方、ぜひコメントをお寄せください!
説明についても、筆者自身が初心者であるため、ご指摘や補足は大歓迎でございます。株式会社Link Sportsでは、あらゆるスポーツを楽しむ人たちに送る、チームマネジメントアプリを開発しています。
未経験でも経験豊富なエンジニアの方でも、スポーツ好きなら活躍できる環境があります!
絶賛エンジニア募集中です!
Wantedly ▶︎ https://www.wantedly.com/projects/324177
Green ▶︎ https://www.green-japan.com/job/82003次回は、データ構造とアルゴリズム#2 File operations(ファイル操作)です。
乞うご期待。