Golang入門中です。
以前、以下の記事を書きました。
Golang入門:AtCoderなどの競技プログラミング問題を解き始めるための基本構文+その他おまけTips
記事に載せようかと思い書いてみたけれど、
趣旨から外れるため除外したサンプルコードをせっかくなのでまとめて公開したいと思います。
入門者によるざっくり試してみたコードの羅列ですが、参考になれば幸いです。
#型宣言
基本の型から独自の型を宣言できます。
package main
import (
"fmt"
)
// intから新しい型を宣言
type aInt int
type bInt int
func main() {
var a aInt = 5
var b bInt = 55
hoge(a, b)
// 数値はaInt型,bInt型に代入できるので実行可能
hoge(5, 55)
// intだとエラーになる
// エラー例:
// # command-line-arguments
// .\newType01.go:19:6: cannot use i (type int) as type aInt in argument to hoge
// .\newType01.go:19:6: cannot use j (type int) as type bInt in argument to hoge
// var i int = 5
// var j int = 55
// hoge(i, j)
// 別の型として扱われるので、引数の順番を逆にするとエラーになる
// エラー例:
// # command-line-arguments
// .\newType01.go:16:6: cannot use b (type bInt) as type aInt in argument to hoge
// .\newType01.go:16:6: cannot use a (type aInt) as type bInt in argument to hoge
// hoge(b, a)
// 型に関連付けられたメソッドを実行
a.show()
}
func hoge(x aInt, y bInt) {
fmt.Printf("x :%T, %v\n", x, x)
fmt.Printf("y :%T, %v\n", y, y)
}
// 新しい型に関数を関連付けられる
// 型の変数をレシーバ、関数をメソッドという
func (a aInt) show() {
fmt.Println(a)
}
$ go run newType01.go
x :main.aInt, 5
y :main.bInt, 55
x :main.aInt, 5
y :main.bInt, 55
5
#構造体
型を組み合わせて構造体が作れます。
package main
import (
"fmt"
)
type vector struct {
x, y int
}
func main() {
var v1 vector
v1.x = 5
v1.y = 55
fmt.Printf("v1: %T, %v\n", v1, v1)
v2 := vector{1, 2}
fmt.Printf("v2: %T, %v\n", v2, v2)
fmt.Printf("subXY(): %v\n", v1.subXY())
fmt.Printf("addXY(): %v\n", v2.addXY())
}
// 型宣言と同様にメソッドを定義できる
func (v vector) addXY() int {
return v.x + v.y
}
func (v vector) subXY() int {
return v.y - v.x
}
$ go run typeStruct01.go
v1: main.vector, {5 55}
v2: main.vector, {1 2}
subXY(): 50
addXY(): 3
#遅延実行
defer
を使うと関数内で、最後に実行される処理を宣言できます。
package main
import "fmt"
func main() {
// 関数の最後に実行される
// 複数宣言した場合、後に宣言したdeferが先に実行される
defer fmt.Println("a defer")
defer fmt.Println("b defer")
defer hoge()
defer fmt.Println("c defer")
fmt.Println("main")
}
func hoge() {
defer fmt.Println("hoge defer")
fmt.Println("hoge")
}
$ go run defer01.go
main
c defer
hoge
hoge defer
b defer
a defer
#例外処理
Golangには例外処理の構文(try ~ catchなど)がありません。
替わりに関数から返されるerrorインターフェースをハンドリングします。
package main
import (
"fmt"
"strconv"
)
func main() {
e()
}
func e() {
n, err := strconv.Atoi("aaa")
// 関数が返す値が判定して例外処理する
if err != nil {
fmt.Printf("type: %T\nerr: %v\n", err, err)
return
}
fmt.Println(n)
}
$ go run error01.go
type: *strconv.NumError
err: strconv.Atoi: parsing "aaa": invalid syntax
typeはNumErrorですが、NumErrorにはerrorが含まれているっぽいです。
// 抜粋
type NumError struct {
Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
Num string // the input
Err error // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.)
}
#ポインタ
Golangではポインタがあります。
package main
import (
"fmt"
)
func main() {
// &でアドレスを取得
var n int = 10
fmt.Println("nのアドレス: ", &n)
fmt.Println("nの値: ", n)
fmt.Println()
//int型のポインタ変数宣言(アドレスを格納できる)
var pointer *int
// nのアドレスを代入
pointer = &n
fmt.Println("pointerの値: ", pointer)
// アドレスから値取得
fmt.Println("pointerの中身: ", *pointer)
fmt.Println()
a, b := 1, 1
addOneByValue(a) // 値渡しで加算
addOneByPointer(&b) // 参照渡しで加算
fmt.Println("a: ", a) // 値渡しなので元の変数に影響なし
fmt.Println("b: ", b) // 参照渡しなので変更される
fmt.Println()
s := []int{1, 2, 3}
fmt.Println("sのアドレス: ", &s) // スライスは&でアドレス確認できない
fmt.Printf("sのアドレス: %p\n", s)
slicePointer(s) //スライスは参照渡し
}
// 値渡しの関数
func addOneByValue(a int) {
a = a + 1
}
// ポインタ渡し(参照渡し)の関数
func addOneByPointer(a *int) {
*a = *a + 1
}
// スライスを引数にした場合の渡し方の確認
func slicePointer(s []int) {
fmt.Printf("sのアドレス: %p\n", s)
}
$ go run pointer01.go
nのアドレス: 0xc0000140a0
nの値: 10
pointerの値: 0xc0000140a0
pointerの中身: 10
a: 1
b: 2
sのアドレス: &[1 2 3]
sのアドレス: 0xc000016180
sのアドレス: 0xc000016180
#インターフェース
インターフェースを使用することで、ポリモーフィズムを実装することができます。
package main
import (
"fmt"
)
type person interface {
// say()を実装するとインターフェースを実装したことになる
say()
}
func personSay(p person) {
p.say()
}
type p1 struct{}
func (a p1) say() {
fmt.Println("aaaaa")
}
type p2 struct{}
func (b p2) say() {
fmt.Println("bbbbb")
}
type p3 struct{}
func (c p3) nosay() {
fmt.Println(". . .")
}
// インターフェースを使うと、どの型も引数に取るれる、返せる関数が作れる
func personShout(someone interface{}) interface{} {
_, ok := someone.(person)
if !ok {
fmt.Print("あああああ")
return "aaaaa"
} else {
fmt.Print("ゴゴゴゴゴゴ")
return 55555555
}
}
func main() {
a := new(p1)
b := new(p2)
// interfaceに関連づいたメソッド経由で実行
personSay(a)
personSay(b)
c := new(p3)
// personSay(c) // sayを実装していないのでエラーになる
// エラー例
// # command-line-arguments
// .\interface01.go:41:11: cannot use c (type *p3) as type person in argument to personSay:
// *p3 does not implement person (missing say method)
c.nosay() // メソッドとしては実行できる
//
fmt.Println(personShout(a))
fmt.Println(personShout(c))
}
$ go run interface01.go
aaaaa
bbbbb
. . .
ゴゴゴゴゴゴ55555555
あああああaaaaa
#並行処理
Golangと言えばで必ず上がるであろうGolangの強みです。
比較的簡単に並行処理の記述が可能になっています。
(そもそも並行処理が難しいですが。。)
##goroutine
go <関数>
するとgoroutineと呼ばれる軽量なスレッドで<関数>が起動します。
package main
import (
"fmt"
"time"
)
func main() {
// 呼び出し側の処理時間内に終わるため最後まで実行される
go subfunc01()
// 10~20まで出力する関数だが、呼び出し側の処理が終了すると終了する
go subfunc02()
for i := 100; i <= 103; i++ {
fmt.Println(i)
time.Sleep(1 * time.Second)
}
fmt.Println("end main")
}
func subfunc01() {
for i := 0; i < 3; i++ {
fmt.Println(i)
time.Sleep(1 * time.Second)
}
fmt.Println("end subfunc01")
}
func subfunc02() {
for i := 10; i < 20; i++ {
fmt.Println(i)
time.Sleep(1 * time.Second)
}
fmt.Println("end subfunc02")
}
$ go run goroutine01.go
100
0
10
1
11
101
12
102
2
103
end subfunc01
13
14
end main
##Channel
golangではChannelを使用して、各goroutine間でメッセージの送受信ができます。
サンプルを書いてる最中に、「これ範囲外だな」と思い書ききらなかったのでサンプルがありません。。(´・ω・`)
#外部パッケージの利用
他の言語同様にGolangでも公開されているパッケージの利用ができます。
外部パッケージを使用する場合はgo get <パッケージ>
で取得します。
ここではmysqlのドライバをgetしてDBにアクセスしてみます。
使用する外部パッケージ(mysql用のドライバ):https://github.com/go-sql-driver/mysql
※以降はIPアドレス:192.168.10.10が割り当てられた環境で実施しています。
$ go get -u github.com/go-sql-driver/mysql
# GOPATHは「go env GOPATH」で確認
<GOPATH>/src/dbsample
└ db.go
create table test_db.goods
(
goods_id bigint not null auto_increment,
name varchar( 255 ) not null,
category varchar( 255 ),
memo varchar( 255 ),
index (goods_id),
primary key (goods_id)
);
insert into test_db.goods (name, category) values ("PC0001", "PC");
insert into test_db.goods (name, category) values ("PC0002", "PC");
insert into test_db.goods (name, category) values ("MB0001", "携帯電話");
insert into test_db.goods (name, category) values ("MB0002", "携帯電話");
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type Goods struct {
goodsId int `db:goods_id`
name string `db:name`
category string `db:category`
memo string `db:memo`
}
func main() {
whereCategory := "PC"
goodsRows := selectQuery(whereCategory)
// *sql.Rows型からGoodsのスライスに詰め替えている。
var goodsSlice []Goods
for goodsRows.Next() {
var goods Goods
goodsRows.Scan(&goods.goodsId, &goods.name, &goods.category, &goods.memo)
goodsSlice = append(goodsSlice, goods)
}
fmt.Println(goodsSlice)
}
func selectQuery(whereCategory string) *sql.Rows {
// DBへの操作は標準パッケージのdatabase/sqlを使用。
// DB接続(ホスト:192.168.10.10、DB:test_dbの場合)
db, err := sql.Open("mysql", "master:master@tcp(192.168.10.10:3306)/test_db?charset=utf8mb4")
if err != nil {
panic(err.Error())
}
defer db.Close()
// ステートメント作成
query := "SELECT * FROM goods;"
stmt, err := db.Prepare(query)
if err != nil {
panic(err.Error())
}
defer stmt.Close()
// クエリの実行
rows, err := stmt.Query()
if err != nil {
panic(err.Error())
}
return rows
}
$ go run db.go
[{1 PC0001 PC } {2 PC0002 PC }]
#標準パッケージのみでWebサーバー作成
標準パッケージで簡単なWebサーバーを作成してみます。
テンプレート機能もあります。簡単にですがコード例を紹介します。
※以降はIPアドレス:192.168.10.10が割り当てられた環境で実施しています。
sampleweb
└ helloserver.go
└ tmpl01.html
package main
import (
"fmt"
"html/template"
"net/http"
)
func main() {
fmt.Println("Start Sample Server")
server := http.Server{
Addr: ":8080",
}
http.HandleFunc("/", index)
server.ListenAndServe()
}
func index(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles("tmpl01.html")
if err != nil {
fmt.Println("template parse error.", err)
}
helloworldSlice := []string{"hello", "world"}
err = t.Execute(w, helloworldSlice)
if err != nil {
fmt.Println("template execute error.", err)
}
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sample</title>
</head>
<body>
<body>
<ul>
{{ range . }}
<li>{{ . }}</li>
{{ else }}
データなし
{{ end}}
</ul>
</body>
</html>
$ go run helloserver.go
Start Sample Server
(停止する場合はCtrl + C)
別コンソールからcurlを叩いてみます。
$ curl 192.168.10.10:8080
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sample</title>
</head>
<body>
<body>
<ul>
<li>hello</li>
<li>world</li>
</ul>
</body>
</html>
ブラウザでアクセスすると以下のように表示される。
#おわり
Golangはシンプルで構文も少ない方だと思いますが、まだまだ勉強することが多く奥が深いです。
着実にできることを増やしていきたいです。
今回は以上です。
#参考
The Go programming Language
Go言語.com
The Go Blog 日本語訳
プログラミング経験者がGo言語を本格的に勉強する前に読むための本
はじめてのGo言語
Go の並行処理