search
LoginSignup
1

More than 1 year has passed since last update.

posted at

updated at

Go言語の基本文法(定義編)

スターフェスティバル Advent Calendar 2020 の16日目です。

弊社のプロダクト「ごちクル」の一部処理がGoに改修されたこともあり勉強し始めたので基本的な文法(主に変数などの定義)をまとめてみました。

※ Udemyのこちらの教材を参照元にしています。

packageとimport

Goではプログラムのエントリーポイントとなるのがmainパッケージmain関数
パッケージのグループは名前空間とも呼ばれる。


// パッケージの宣言
package main

// ライブラリ
import "fmt"

// main関数の定義と実行
func main() {
  fmt.Println("こんにちは!")
}
// こんにちは!

ソースファイルは必ず1つのパッケージに所属させる必要があるため、
今回の場合はmain宣言とmain関数が存在しないとエラーになる。
また1つのファイルに複数のパッケージを設定することはできない。

パッケージはディレクトリ単位で管理される。
1つのディレクトリに複数のパッケージは置けない。

fmt(フォーマット)は、Goが標準で提供するライブラリ(パッケージ)の1つ。

変数宣言

varを使った変数宣言の場合

package main

import "fmt"

func main() {
  // 色々な型で変数宣言
  var i = 1
  var f64 float64 = 1.2
  var s string = "test"
  var t, f bool = true, false  // 複数の変数宣言も可能

  // 出力
  fmt.Println(i, f64, s, t, f)
}
// 1 1.2 test true false

var変数を()丸括弧で囲ってまとめて宣言することもできる。

// 省略

func main() {
  var (
    i int = 1
    f64 float64 = 1.2
    s string = "test"
    t, f bool = true, false
  )
  fmt.Println(i, f64, s, t, f)
}
// 1 1.2 test true false

:=を使った変数宣言

:=を使った Short variable declaration の書き方はこう。


// 省略

func main() {
  xi := 1
  xf64 := 1.2
  xs := "test"
  xt, xf := true, false
  fmt.Println(xi, xf64, xs, xt, xf)
}
// 1 1.2 test true false

:= 使った変数宣言のvarとの違いは、
関数の中でしか宣言できないこと。
上記例の場合、func main() {}の外で宣言すると、エラーになる。

この宣言では型が指定されていないuntypedとなり、型は自動判定される。
このuntypedの変数に型を付けたい場合は、下記のようにする。


// 省略

func main() {
  xf64 := 1.2

  fmt.Printf("%T", xf64)
  // 型を確認できるPrintfで確認
  // float64

  var xf32 float32 = 1.2
  // 型をfloat32と明示的に宣言

  fmt.Printf("%T", xf32)
  // 型を確認できるPrintfで確認
  // float32
}

型を明示的に宣言したいときはvarで、
簡単に宣言したいときは:=を使う。

constを使った変数宣言

constは定数宣言。
変数名は、頭文字を大文字にすると他のファイルからも参照できるグローバル変数になる。

const Pi = 3.14

constも型を指定しないuntypedの変数。

型について

数字型

数値型は下記のように宣言する。
数値型の一覧ドキュメントはこちら。

package main

import "fmt"

func main() {
  var (
    u8  uint8     = 225
    i8  int8      = 127
    f32 float32   = 0.2
    c64 complex64 = -5 + 12i
  )
  fmt.Println(u8, i8, f32, c64)
  fmt.Printf("type=%T value=%v", u8, u8)
}
// 225 127 0.2 (-5+12i)
// type=uint8 value=225

Printfのドキュメントはこちら)

Printfで型やvalueを確認することができる。

// 省略
  x := 1 + 1
  fmt.Println(x)
  fmt.Println("1 + 1 =", 1+1)
  fmt.Println("10 - 1 =", 10-1)
  fmt.Println("10 / 2 =", 10/2)
  fmt.Println("10 / 3 =", 10/3)
  fmt.Println("10.0 / 3 =", 10.0/3)
  fmt.Println("10 / 3.0 =", 10/3.0)
  fmt.Println("10 % 2 =", 10%2)
  fmt.Println("10 % 3 =", 10%3)
}
// 2
// 1 + 1 = 2
// 10 - 1 = 9
// 10 / 2 = 5
// 10 / 3 = 3
// 10.0 / 3 = 3.3333333333333335
// 10 / 3.0 = 3.3333333333333335
// 10 % 2 = 0
// 10 % 3 = 1

