20210301のGoに関する記事は6件です。

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型で扱うにはどうすれば良いか?
今回の例の場合はMarch3と扱うにはどうすれば良いか?

これは簡単で、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型で扱える。
以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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のスライスを返す関数のようです。

ここまできたら、材料は揃ったのであとは解くだけです。

コード

Maps
package 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)
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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も楽しめると思うのでぜひ!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【よくある間違いを避ける】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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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がメモリ上の命令を実行することで動いています。

メモリのイメージ ☟
スクリーンショット 2021-03-08 1.26.06.png

ビット(bit)とは、
コンピューターの中で扱うデータの最小単位。1ビットで2進数の1桁が0か1かを表せる。
文字や画像など、すべての情報はビットの組み合わせで表現されている。
ただし、実際にコンピューターが扱う情報の単位は8ビットを1組にしたバイト単位で表すことが多い。1バイト=8ビットである。
引用: コトバンク

参考: コンピューターは0と1だけで計算している?

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のアドレス: 0xc000016100

Goでのポインタの書き方などは、以下の記事がとてもわかりやすかったので、こちらを見ていただきたいです。
参考:
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
スクリーンショット 2021-03-08 1.01.01.png
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文字)があります。
スクリーンショット 2021-03-08 2.00.17.png

? 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(ファイル操作)です。
乞うご期待。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む