1.Goの基本 - iruma-tea/go_programming GitHub Wiki

1. Goの基本

1.1 基本的な処理の流れを学ぼう

Goはパッケージという単位でソースコードを管理する。多くの組み込み関数は、パッケージのインポート(読み込み)が必要となるため、
インポートする方法についても説明する

1.1.1 main関数とinit関数の働きを知ろう

  • main関数: main関数は特別な関数で、コードを実行したときに最初に呼び出される。main関数のないコードは実行するとエラーとなる。
  • 複数の文字を出力する: fmt.Println関数は、複数の引数を渡すことができる。
  • 関数の呼び出し
    • 関数の定義:funcの後に関数名(){}と続け、実行したい処理を{}の中に書く。
    • 定義した関数は、関数名()で呼び出すことができる。
  • init関数: main関数よりも先に呼ばれる。
    • init関数は、変数の多いコードで初期設定を行うときなどに利用する。
  • コメントアウト
    • 行頭に//付ける方法
    • 複数の行を/**/で囲む方法

1.1.2 複数のパッケージをインポートしよう

複数のパッケージをインポートする場合、importの後に()をつけ、()内に改行で区切ってパッケージ名を記述する。
標準パッケージの一覧

  • Goが用意している標準パッケージは、上記、ドキュメントから確認できる。Goの学習を進めていく中で、少しずつ見ながら、理解を深めましょう。
  • パッケージに関する情報は以下のコマンドでも確認できる
    • go doc パッケージ名
    • go doc パッケージ名 関数名

1.2 変数の作り方をマスターしよう

変数は、数値や文字列など何らかのデータを入れる箱のようなもの。変数を作ることを「宣言」、変数にデータを入れることを「代入」という。
また、データには種類があり、変数を宣言するときに代入したいデータの種類を指定する。

1.2.1 変数を宣言しよう

変数の宣言にはvarを使う。このvarは、変数という意味を持つvariableの略です。
varの後に変数名とデータの種類を表すデータ型を書く。宣言と同時に初期化したい場合は、続けて=(イコール)と代入したい値を書く。
宣言した変数はコード内で必ず使うこと使用しない場合、エラーになる。

  • 複数の変数を宣言する
    • varの後に()をつけ、()内で複数の変数を定義することができる。
var (
    i int = 1
    f64 float64 = 1.2
    s string = "test"
    t, f bool = true, false
)
  • 短縮変数宣言(short variable declaration)
    • 短縮変数宣言という記述方法にすると、varを省略して変数を宣言できる。短縮変数宣言は、変数名と価を **:=(コロンとイコール)**でつなげるだけです。
    • 「xi *= 1」とすると、int型の変数xiが定義され、xiに1が代入される。
    • 「:=」を使って変数を定義すると、データ型が自動的に設定される点に注意。
    • fmt.Printf関数を使うと、変数のデータ型を確認できる。
  • 宣言方法の使い分け
    • var: 関数の外で定義することが可能
    • 短縮変数宣言は、関数の中でしか使えない
package main

import "fmt"

var (
	i    int     = 1
	f64  float64 = 1.2
	s    string  = "test"
	t, f bool    = true, false
)

func foo() {
	xi := 2
	xf64 := 1.3
	xs := "test test"
	xt, xf := true, false
	fmt.Println(xi, xf64, xs, xt, xf)
	fmt.Println(i, f64, s, t, f)
}

func main() {
	fmt.Println(i, f64, s, t, f)
	foo()
}

1.2.2 constを使って定数を宣言しよう

不変変数(定数)はconstを使って宣言する。
constで宣言した定数は後から値を代入できないため、宣言と同時に初期化する。
定数はプログラム上で書き換える必要がない場合に使用する。     var と同じく、const()で複数の定数を宣言することができる。

package main

import "fmt"

const Pi = 3.14

const (
	Username = "test_user"
	Password = "test_pass"
)

func main() {
	fmt.Println(Pi, Username, Password)
}
  • constとオーバーフロー
    • constの上限値を超える値を扱うと変数の宣言と初期化時に扱える値の範囲を超えたことを表す「オーバーフロー(overflows)」が発生する。
package main

import "fmt"

var big int = 9223372036854775807

func main() {
	fmt.Print(big)
}

1.3 データ型について学ぼう

データ型とは、コードで扱うデータの種類のこと。

