LoginSignup
3
0

More than 3 years have passed since last update.

Golang 入門

Last updated at Posted at 2020-03-27

個人的なメモとして用いる。

GOの特徴

  • コンパイラ言語
  • 静的型付け言語
  • 文法がシンプル
  • 並行処理プログラミング
  • クロスコンパイルが可能
  • 標準パッケージが豊富

ソースコードの構成

main.go
// パッケージの定義
package main

// 標準パッケージや外部ライブラリのインポート
import "fmt"

// main関数の定義
func main {
  fmt.Println("Hello World")
}

プログラムはどこから実行されるのか

  • main関数
    • mainパッケージに存在する
    • ソースコードを追う時はここから
    • プログラムのエントリポイントで、初期化後はここから実行される
    • webサーバの場合は常に実行されているので代わりにhttpハンドラを読む

GOPATH

  • GOPATHとは?
    • Goのソースコードやビルドされたファイルが入るパスが設定されている
    • インポートされるパッケージもここから検索される
    • 環境変数として設定される
    • デフォルトが決まっている
      • Unix系:$HOME/go
      • Windows:%USERPROFILE%\go
    • 複数設定できる
    • go env GOPATH コマンドで取得可能

実際のGOPATH

  • $GOPATH
    • bin
      • fuga ビルドされた実行可能ファイルが入る
    • pkg
      • darwin_amd64 ビルドされたパッケージが入る
        • hoge.a
    • src
      • github.com
        • Kanai-Yuki
          • sample project
            • main.go 実行ファイル
          • sample2 project
            • main.go 実行ファイル

Goプログラムの構成要素

  • 関数
  • 変数
  • 定数
  • パッケージ

組み込み型(最初から使える)

種類 ゼロ値
整数 int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64
uintptr, byte, rune
0
浮動小数点数 float32, float64 0
複素数 complex64, complex128 0
文字列 string ""
真偽値 bool false
エラー error nil

型のキャスト

下のsample.goはコンパイルエラーになる。
原因はif文で用いる変数と浮動小数点数の不一致。

sample.go
var sum int
sum = 5 + 6 + 3
avg := sum / 3

// float64(avg)とキャストすればエラーではなくなる
if avg < 4.5 {
    fmt.Println(avg)
}

キャストできない場合には、エラーとなる
例)int("hello world") など

コンポジット型

複数のデータ型が集まって1つの型となっている

型の種類 説明 ゼロ値
構造体 型の異なるデータ型を集めたデータ型 全フィールドがゼロ値
配列 同じ型のデータを集めて並べたデータ型 要素が全てゼロ値
スライス 配列の一部を切り出したデータ型 nil
マップ キーと値をマッピングさせたデータ型 nil

構造体

  • 型のデータ型の変数を集めたデータ構造
    • 各変数はフィールドと呼ばれる
    • フィールドの方は自由
    • フィールドの方にはコンポジット型やユーザ定義型も使用可能
sample.go
type Person struct {
    Name string
    age  int
}

bob := Person{"Bob", 30}
fmt.Println(bob.Name, bob.Age) // Bob 30

また以下のように初期化関数を作成することで初期化をすることも一般的に行われます。

sample.go
func newPerson(name string, age int) *Person {
     person := new(Person)
     person.Name = name
     person.Age = age
     return person
}

func main(){
  var jen *Person = newPerson("Jennifer", 40)
  fmt.Println(jen.Name, jen.Age) //=>Jennifer 40
}

配列

  • 同じ型のデータを集めて並べたデータ構造
    • 要素の型は全て同じ
    • 要素数が違えば違う型
    • 要素数は変更できない
    • 型は型リテラルで記述することが多い
sample.go
// 初期化
var n [5]int
var n [5]int{1, 2, 3, 4, 5}
n := [...]{1, 2, 3, 4} //lengthを値から推論
n := [...]{3: 20, 4: 11} //要素数: 5, n[3] = 20, n[4] = 11

// アクセス
n[1] // 添字は変数可
n[2:3] //スライス演算

スライス

  • 配列の一部を切り出したデータ構造
    • 要素の型は全て同じ
    • 要素数は型情報に含まない
    • 背後に配列が存在する
sample.go
// 初期化 // アクセスは配列と同様
var n []int // 変数にゼロ値を代入
n := make([]int, 3, 10) // 長さと容量を指定して初期化, 各要素䛿ゼロ値で初期化される
var n = []int{1, 2, 3, 4, 5} // スライスリテラルで初期化, 要素数は指定しない, 自動で配列が作られる

n = append(n, 10, 30) // 要素の追加 // 容量が足りない場合は背後の配列が再確保される
len(n) // 長さ
cap(n) // 容量