インクリメントを使うこともできる。

// 省略
  x := 0
  fmt.Println(x) // 0

  x++
  fmt.Println(x) // 1

  x--
  fmt.Println(x) // 0
}

文字列型

文字列型は下記のように定義できる。

package main

import "fmt"

func main() {
  fmt.Println("Hello World")
  fmt.Println("Hello " + "World")
}
// Hello World
// Hello World

stringのキャスト

文字列の一番初めの文字を表示したい時、
fmt.Println("Hello World"[0])このようにインデックスを指定するだけでは、アスキーコードが取得されてしまい、取得できない。

stringを使ってキャストする必要がある。

package main

import (
  "fmt"
  "strings" //追加
)

func main() {
  fmt.Println("Hello World"[0]) // 72
  fmt.Println(string("Hello World"[0])) // H
}

エスケープ

バッククオートで囲うと、エディタの改行がそのまま反映できる。
ダブルクオートの中にダブルクオートを入れたい場合は \(バックスラッシュ)でエスケープ

  fmt.Println(`Test
Test`)

  fmt.Println("\"")
}
// Test
// Test

// "

論理値型

package main

import "fmt"

func main() {
// %T = 型
// %v = 値
// %t = 単語、true または false 指定した型じゃないと正しく表示されない

//   var t, f bool = true, false
  t, f := true, false
  fmt.Printf("%T %v %t\n", t, t, t)
  fmt.Printf("%T %v %t\n", f, f, f)

// 論理演算子 && (and)
  fmt.Println(true && true) // 真かつ真 = 真
  fmt.Println(true && false) // 真かつ偽 = 偽
  fmt.Println(false && false) // 偽かつ偽 = 偽

// 論理演算子 || (or)
  fmt.Println(true || true) // 真もしくは真 = 真
  fmt.Println(true || false) // 真もしくは偽 = 真
  fmt.Println(false || false) // 偽もしくは偽 = 偽

// 論理演算子 ! (not)
  fmt.Println(!true) // false 条件の反対の結果
  fmt.Println(!false) // true 条件の反対の結果
}

型変換(キャスト)

Goではinteger -> floatのような数値同士の型変換は簡単にできるが、文字列 -> 数値のような型変換は少しコツがいる。

integer->floatへの型変換(簡単)

下記のようにスムーズに変換できる。
Printfのドキュメントはこちら)

package main

import "fmt"

func main() {
  var x int = 1
  xx := float64(x) // int を float64 に型変換

  fmt.Printf("%T %v %f\n", xx, xx, xx)
}
// float64 1 1.000000
// int -> float64 へ型変換できている

string -> intに型変換(ちょっとコツいる)

文字列の相互変換(コンバージョン)用のライブラリstrconvを使用して型変換する。
strconvのドキュメントはこちら)

package main

import (
  "fmt"
  "strconv" //追加
}

func main() {
  var s string = "14"
  i, _ := strconv.Atoi(s) // int型に変換
  fmt.Printf("%T %v\n", i, i)
}
// int 14
// string -> int へ型変換できている

Atoi関数はAscii to integerの略。

i, _ := strconv.Atoi(s)_について。
Atoiは返り値を2つ(int, error)返す関数。

2つ目の返り値はエラーハンドリング用だが、
i := strconv.Atoi(s)このように省略するとエラーになってしまう。

今回のように使わない場合は_で省略することができる。

配列

goでの配列の基本的な書き方はこんな感じ。
[]スクエアブラケットの中身は、配列の個数が入る。
配列の場合は、[2]int この部分が型になる。

package main

import "fmt"

func main() {
  // 配列
  var a [2]int
  a[0] = 100
  a[1] = 200
  fmt.Println(a)


  // この書き方も可
  var b [2]int = [2]int{100, 200}
  fmt.Println(b)
}
// [100 200]
// [100 200]

配列は[2]intこのように配列の個数と型が決まっているため、配列にappendなどで追加してリサイズすることはできず、エラーとなる。

// 省略

