Go言語でGO(勉強記録1)
Go言語(通称Golang)をやってみることにしました。
PythonやPHP、JavaScriptなどはわりと頻繁に使っていますが、
Goは"聞いたことがある"止まりでした。
ということで、GW休みも生かして勉強することに。
インストール
Go言語のページからMSIをダウンロード→実行。
最近のGitみたいに色々聞かれることはなく、なんとなくでインストールできました。
Hello Worldに至るまで
ファイル作成→VSCode拡張インストール
とりあえず作業用ディレクトリを作って、その中に「helloworld.go」という名前のファイルを作ってみました

これをVisualStudioCodeで開くと、Goの拡張機能をオススメされたので、従うままに入れてみることに。

その後もなんか「アレコレが足りないよ!入れてね!」という通知が出るので、一旦全部ぶち込む。
シンプルなHello World
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
たぶん一番シンプルなのがコレ。
Goはすべての関数が何かしらの「パッケージ」に属している、と。
Goのエントリーポイント(最初に実行される関数)は mainパッケージ内のmain関数 とのこと。
んで、その中でfmtパッケージ内のPrintlnを実行している、と。
Printlnの仲間にPrintとPrintfがいるみたい。
それぞれの機能の差はJavaと同じっぽい?ので割愛。
実行
いずれ、VSCodeのF5でも実行できるようにするけど、一旦はアナログにコマンド入力で実行してみましょう。
go run helloworld.go
で、即時にコンパイルして実行してくれました。

コンパイルして実行形式にしたいなぁ、ってときは go build helloworld.go
。

うまくいけば、同一フォルダに「helloworld.exe」が生まれます。

これをコマンドラインから実行したら、ちゃんとHello Worldと言ってくれる。
コンパイルがラクですね。
(コンパイル言語を使うのが久しい人の感想)
基本文法でお遊び
以下、main()の中を色々いじっていく。
変数
Hello Worldを変数化してみよう。
func main() {
var hw string = "Hello, World!"
fmt.Println(hw)
}
変数宣言のキーワードはvar
。var [変数名] [型] = [初期値]
。
もちろん、イコールから先、初期値はなくてもよい。
初期値から型が自明なら、型宣言は省略できる。
func main() {
var hw = "Hello, World!"
fmt.Println(hw)
}
初期値があれば、varすら省略できる。
ただしその場合、=
を:=
に変える必要がある。イコールの前にコロン。
func main() {
hw := "Hello, World!"
fmt.Println(hw)
}
varはブロックでまとめて一気に宣言することもできる。
func main() {
var (
h string
w string
)
h = "Hello,"
w = "World!"
fmt.Println(h, w)
}
ちなみにfmt.Println()に複数の引数を与えると、半角スペース区切りで出してくれるっぽい。

変数は定義時に予め初期化されているので、何も代入せずにいきなり使うことが可能。
文字列型なら""、数値型なら0、真偽型なら偽が初期値。
定数
キーワードはconst
。
func main() {
const japanese string = "こんにちは、世界。"
const (
h string = "Hello,"
w string = "World!"
)
fmt.Println(japanese)
fmt.Println(h, w)
}
定数なので、後から変更しようとするとコンパイルエラーになります。
ちなみに未使用変数があればコンパイルエラーになりますが、未使用定数はエラーになりません。

