はじめに

前回の記事の続きです。
前回の記事はこちら

Go言語の基礎

GoはCに似たコンパイラ型言語です。ですが、このコンパイル速度は非常に速く、
この言語のキーワードもたったの25個です。

なるほど🤔

予約語

break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

C言語だと defer func interface … etcが見たことないような。
制御構文で使用するもの以外はほとんど見たことない気がします。
逆にC言語にある typedef とか volatile はないが、どれかに含まれている・・・?

こんにちは、Go

Hello,Worldのプログラムが紹介されているので、自分の環境でもコンパイル&実行してみます。
環境は前回の記事で構築したものを使用します。

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
    fmt.Printf("Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい\n")
}

main.go を上記のものに書き換えて、実行してみます。
以下のような感じになりました。

vscode_01

GoはUTF-8文字列と識別子(なぜならUTF-8の発明者もGoの発明者と同じだからです。)を使用しますので、はじめから多言語サポートを有しています。

初めて知りました。

Goの基礎

変数の定義

var キーワードで変数宣言できるらしい

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var test int

var hoge, fuga int

var foo = 0

var bar, foobar int = 1, 2
or
var bar, foobar = 1, 2
or
bar, foobar := 1, 2

_ は変数の値を捨ててしまう

1
_ , b := 3, 4

Scala とかでも見たことあるような。 新しめの言語にはついている印象です。
未使用の変数があるとコンパイルエラーになる

1
2
3
4
5
package main

func main() {
    var i int
}

これは一番最初なぜコンパイルエラーになるのかと思いましたが、
コンパイラレベルで未使用変数やimport を検知してくれると後々コードが綺麗になりやすいと思いました。
コードを改修するときに、IDE の力を借りて消していくよりかは、誰がどうつくっても未使用の変数が消えている方がありがたいです。

定数

const キーワードで宣言します。

1
2
3
const constantName = value
//もし必要であれば、定数の型を明示することもできます:
const Pi float32 = 3.1415926

これは他の言語でも割とメジャーなものですね。

下記のように () を使用して複数宣言ができたりします。 これは const に限らず var でもできますね。

1
2
3
4
const (
    value1 = 1.2
    value2 = 2.1
)

ビルトイン基本型

このあたりはさらっと読みます。
boolean ・・・ 論理型
int, uint ・・・数値型

rune, int8, int16, int32, int64とbyte, uint8, uint16, uint32, uint64 のようにbit指定もできるようです。

Rust もこんな感じだったような。

float32, float64 ・・・浮動小数点型
complex128, complex64 ・・・複素数型

複素数型というものがあるんですね・・・。
他の言語では見たことない気がします。
最近高校数学を復習しているのですが、Goで問題を解けるようにコードを書く日がくるかもしれません。

文字列

string ・・・文字列型

Goの文字列はすべてUTF-8コードが採用されています。文字列は一対のダブルクォーテーション("")またはバッククォート( )で括られることで定義されます

これは他の言語と比べて同じですね。
バッククォートは JavaScript ですと変数展開可能な文字列を書きたいときに使ったりしますね。

Goの文字列は変更することができません。

なるほど🤔

[]byte 型に変換したあとに入れ直して、string 型に変換でいけるらしい

※補足
string 型にキャストした際に新しくメモリに割り当てられていると思うので、変換前・後で参照しているものは違いそう.
新しく string の文字列が作られている場所を参照しています。
(Javaとか他の言語も文字列は不変だったと思います。)

vscode_02

Goでは+演算子を使って文字列を連結することができます

+ 演算子での連結。 Java とかだと + が遅いから StringBuilder を使う風潮がありましたが、 Go でもありそうです。 (調べてない)

文字列の修正もこのように書けます

スライス