マップ

  • キーと値をマッピングさせるデータ構造
    • キーと値の型を指定できる
    • キーには「==」で比較できる型しかNG

型リテラル
var n map[string]int

sample.go
// 初期化
var n map[string]int
var m = make(map[string]int)
n := make(map[string]int, 10) // 容量の指定
m = map([string]int){"a": 2, "b": 4}

// 操作
n["a"] // アクセス
n["a"] = 20 // キーの値を上書き

n, ok := m["a"] // キー存在の確認, ok << true, false
if _, ok := m["no"]; !ok {
    fmt.Println("そのキーはありません")
}

delete(m, "z") // 要素の削除
sample.go
// マップのバリューがスライス
var a map[string][]int
a = map[string][]int{
    "a": []int{1, 2, 3},
    "b": []int{4, 5, 6},
    "c": []int{7, 8, 9},
}
fmt.Println(a["a"][0])

ユーザ定義型

  • typeで名前をつけて新しい型を定義する type 型名 基底型
sample.go
// 組み込み型を基にする
type MyInt int

// 他のパッケージを基にする
type MyWriter io.Writer

// 型リテラルを基にする
type Person struct {
    Name string
}

型リテラル

  • 型リテラルとは
    • 型の具体的な定義を書き崩した型の表現方法
    • コンポジット型などを表現するために使う
    • 変数定義やユーザ定義型などで使用する
sample.go
// intのスライスを用いた変数定義
var n []int

// mapの型リテラルを用いた変数定義
var m map[string]int

リテラル = 識別子(名前)が付与されていないもの

関数

  • 一連の処理をまとめたもの
    • 引数で受け取った値を基に処理を行い戻り値として結果を返す機能
    • 引数:関数䛾入力となるものf(x)の場合x
    • 戻り値(返り値): 関数の出力となるもの
  • 戻り値がある場合䛿変数に代入したり式中で使う
    • x := f(12, 1+1, y)

組み込み関数

関数名 機能
print, println 表示を行う
make コンポジット型の初期化
new 指定した型のメモリ
len/cap スライスの長さ/容量を返す
copy スライスのコピー
delete マップから指定したキーのエントリ
complex 複素数型を作成
imag/real 複素数の虚部/実数部を取得
panic/revocer パニックを起こす/回復する

関数の定義

sample.go
// 関数の定義方法
func add(x, y int) int {
}

// 複数の戻り値を返す
func swap(x int, y int) (int, int) {
    return x, y
}

// 名前付き戻り値
func swap(x y int) (x2, y2 int) {
    x2, y2 := x, y
    return
}

func main() {
    x, y := swap(1, 2) // 返り値はカンマで区切って取得する
}

クロージャ

  • 無名関数とも呼ばれる
  • 定義と実行のタイミングを気をつける
    • 関数外の変数を参照している場合
    • 実行のタイミングでは値が変わっている可能性がある
sample.go
// 「引数に文字列を取り、戻り値に文字列を返す関数」を返す関数
func later() func(string) string {
    var store string

    // 前の関数の呼び出し時に格納されている変数の値を返す
    return func(next string) string {
        s := store
        store = next
        return s
    }
}

func main() {
    f := later()
    fmt.Println("a: " + f("Golang"))
    fmt.Println("b: " + f("is"))
    fmt.Println("c: " + f("awesome!"))
    fmt.Println("d: " + f(""))
}

メソッドとレシーバ

  • Goではクラスの概念がないが、ユーザ定義型に対してメソッドを定義することができる
    • typeで型を定義し、レシーバ・ユーザ定義型を指定することでメソッドを定義することができる
  • レシーバにできる型
    • typeで定義した型
      • ユーザ定義型をレシーバにできる
    • ポインタ型
      • レシーバに変更を与えたい的
    • 内部にポインタを持つ型
      • マップやスライスなどもレシーバにできる
sample.go
func (<レシーバ引数>) <関数名>([引数]) [戻り値の型] {
    [関数の本体]
}
sample.go
func (p Person) intro(greetings string) string{
    return greetings + " I am " + p.Name
}

type T int

func (t *T) M() {
    fmt.Println("method")
}

func main(){
    bob := Person{"Bob", 30}
    fmt.Println(bob.intro("Hello")) // Hello I am Bob

    var t T
    p := &t
    p.M() // method
}

変数

  • メモリに名前をつけて値を格納する
  • 変数は型を持つ
  • 変数に違う型の値を代入することはできない
  • 暗黙の型演算がない
  • 型推論がある

変数定義

sample.go
// 変数定義と代入を同時に
var n int = 100
// 変数代入時に型推論
var n = 100
// 変数に後から代入
var n int
n = 100
// varを省略, 型推論
n := 100
// まとめて定義
var (
    n = 100
    b = 1000
)