func main() {
  var b [2]int = [2]int{100, 200}
  b = append(b, 300) //このように配列に追加しようとするとエラー
  fmt.Println(b)
}
// first argument to append must be slice; have [2]int
// b redeclared in this block

ではどうやって追加するのか?
配列ではなくスライスを使用する。

スライス

スライスは配列と違い、個数を指定せず下記のように宣言する。

// 配列
var b [2]int = [2]int{100, 200}

// スライス
var b []int = []int{100, 200}

スライスは個数を追加することができる。

package main

import "fmt"

func main() {
  n := []int{1, 2, 3, 4, 5, 6}
  // スライスに値を追加
  n = append(n, 100, 200, 300, 400)
  fmt.Println(n)
}
// [1 2 3 4 5 6 100 200 300 400]

配列はリサイズできない。
スライスはリサイズできる。

スライスにインデックスを指定する

スライスをインデックス指定で取得できる。
●〜●番目という風にレンジで取得する際の数え方が独特。

package main

import "fmt"

func main() {
  n := []int{1, 2, 3, 4, 5, 6}

  // 配列の●番目
  fmt.Println(n[2])

  // 配列の●〜●番目
  fmt.Println(n[2:4])

  // 配列の〜●番目まで
  fmt.Println(n[:2])

  // 配列の●番目以降
  fmt.Println(n[2:])

  // 配列すべて表示
  fmt.Println(n[:])
}
// 3
// [3 4]
// [1 2]
// [3 4 5 6]
// [1 2 3 4 5 6]

スライスの値を書き換える

指定したインデックスの値を書き換える。

// 省略
func main() {
  n := []int{1, 2, 3, 4, 5, 6}
  n[2] = 100
  fmt.Println(n)
}
// [1 2 100 4 5 6] 

スライスを入れ子にする

// 省略
func main() {
  n := []int{1, 2, 3, 4, 5, 6}
  var board = [][]int{
    []int{0, 1, 2},
    []int{3, 4, 5},
    []int{6, 7, 8},
  fmt.Println(board)
}
// [[0 1 2] [3 4 5] [6 7 8]]

スライスに追加する

// 省略
func main() {
  n := []int{1, 2, 3, 4, 5, 6}
  n = append(n, 100, 200, 300, 400)
  fmt.Println(n)
}
// [1 2 3 4 5 6 100 200 300 400]

map(連想配列)

mapの基本的な書き方は下記の通り。

package main

import "fmt"

func main() {
  m := map[string]int{"apple": 100, "banana": 200}
  fmt.Println(m)
}
// map[apple:100 banana:200]

mapをキーを指定して取り出し

// 省略func main() {
  m := map[string]int{"apple": 100, "banana": 200}

  // キーを指定してvalueを取り出す
  fmt.Println(m["apple"])

  // banana のvalueを 300 で上書き
  m["banana"] = 300
  fmt.Println(m)
}
// 100
// map[apple:100 banana:300]

mapに追加する

// 省略
func main() {
  m := map[string]int{"apple": 100, "banana": 200}

  // 追加する
  m["new"] = 500
  fmt.Println(m)
}
// map[apple:100 banana:300 new:500]

mapで存在しないキーを使った場合

存在しないキーを指定した場合は0が返る。
mapの宣言の際、2つ目の返り値を指定すると、値が存在するかどうかbool型で確かめることができる。

// 省略
func main() {
  m := map[string]int{"apple": 100, "banana": 200}

  // 存在しないキーを指定
  fmt.Println(m["nothing"])

  // 2つ目の返り値を指定
  v, ok := m["apple"]
  fmt.Println(v, ok)

}
// 0
// 100 true

makeを使ったmapの初期化

makeを使うと、空のmapを作成できる。
下記では空のmapを:=で定義した。

func main() {
  m := make(map[string]int)
  m["pc"] = 5000
  fmt.Println(m)
}
// m["pc"] = 5000

対してvarで同じように定義すると、panicというエラーが起きる。

func main() {
  var m map[string]int
  m["pc"] = 5000
  fmt.Println(m)
}
// panic: assignment to entry in nil map

これは宣言はしているものの、メモリー上に入れるmapがないのでエラーとなる。

以上、Goの色々な定義の仕方をまとめてみました。
定義編はここまでにします。
また後日、続きを書きたいと思います。

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
What you can do with signing up
1