もし複数行の文字列を宣言したくなったらどうしましょうか?この場合` で宣言することができます:

これは便利かも

エラー型

Goにはビルトインのerror型があります。専らエラー情報の処理に使用されます。Goのpackageの中にはエラー処理を行うerrorsというパッケージがあります。

他言語で使う例外処理の Exception に似てる気がします。
try-catch しない分見やすさはあります。

Goデータの低レイヤの保存

図を見てみると、
int4byte ,
int32 も同じで 4byte 使用してますね。
[]byte の要素が 5個あるものは 5byte になってます。
ここは C言語 でも同じような動作な気がする。

テクニック

グループ化による宣言

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import(
    "fmt"
    "os"
)

const(
    i = 100
    pi = 3.1415
    prefix = "Go_"
)

var(
    i int
    pi float32
    prefix string
)

こんな感じにかけるというものですね。(さっき書いてしまった)

iota列挙型

Goではiotaというキーワードがあります。このキーワードはenumを宣言する際に使用されます。

enum がこれらしい

1
2
3
4
5
const(
    blue = iota
    yellow
    red
)

自分でも書いてみました。

vscode_03

上記のように 0,1,2 で出力されるようです。
当然 iota キーワードがないとコンパイルエラーになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
エラー
const(
    blue
    yellow
    red
)
エラー
const(
    blue
    yellow = iota
    red
)

Goプログラムのデザインルール

  • 大文字で始まる変数はエクスポート可能です。
    つまり、他のパッケージから読むことができる、パブリックな変数だということです。
    対して小文字で始まる変数はエクスポートできません。これはプライベート変数です。
  • 大文字で始まる関数も同じです。
    classの中でpublicキーワードによってパブリック関数となっているのと同じです。
    対して小文字で始まる関数はprivateキーワードのプライベート関数です。

文字の先頭が大文字/小文字で区別するのは面白いかも。 private public はちょっと長いですからね。

array、slice、map

array

固定長の配列
宣言

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var arr [5]int
var arr2 = [3]int{1,2,3}
arr3 := [3]int{1,2,3}

// 二次元配列
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
darr := [2][2]int{
	[2]int{1, 2},
	[2]int{3, 4},
}
fmt.Println(darr)
slice

動的な配列
宣言

1
2
var fslice []int
slice := []int{1,2,3}

要素の取得

1
2
3
var arr = []int {1,2,3,4,5,6}
a := arr[1:2]
要素を部分的に取得できる

sliceは参照型ですので、この中の要素の値を変更すると、
そのほかのすべての参照でも値が変更されます。
たとえば上のaSliceとbSliceで、aSliceの中の要素を変更すると、bSliceの対応する値も同時に変更されます。

ここかなり重要だと思います。

概念上では、sliceは構造体です。この構造体には3つの要素が含まれます: 

  • 一つはポインタです。配列中のsliceが示す開始位置を指しています。
  • 長さ、つまりsliceの長さです。
  • 最大の長さ、sliceの開始位置から配列の最後の位置までの長さです。

知らなかった・・・。

map

mapの概念もPythonのディクショナリです。

PHPの連想配列(assoc array)でもOK?

Key,Valueで保存するデータ構造ですね。
宣言

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// intがkeyで stringがvalue
var m map[int]string

// makeしないとエラーになる
m = make(map[int]string)
m[0] = "lemon"
m[1] = "apple"
m[2] = "melon"

// 初期化しつつ宣言
likes := map[string]int{"baseball":90, "soccer":85}

// mapの値の取得
val , ok = m[0]
if ok {
    fmt.Println(val)
} else {
    fmt.Println("none key")
}

// 削除
delete (m, 0)

make, new操作

makenew は違うよって話

makeはビルトイン型(map、sliceおよびchannel)のメモリの割り当てです。newは各型のメモリを割り当てます。

new を使うのは自前の構造体とかになるってこと?

ゼロ値

“ゼロ値"というのは何も空の値ではありません。これは一種の"変数が埋めらる前"のデフォルト値であり、通常は0です。

初期化する前の値ということかな

おわりに

2章を全部やるつもりでしたが、続きは別記事にします。