Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@MOssan-32

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の色々な定義の仕方をまとめてみました。
定義編はここまでにします。
また後日、続きを書きたいと思います。

1
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
MOssan-32
30代からのジョブチェンジで駆け出したばかりのWeb系エンジニア

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?