はじめに
前回の記事の続きです。
前回の記事はこちら
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のプログラムが紹介されているので、自分の環境でもコンパイル&実行してみます。
環境は前回の記事で構築したものを使用します。
package main
import "fmt"
func main() {
fmt.Printf("Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい\n")
}
main.go
を上記のものに書き換えて、実行してみます。
以下のような感じになりました。
GoはUTF-8文字列と識別子(なぜならUTF-8の発明者もGoの発明者と同じだからです。)を使用しますので、はじめから多言語サポートを有しています。
初めて知りました。
Goの基礎
変数の定義
var
キーワードで変数宣言できるらしい
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
_
は変数の値を捨ててしまう
_ , b := 3, 4
Scala
とかでも見たことあるような。 新しめの言語にはついている印象です。
未使用の変数があるとコンパイルエラーになる
package main
func main() {
var i int
}
これは一番最初なぜコンパイルエラーになるのかと思いましたが、
コンパイラレベルで未使用変数やimport
を検知してくれると後々コードが綺麗になりやすいと思いました。
コードを改修するときに、IDE
の力を借りて消していくよりかは、誰がどうつくっても未使用の変数が消えている方がありがたいです。
定数
const
キーワードで宣言します。
const constantName = value
//もし必要であれば、定数の型を明示することもできます:
const Pi float32 = 3.1415926
これは他の言語でも割とメジャーなものですね。
下記のように ()
を使用して複数宣言ができたりします。
これは const
に限らず var
でもできますね。
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
とか他の言語も文字列は不変だったと思います。)
Goでは+演算子を使って文字列を連結することができます
+
演算子での連結。
Java
とかだと +
が遅いから StringBuilder
を使う風潮がありましたが、 Go
でもありそうです。 (調べてない)
文字列の修正もこのように書けます
スライス
もし複数行の文字列を宣言したくなったらどうしましょうか?この場合` で宣言することができます:
これは便利かも
エラー型
Goにはビルトインのerror型があります。専らエラー情報の処理に使用されます。Goのpackageの中にはエラー処理を行うerrorsというパッケージがあります。
他言語で使う例外処理の Exception
に似てる気がします。try-catch
しない分見やすさはあります。
Goデータの低レイヤの保存
図を見てみると、int
は 4byte
,int32
も同じで 4byte
使用してますね。[]byte
の要素が 5個あるものは 5byte
になってます。
ここは C言語
でも同じような動作な気がする。
テクニック
グループ化による宣言
import(
"fmt"
"os"
)
const(
i = 100
pi = 3.1415
prefix = "Go_"
)
var(
i int
pi float32
prefix string
)
こんな感じにかけるというものですね。(さっき書いてしまった)
iota列挙型
Goではiotaというキーワードがあります。このキーワードはenumを宣言する際に使用されます。
enum
がこれらしい
const(
blue = iota
yellow
red
)
自分でも書いてみました。
上記のように 0,1,2 で出力されるようです。
当然 iota
キーワードがないとコンパイルエラーになります。
エラー
const(
blue
yellow
red
)
エラー
const(
blue
yellow = iota
red
)
Goプログラムのデザインルール
- 大文字で始まる変数はエクスポート可能です。
つまり、他のパッケージから読むことができる、パブリックな変数だということです。
対して小文字で始まる変数はエクスポートできません。これはプライベート変数です。- 大文字で始まる関数も同じです。
classの中でpublicキーワードによってパブリック関数となっているのと同じです。
対して小文字で始まる関数はprivateキーワードのプライベート関数です。
文字の先頭が大文字/小文字で区別するのは面白いかも。 private
public
はちょっと長いですからね。
array、slice、map
array
固定長の配列
宣言
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
動的な配列
宣言
var fslice []int
slice := []int{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で保存するデータ構造ですね。
宣言
// 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操作
make
と new
は違うよって話
makeはビルトイン型(map、sliceおよびchannel)のメモリの割り当てです。newは各型のメモリを割り当てます。
new
を使うのは自前の構造体とかになるってこと?
ゼロ値
“ゼロ値"というのは何も空の値ではありません。これは一種の"変数が埋めらる前"のデフォルト値であり、通常は0です。
初期化する前の値ということかな
おわりに
2章を全部やるつもりでしたが、続きは別記事にします。