定数

  • 値の変わらないもの
  • リテラルで記述される場合が多い
  • 型推論ではデフォルトの型を使用
  • 桁区切りには_を用いる
    • 例)100_000_000

定数定義

sample.go
const n int = 100
const n = 100
const n = "hello" + "world" // 定数式, コンパイル時に計算される

const (
    n = 100
    m = 200
)

制御構文

条件分岐: if

sample.go
// 通常のif文
if x == 0 {
    fmt.Println("xは0です")
} else if x == 1 {
    fmt.Println("xは1です")
} else {
    fmt.Println("xは2以上です")
}

// 代入if文
if a := f(); a < 1 {
    fmt.Println(a)
} else {
    fmt.Println(a*2)
}

条件分岐: switch

sample.go
switch a {
case 1, 2:
    fmt.Println("a is 1 or 2")
case a == 3:
    fmt.Println("a is 3")
default
    fmt.Println("default")
}

繰り返し: for

Golangの繰り返しはfor文のみ

sample.go
// 初期化; 継続条件; 更新
for i := 0; i <= 10; i++ {
    fmt.Println(i)
}

// 継続のみ
for i <= 100 {
}

// rangeを使った繰り返し
for i, v := range []int{1, 2, 4, 8} {
    fmt.Println(i, v)
}

ひたすら簡単なおみくじプログラム

sample.go
package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().Unix())

    results := map[int]string{
        0: "凶",
        1: "小吉",
        2: "小吉",
        3: "吉",
        4: "吉",
        5: "中吉",
        6: "大吉",
    }

    for i := 0; i <= 10; i++ {
        num := rand.Intn(8)
        if a := results[num]; a == "" {
            fmt.Printf("サイコロに%vの目はありません\n", num)
        }
        fmt.Printf("あなたの今年の運命は%vです\n", results[num])
    }
}

ポインタ

  • ポインタ = 0xc000016068 的なもの
  • 変数の格納先を表す値
    • 値で渡される型の値に対して破壊的な操作を加える際に利用する
      • 破壊的な操作 = 関数を出てもその影響が残る
sample.go
func f(xp *int) {           // intのポインタ型
    fmt.Println("xp", xp)   // xp 0xc000016068
    fmt.Println("*xp", *xp) // *xp 0
    fmt.Println("&xp", &xp) // &xp 0xc00000e028

    *xp = 100               // *でポインタの指す先に値を入れる
}

func main() {
    var x int      // ゼロ値 = 0
    f(&x)          // &でポインタを取得する
    fmt.Println(x) // x == 100
}
  • 以下の型では内部でポインタが用いられているためポインタを使用する必要がない
    • スライス
    • マップ
    • チャネル
sample.go
ns := []int{10, 20, 30}
ns2 := ns
ns[1] = 200
fmt.Println(ns[0], ns[1], ns[2])    // 10 200 30
fmt.Println(ns2[0], ns2[1], ns2[2]) // 10 200 30

パッケージ

  • 関数や定数・変数・型を意味のある単位でまとめたもの
  • Goプログラムはパッケージの組み合わせでできあがる
    • mainパッケージから別のパッケージをimportする
      • importすることで様々な機能が使えるようになる

標準パッケージ:https://golang.org/pkg/

package名 用途
fmt 書式に関する処理など
net/http webサーバ
archive, comress zipなどの圧縮形式
encording JSON, XML, CSV, TSVなどのファイル形式
html/template web用のHTMLを生成する機能
os, path/filepath osへのアクセスや操作、ファイル操作など

パッケージ外へのエクスポート

  • 先頭を大文字にした識別子がエクスポートされる
    • var Abc string // exported
    • var abc string // unexported

go get でライブラリの取得をする

  • goのライブラリを取得するコマンド
  • 依存するライブラリも一緒に取得してくれる
  • 指定した場所からダウンロード&インストールしてくれる
$ go get github.com/tenntenn/greeting
$ ls $GOPATH/src/github.com/tenntenn/greeting
README.md greeting.go

go modコマンド

  • go modコマンドを有効にする
    • 環境変数のGO111MODULEonにする
  • go modコマンドのサブコマンド
コマンド 処理
go mod init
go mod init モジュール名
指定したモジュール名でgo.modファイルを作成する
モジュール名を省略するとGOPATHから推測する
go mod tidy 使用していないパッケージのgo.modからの削除
必要なパッケージのダウンロードとgo.modへの追加
go mod why 指定したパッケージがなぜ必要になったか表示
go mod vendor 依存するパッケージをvendor以下にコピーする
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0