1.3.1 数値型の基本を知ろう

  • 数値型
    • 正または負の値を持つ符号つきの整数型(Integer)であるint型は、32bitか64bitの数値に対応しており、コードの実行環境によって扱える数値の範囲が変わる。
    • 32bitか64bitかを確実に指定したい場合は、int64かint32を明示的に指定する。
    • 正の値のみ、符号なしの整数型(Unsigned Integer)であるuint型は、
      • unit8型で8bitの0~255の値
      • uint16型で16bitの0~65535の値
    • 複素数を扱うcomplex64型complex128型などがある。
  • 演算子を使った数値の操作
    • 数値は、+,-などの算術演算子を使った式で計算を行うことができる。
    • 「++」を使ったインクリメント、「--」を使ったデクリメント
    • シフト演算子
      • シフト演算とは二進数で表した値を左右にシフト(移動)して行う計算のことで、<<や>>を使う。
      • シフト演算子を使うと元の値から倍々に変化していくことがわかる。
package main

import "fmt"

func main() {
	fmt.Println(1 << 0) // 1を0回左にシフト
	fmt.Println(1 << 1) // 1を1回左にシフト
	fmt.Println(1 << 2) // 1を2回左にシフト
	fmt.Println(1 << 3) // 1を3回左にシフト
}

1.3.1.1 Goのリファレンス

データ型や組み込み関数の詳細を知りたい場合、下記URLからリファレンスを確認。
Goのリファレンス

1.3.2 文字列型の基本を知ろう

**"(ダブルクォート)や`(バッククォート)**で囲んだ部分は文字列として扱われる。
文字列を+でつなげると、文字列の連結を行われる。

  • 文字列から指定した文字を取得する
    • 文字列は1文字目から順番にインデックスという順番を表す番が0から振られます。
    • "Hello World"[0]でインデックスが0の「H」が取得できるが、Goの場合、ASCIIコードで出力されるため、文字列として出力するためには、string()を使った型変換が必要。
  • 文字列型の変数宣言
    • 文字列型はstring型です。string型は、次のように宣言する
  • 文字列の置き換え
    • 文字列中のある文字を置き換える際は、文字列操作のための便利な処理(関数)がまとめられたstringsパッケージをインポートして、strings.Replace関数を使用する
  • 文字列を探す
    • stringsパッケージには、他にも指定した文字列を探すstrings.Contain関数がある。
  • 文字列の改行
    • 文字列の途中で改行を入れる場合は「\n」を入れる。
package main

import "fmt"

func main() {
	var s string = "Hello World"
	fmt.Println(s)
}

1.3.3 論理値型の基本を知ろう

論理値型の**bool*。

  • 論理演算子
    • && 演算子: 論理積は左右の値がどちらもtrueであれば、trueを出力。左右のどちらかが、もしくは両方がfalseであれば、falseを出力する
    • || 演算子: 論理和は左右の値のどちらかが、trueであればは、trueを出力。どちらもfalseであれば、falseを出力
    • ! 演算子: 否定は値を反転した結果を返す。

1.3.4 データ側を変換してみよう

  • 数値のcast
    • データ型を変換することをcastという。int型の変数を定義し、**float64()**でfloat64型に変換する。
  • 文字列のcast
    • Goでstring型をint型に型変換したい場合は、strconv.Atoi関数を使う。
    • strconv.Atoi関数を使う場合、strconvパッケージのインポートが必要。

1.4 データ構造のしくみを学ぼう

  • 配列(Array): 固定長であとから長さを変えることはできない。
  • スライス(Slice): 可変長で後から長さを変えることができる。
  • マップ(Map): キー(key)と値(value)の組み合わせを管理する。

1.4.1 配列(Array)の基本を学ぼう

配列もvarを使って宣言する。「配列名 [要素数]データ型」という形で配列の要素数とデータ型を指定する。
配列の宣言とともに値を代入することが可能。この場合、{}に,(カンマ)で区切って代入する。

package main

import "fmt"

func main() {
	a := [2]int{100, 200}
	fmt.Println(a)
}

1.4.2 スライス(Slice)の基本を学ぼう

スライスを宣言する場合は、配列の宣言とはほぼ同じですが、[]に要素数を指定せずに空にする。

  • 要素を出力する
    • 配列やスライスから指定した要素を取得するには、**配列[インデックス]**といった形で書く。
    • :(コロン)を使って、取得する要素を指定することが可能。**配列[開始値:終了値]**という形で書く。
  • 要素の追加
    • append関数を使うことで、スライスの要素数を後から増やすことができる。append(スライス, 値)とすることで、スライスの後に新しい値が追加される。
  • 多次元スライス
    • スライスの中にスライスを入れることも可能。[][]データ型{...}と宣言時に[]を二つ書く。