キーワードiota
を初期値として使えば、連番で定数を作成できます。
0から始まり、constブロックの終わりまで1ずつインクリメントされていきます。
C++のstd::iotaと同じですかね。
func main() {
const (
sun = iota
mon
tue
wed
)
const (
thu = iota
fri
sat
)
fmt.Println(fri)
}
上の例の場合、sunは0、monは1、tueは2、wedは3。
thuは改めて0、friは1、satは2。
ポインタ
C言語で寒気が出たやつ。
猫でも分かる本を読んでいた小学生時代の自分は、猫未満だった。
変数宣言時に型名の頭に*
をつけると、ポインタ型の扱いになる。
アドレス番地を取得するときは、変数名の頭に&
をつける。
値を取得するときは、変数名の頭に*
をつける。
func main() {
// var1は値
var var1 int = 10
// var2はvar1を指し示したポインタ
var var2 *int = &var1
fmt.Println(var1) // 10
fmt.Println(var2) // 0xc000014088
fmt.Println(*var2) // 10
var1 = 20
// var1を変えると、var2が示す先の値も変わる
fmt.Println(var1, *var2) //20 20
}
関数
main()を見れば分かる通り、func
がキーワードで、{}内に処理を書きます。
package main
func main() {
greeting()
}
func greeting() {
const ret = "hello"
}
↑実行しても何も起きない。
引数のところは値 型名の順。戻り値の型は)と{の間に書く。
package main
import "fmt"
func main() {
fmt.Println(greeting("Hello!"))
}
func greeting(ret string) string {
return ret
}
引数はもちろん、戻り値も複数書ける。複数書く場合は、戻り値エリアもカッコで囲む。
戻り値複数は事故の元やな…
package main
import "fmt"
func main() {
fmt.Println(greeting("Hello!", "World!!"))
}
func greeting(ret1 string, ret2 string) (string, string) {
return ret1, ret2
}
名前付き戻り値
戻り値欄には名前をつけられる。その名も「名前付き戻り値」。
package main
import "fmt"
func main() {
fmt.Println(greeting1()) // こんにちは新世界
fmt.Println(greeting2()) // こんにちは新世界
fmt.Println(greeting3()) // こんにちは
}
// ↓おとなしいほう
func greeting1() string {
message1 := "こんにちは"
message2 := message1 + "新世界"
return message2
}
// ↓名前付き戻り値
func greeting2() (message2 string) {
message1 := "こんにちは"
message2 = message1 + "新世界" // 戻り値欄で先に宣言してるので := ではダメ
return
}
// ↓名前付き戻り値を書いておきながら、それを戻さない
func greeting3() (message2 string) {
message1 := "こんにちは"
message2 = message1 + "新世界"
return message1 // こうしておくと戻り値は「こんにちは新世界」ではなく「こんにちは」になる
}
名前付き戻り値を活用(greeting2()のほう)したほうが、名前がない戻り値よりもメモリ効率がとてもよいらしいので、こっちを使うといいっぽいです。
FORループとIF分岐
for文はよくある形。丸括弧は不要。for 初期化; 継続条件; 周回後処理 { … }
if文もよくある形。こっちも丸括弧は不要(不要なのに入れてるとVSCodeさんに消される)
どっちも{}は必要。処理が1行であれ、{}は端折れないみたい。
func main() {
var ret string
for i := 1; i <= 30; i++ {
ret = ""
if i%3 == 0 {
ret += "Fizz"
}
if i%5 == 0 {
ret += "Buzz"
}
if ret == "" {
ret = fmt.Sprint(i)
// goは暗黙的型変換ができないため、明示的に変換してあげる
// 文字列型にするときはfmt.Sprint()を使用する。
}
fmt.Println(ret)
}
}
↑みんな大好きFizzBuzz
for文は、継続条件以外は端折れます。
また、if文は当然elseもあります。
func main() {
var onakasuita = true
for onakasuita {
if onakasuita {
fmt.Println("カレーをお腹に入れよう")
onakasuita = false
} else {
fmt.Println("満腹だから、もう入らないよ")
}
}
}
↑これを書いているのは、まさにカレーを作ってるときだった。
ちなみにwhile文は無いとのこと。
Switch分岐
switch{…}の中に、case 条件:を書いて、その後ろの行に処理を書く。
func main() {
curry := "甘口"
switch curry {
case "甘口":
fmt.Println("あっ、お子様ですね!仲間!")
case "中辛":
fmt.Println("あっ、立派ですね!")
case "辛口":
fmt.Println("あっ、めっちゃすごいですね!")
case "激辛":
fmt.Println("正気か?")
default:
fmt.Println("カレー、食べないんですか…?")
}
}
Goのswitch-case文は、breakがなくても1ブロックだけ実行して終わるみたい。
また、switchの後に条件を書かずにいると、switch trueと同じ扱いになる。
どういうことかというと、シンプルなif文っぽくなる。
func main() {
watashi := "髪"
switch {
case watashi == "神":
fmt.Println("私が神だ")
case watashi == "髪":
fmt.Println("また髪の話してる")
default:
fmt.Println("一般ピーポー")
}
}
配列
変数宣言時に、型名の前に[]で要素数を書くと配列になります。
宣言と同時に初期化するときは、型名の後に{}で初期値を書きます。
要素数が初期化のときに分かる時は[...]で要素数を省けます。
func main() {
// 宣言と代入を分けるときは、こう。
var arr1 [3]string
arr1[0] = "a"
arr1[1] = "b"
arr1[2] = "c"
// 宣言同時代入時は、こう。
// [...]は個数未定のときに使える。
// 未定とはいえ、配列要素数は後から変えられない。
arr2 := [...]string{"a", "b", "c"}
fmt.Println(arr2[2])
}
なお、Goの配列は「複数の要素を持った値」とのこと。
多くの言語ではポインタだけれど、Goではあくまで値です。
クソデカ配列を関数に引数として渡すと、クソデカ配列がメモリ上にまるまる増えるということ。
あと、これは融通がきかない配列あるあるだけれど、配列要素数は後から変えられない、と。
スライス
配列と違いポインタで持つモノ。こっちのほうが他言語の配列っぽい。
func main() {
// 配列宣言時の[]は空にするとスライスになる
slice1 := []string{"sun", "mon", "tue"}
// fmt.Println(slice1[3]) // ※3番目がないのでランタイムエラーになる
// 別変数を掘っ立てて、appendで要素を追加する
// ※appendの戻り値は新しいスライス
slice11 := append(slice1, "wed")
fmt.Println(slice11[3]) // 答え:wed
// 予め幅を決めて予約しておきたいときはこうなる
slice2 := make([]string, 3)
fmt.Println(slice2[2])
// そもそも何も宣言せずにスライスを作れる
// ただ、このslice3には後から要素を入れることはできないので
// 使いみちとしては、何があるんだろう?
var slice3 []string
fmt.Println(slice3)
}
ただ結局配列の仲間なので、好き勝手に要素を増やすことはできないみたい。
ディスカッション
コメント一覧
まだ、コメントがありません