#基本的な文法の振り返り
####mainパッケージ
プログラムをコンパイルして実行すると,まずmainパッケージの中にあるmain()関数が実行される。
package main
func main() {
}
####インポート
インポートしたパッケージ内へは,パッケージ名にドットを付けてアクセスできる。
package main
import (
"fmt"
"github.com/wdpress/gosample"
"strings"
)
func main() {
fmt.Println("hello world")
}
####オプションの指定
任意の名前(上記ではf)を指定した場合はfで使用可能。
_を付けた場合は使用しないの意。
.を付けた場合は,使用時にパッケージ名が省略可能。
package main
import (
f "fmt"
_ "github.com/wdpress/gosample"
. "strings"
)
func main() {
// fmt.Println()がf.Println()になり
// strings.ToUpper()がToUpper()なっている
f.Println(ToUpper("hello world"))
}
変数
//var message string = "hello world"
//var foo, bar, buz string = "foo", "bar", "buz"
func main() {
// どちらの書き方も同じ意味
// var message string = "hello world"
message := "hello world"
fmt.Println(message)
const Hello string = "hello"
Hello = "bye" // cannot assign to Hello
}
変数の宣言はvarから。JSと似ている。
複数の宣言ができるところも似てる。
varと変数の型は省略可能。
型は推論。ここは似なくてもいいのに・・・
if
func main() {
a, b := 10, 100
if a > b {
fmt.Println("a is larger than b")
} else if a < b {
fmt.Println("a is smaller than b")
} else {
fmt.Println("a equals b")
}
}
if n == 10
fmt.Println(n)
//これはエラー
n == 10 ? fmt.Println(n): fmt.Println(0)
//これもエラー
ifに丸括弧は不要。個人的には欲しい。
波括弧の省略はエラー。
ifの省略もエラー。
for
func main() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
for { //無限ループ
doSomething()
}
}
forも丸括弧は不要。
whileはなし。
switch
func main() {
n := 3
switch n {
case 3:
n = n - 1
fallthrough
case 2:
n = n - 1
fallthrough
case 1:
n = n - 1
fmt.Println(n) // 0
}
}
goのswitchはcaseが終わったら抜けるのでbreakを書く必要はない。
続けたい場合はfallthrough。
関数
func sum(i, j int) { // func sum(i int, j int) と同じ
fmt.Println(i + j)
}
引数の型が同じなら最後にまとめることができる。
func swap(i, j int) (int, int) {
return j, i
}
func main() {
x, y := 3, 4
x, y = swap(x, y)
fmt.Println(x, y) // 4, 3
x = swap(x, y) // コンパイルエラー
x, _ = swap(x, y) // 第二戻り値を無視
fmt.Println(x) // 3
swap(x, y) // コンパイル,実行ともに可能
}
関数は複数の値を返すことができる。
戻り値の数があってないとコンパイルエラーだが
_とすれば明示的に無視することができる。
エラーを返す関数
func main() {
file, err := os.Open("hello.go")
if err != nil {
// エラー処理
// returnなどで処理を別の場所に抜ける
}
// fileを用いた処理
}
エラーでない場合、nil。
自作のエラーはerrorsパッケージを使う。
エラーを最後にする慣習があるため、自作APIもそれに従う。
package main
import (
"errors"
"fmt"
"log"
)
func div(i, j int) (int, error) {
if j == 0 {
// 自作のエラーを返す
return 0, errors.New("divied by zero")
}
return i / j, nil
}
func main() {
n, err := div(10, 0)
if err != nil {
// エラーを出力しプログラムを終了する
log.Fatal(err)
}
fmt.Println(n)
}
名前付き戻り値
func div(i, j int) (result int, err error) {
if j == 0 {
err = errors.New("divied by zero")
return // return 0, errと同じ
}
result = i / j
return // return result, nilと同じ
}
戻り値に名前を付けることができ、ゼロ値で初期化された変数として扱われる。
この場合、returnのあとに返す値を明示する必要がない。
関数リテラル
無名関数が作れる。
すぐに実行する関数を作る場合は以下。
func main() {
func(i, j int) {
fmt.Println(i + j)
}(2, 4)
}
関数を変数に代入可能。
これもJSと同じ。
var sum func(i, j int) = func(i, j int) {
fmt.Println(i + j)
}
func main() {
sum(2, 4)
}
配列
goの配列は固定長。
関数に配列を渡す場合は値渡しとなる。
func fn(arr [4]string) {
arr[0] = "x"
fmt.Println(arr) // [x b c d]
}
func main() {
arr := [4]string{"a", "b", "c", "d"}
fn(arr)
fmt.Println(arr) // [a b c d]
}
スライス
スライス=可変長配列。
for文の中でrangeを用いると,添字と値の両方が取得できる。
s1 := []string{"a", "b"}
s2 := []string{"c", "d"}
s1 = append(s1, s2...) // s1にs2を追加
fmt.Println(s1) // [a b c d]
var arr [4]string
arr[0] = "a"
arr[1] = "b"
arr[2] = "c"
arr[3] = "d"
for i, s := range arr {
// i = 添字, s = 値
fmt.Println(i, s)
}
始点と終点をコロンで挟んで指定すると,その範囲の値を切り出すことができる。
始点,終点を省略した場合,それぞれ先頭,末尾となる。
s := []int{0, 1, 2, 3, 4, 5}
fmt.Println(s[2:4]) // [2 3]
fmt.Println(s[0:len(s)]) // [0 1 2 3 4 5]
fmt.Println(s[:3]) // [0 1 2]
fmt.Println(s[3:]) // [3 4 5]
fmt.Println(s[:]) // [0 1 2 3 4 5]
マップ
//var month map[int]string = map[int]string{}
month := map[int]string{
1: "January",
2: "February",
}
fmt.Println(month) // map[1:January 2:February]
マップからキーと取り出す場合。
キーがあるかを調べる場合は二つ目の引数で判断できる。(boolで返却)
jan := month[1]
fmt.Println(jan) // January
_, ok := month[1]
if ok {
// データがあった場合
}
スライス同様,rangeを使用可能。
defer
ファイル操作などを行う場合,使用後のファイルは必ず閉じる必要がある。
途中で抜けたりして閉じられない可能性が出てくると困る処理はdeferを付ける。
下記ではmain()を抜ける直前に必ず実行されるようになる。
func main() {
file, err := os.Open("./error.go")
if err != nil {
// エラー処理
}
// 関数を抜ける前に必ず実行される
defer file.Close()
// 正常処理
}
パニック
戻り値で表現できないエラーはパニックという方法でエラーが発生する。
func main() {
defer func() {
err := recover()
if err != nil {
// runtime error: index out of range
log.Fatal(err)
}
}()
a := []int{1, 2, 3}
fmt.Println(a[10]) // パニックが発生
}
このパニックで発生したエラーはrecover()という組込み関数で取得し,そこでエラー処理を実施できる。
recover()をdeferの中に書くことで,パニックで発生したエラーの処理を実施してから,関数を抜けることができる。
パニックは組込み関数panic()を用いて自分で発生させることもできる。
a := []int{1, 2, 3}
for i := 0; i < 10; i++ {
if i >= len(a) {
panic(errors.New("index out of range"))
}
fmt.Println(a[i])
}
ただしパニックを用いるのは,エラーを戻り値として表現できない場合や,回復が不可能なシステムエラー,やむを得ず大域脱出が必要な場合など。
基本は戻り値で表現する。