1.4.3 make関数でスライスを作ろう

make関数を使うと、要素の値が0で初期化されたスライスが作れる。「n := make([]int,3, 5)」でint型で、
長さ(length)3、容量(capacity)が5のスライスを定義し、変数nに代入する。

  • 長さ0のスライス
    • make関数を使う方法: 長さ0のスライスをメモリに確保する。
    • make関数を使わない方法: nilという状態で、メモリに確保されない。

1.4.4 バイト配列を作ろう

要素がbyte型のスライス。「[]byte{値1,値2,...}」でbyte型の要素を持つスライスを定義できる。
要素がbyte型のスライスや配列は、バイト配列とも呼ばれており、要素の値はASCIIコードとして扱える。
ASCIIコードの一覧

1.4.5 マップ(Map)の基本を学ぼう

キー(Key)と値(value)の組み合わせで管理する。
「map[キーのデータ型]値のデータ型{キー1:値1, キー2:値2,...}」という形で宣言し、「map[キー]」で値を取得できる。

  • マップとmake関数
    • make関数で空のマップを作ってから、空のマップに対して値を入れていくことができます。    
package main

import "fmt"

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

1.5 関数で処理をまとめよう

関数とは、ひとまとりの処理に名前をつけ、何度も呼び出せるようにしたものです。

1.5.1 関数の基本を知ろう

関数を定義するときにはfuncを使う。

  • 引数と返り値がないadd関数の定義
func add() {
  fmt.Println("add function")
}
  • 引数を受け取る関数の定義
func add(x int, y int) {
  fmt.Println("add function")
  fmt.Println(x + y)
}
  • 返り値を返す関数の定義
func add(x int, y int) int {
  return x + y
}
  • 返り値を複数返す関数の定義
func add(x, y int) (int, int) {
  return x + y, x - y
}
  • Named return values
    • Named return valuesとは、返り値に名前をつけること。
      • 返り値が1つの場合は返り値に名前をつけない。
      • 返り値が複数ある場合は返り値に名前をつける。
func cal(price, item int) (result int) {
  result = price * item
  return result
}
func cal(price, item int) (result int, tax float64) {
  tax = 1.1
  result = int(float64(price*item) * tax)
  return result, tax
}
  • 関数リテラル
    • 関数は変数に入れることも可能
    • 変数に代入した関数は、変数名()で呼び出すことができる。
func main() {
  f := func(x int) {
    fmt.Println("inner func ", x)
  }
  f(1)
}
func main() {
  func(x int) {
    fmt.Println("inner func ", x)
  }(1)
}

1.5.2 クロージャーのしくみを学ぼう

関数リテラルは、同じ関数の中で宣言された変数にアクセスできる。この仕組みはクロージャーと呼ばれる。

func main() {
  x := 0
  increment = func() int {
    x++
    return x
  }
  fmt.Println(increment())
  fmt.Println(increment())
  fmt.Println(increment())
}
  • 関数を戻り値にする
    • 戻り値で関数を返す関数について、returnで関数リテラルを戻り値として、呼び出しもとに渡すことができる
func increamentGenerator() func() int {
  x := 0
  return func() int {
    x++
    return x
  }
}

1.5.3 可変長引数を使ってみょう

可変長引数とは、関数が受け取る引数の数を変えられる引数のこと。
可変長引数にするには、(param1, param2 int)を(params ...int)という形式に書き換える。

  • 可変長引数とfor
func foo(prams ...int) {
  fmt.Println(len(params), params)
  for _, param := range params {
    fmt.Println(Param)
  }
}

可変長引数の実体はスライスです。そのため、可変長引数を受けとる関数に、スライスを渡して呼び出すこともできる。
スライスを引数にするさいは、関数名(スライス...)という形で、スライスの後に「.」を3つ続ける。

func foo(params ...int) {
  fmt.Println(len(params), params)
  for _, param := range params {
    fmt.Println(param)
  }
}

func main() {
  s := []int{1, 2, 3}
  fmt.Println(s)
  foo(s...)
}
⚠️ **GitHub.com Fallback** ⚠️