最近ちょっとGolangを入門しました。
アウトプットも兼ねて、ひとまず深いところは置いといて、
これが分かれば「AtCoder過去問精選10問などを調べながら取り組める」を目標に作成してみました。
サンプルコードを並べただけだったりしますが、参考になればと思います。
#環境構築
環境構築については、以前書いた記事があるので、そちらを参照してください。
Golang環境構築手順メモ
環境構築が面倒な場合はオンラインで実行できる環境が公式で提供されています。(標準入力はできないっぽい?)
The Go Playground
AtCoderでもコードのテスト実行ができます。(要ログイン)
AtCoder Beginners Selection - コードテスト
Dockerスキーな方にはOfficial Imagesが用意されています。
golang - Docker Hub
※以降はLinuxで環境構築した前提で説明します。
#HelloWorld
プログラミング言語の入門と言えばHelloWorldです。
このハンズオンでもHelloWorldから始めたいと思います。
package main
import (
"fmt"
)
// Golangではmainパッケージのmain関数が最初に実行されます。
func main() {
fmt.Println("Hello World")
}
ビルド・実行
Golangはコンパイル言語です。go build
でコンパイルすることができます。
先ほどのHelloWorldのコードをビルドして実行してみます。
$ ls
hello.go
$ go build hello.go
$ ls
hello hello.go
$ ./hello
Hello World
go run
コマンドを使用すれば、スクリプト言語感覚での実行もできます。
$ go run hello.go
Hello World
#パッケージ
package
でソースファイルが所属するパッケージを必ず宣言します。
package main
参考にした本に記載されていた、Golangでのパッケージの特徴を列挙します。 (良く分からない場合は後回しで大丈夫です。)
・プログラムはmainパッケージのmain関数から開始される。
・Golangのプログラムは1つ以上のパッケージで構成される。
・パッケージは1つ以上のソースファイルで構成される。
・同じパッケージのソースファイルはすべて同じディレクトリに格納する。
・1つのディレクトリに複数のパッケージを配置できない。
・mainパッケージを除き、パッケージ名と格納するディレクトリ名は同じにする(必須ではなく慣習)。
#インポート
別のパッケージに含まれる関数などを使用する場合はimport
を使用します。
// 一つずつ指定
import "fmt"
import "strings"
// ひとまとめに指定
import (
"fmt"
"strings"
)
HelloWorldではfmt
パッケージに含まれるPrintln
を使用しています。
Golangでは様々なパッケージが用意されています。
・Go - Packages
・パッケージ - Go 言語
・Golang.jp - パッケージドキュメント (古め)
#コメント
//
(行コメント)または/* */
(ブロックコメント)でコメントします。
ドキュメントによると、ブロックコメントはパッケージコメントで使用して、普段は行コメントがおすすめらしいです。
Line comments are the norm; block comments appear mostly as package comments,
but are useful within an expression or to disable large swaths of code.
#グローバル変数・ローカル変数
変数は宣言する場所によってグローバル変数とローカル変数に区別されます。
・グローバル変数:ソースのトップレベルで宣言
・ローカル変数:関数内で宣言
package main
import (
"fmt"
)
var global int // グローバル変数
func main() {
var local int // ローカル変数
fmt.Println(global)
fmt.Println(local)
}
$ go run globallocal.go
0
0
ここでは出力が0
になっています。
Golangではゼロ値という値があり、変数の初期化時に値を入れなかった場合はゼロ値が入ります。
このゼロ値については後続の変数・定数、型でも再度触れます。
#変数・定数
変数はvar
、定数はconst
で宣言します。
変数名の先頭文字によって異なるパッケージでインポートして使用できるできないが変わります。
・先頭が小文字:使用できない
・先頭を大文字:使用できる
package main
import (
"fmt"
)
var a int // 初期値の指定なし
var b int = 1 // 初期値の指定あり
var c = 2 // 型を省略
// まとめ宣言
var (
d int
e int = 3
f = 4
)
// 同じ型の場合は「,」区切りで複数指定可能
var a1, a2 int
var b1, b2 int = 5, 6
var c1, c2 = 7, 8
func main() {
aa := 9 // ローカル変数でのみで可能
bb, cc := 10, 11
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
fmt.Println(e)
fmt.Println(f)
fmt.Println(a1, a2)
fmt.Println(b1, b2)
fmt.Println(c1, c2)
fmt.Println(aa)
fmt.Println(bb, cc)
}
$ go run var01.go
0
1
2
0
3
4
0 0
5 6
7 8
9
10 11
初期値の指定なしで変数を宣言した場合、
型に応じてゼロ値という初期値(intの場合は「0」)が設定されます。
定数の場合は値指定が必須です。
package main
import (
"fmt"
)
const a int = 1 // 初期値の指定あり
const b = 2 // 型を省略
// まとめ宣言
const (
c int = 3
d = 4
)
// 同じ型の場合は「,」区切りで複数指定可能
const a1, a2 int = 5, 6
const b1, b2 = 7, 8
func main() {
aa := 9 // ローカル変数でのみで可能
bb, cc := 10, 11
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
fmt.Println(a1, a2)
fmt.Println(b1, b2)
fmt.Println(aa)
fmt.Println(bb, cc)
}
$ go run const01.go
1
2
3
4
5 6
7 8
9
10 11
#演算子
演算子を種類別に列挙します。
必要に応じて参照してください。
算術演算子
+ 和 整数、浮動小数点、複素数、文字列
- 差 整数、浮動小数点、複素数
* 積 整数、浮動小数点、複素数
/ 商 整数、浮動小数点、複素数
% 剰余 整数
& ビット演算 and 整数
| ビット演算 or 整数
^ ビット演算 xor 整数
&^ ビットクリア(and not) 整数
<< 左シフト 整数 << 符号なし整数
>> 右シフト 整数 >> 符号なし整数
##比較演算子
== 等しい
!= 等しくない
< 小なり
<= 小なりイコール
> 大なり
>= 大なりイコール
##論理演算子
& and条件
|| or条件
! 否定
##優先順位
高 * / % << >> & &^
+ - | ^
== != < <= > >=
&&
低 ||
##単項演算子
++ インクリメント(x += 1)
-- デクリメント(x -= 1)
- マイナス
^ ビットNOT
! 論理NOT
今回紹介した以外に、アドレス演算子や受信演算子などがあります。
詳細についてはドキュメントを参照してください。
#型
基本の型を紹介します。
ここでは数値、文字列、論理値を紹介します。
また、型ではありませんがnilも紹介します。
##数値
数値は符号付き、符号なしなどの他にもサイズによって細かく分かれています。
整数のゼロ値は0
、浮動小数値のゼロ値も0
です。
慣れるまでは、整数はint
、浮動小数はfloat64
を使うで良いと思います。
int 32または64ビット(環境依存)
uint uintと同じサイズ
uintptr ポインタの値をそのまま格納するのに充分な大きさの符号なし整数
int8 符号あり 8ビット 整数 (-128 to 127)
int16 符号あり 16ビット 整数 (-32768 to 32767)
int32 符号あり 32ビット 整数 (-2147483648 to 2147483647)
int64 符号あり 64ビット 整数 (-9223372036854775808 to 9223372036854775807)
uint8 符号なし 8ビット 整数 (0 to 255)
uint16 符号なし 16ビット 整数 (0 to 65535)
uint32 符号なし 32ビット 整数 (0 to 4294967295)
uint64 符号なし 64ビット 整数 (0 to 18446744073709551615)
float32 IEEE-754 32ビット 浮動小数値
float64 IEEE-754 64ビット 浮動小数値
complex64 float32の実数部と虚数部を持つ複素数
complex128 float64の実数部と虚数部を持つ複素数
byte uint8の別名(エイリアス)
rune int32の別名(エイリアス)
```
```golang:number01.go
package main
import (
"fmt"
"reflect"
)
func main() {
fmt.Println("number sample")
var a int
var b float64
var c float64 = 1.0
var d float64 = 1.0
// int, float64のゼロ値は0
fmt.Println(a, b)
fmt.Println(c + d) // 同じ型の数値はそのまま足し算できる
fmt.Println(float64(a) + c + d) // 違う型は型変換が必要
fmt.Println(reflect.TypeOf(a)) // reflect.TypeOf()で型を確認できる
fmt.Printf("%v , %T\n", a, a) // Printfで「%T」を使うと型の出力が可能
fmt.Println()
var e int
e = 10
fmt.Println(e)
fmt.Println(e - 11)
var f uint
f = 10
fmt.Println(f)
fmt.Println(f - 11)
}
```
```:実行結果
$ go run number01.go
numberSample
0 0
2
2
int
0 , int
10
-1
10
18446744073709551615
```
##文字列
文字列のゼロ値は`""`(空文字)です。
```golang:文字列
string 文字列型
// 例
str01 := "文字列"
str02 := `改行も
含められる`
```
```golang:string01.go
package main
import (
"fmt"
"reflect"
)
func main() {
fmt.Println("string sample")
var s1 string
s2 := "aaa"
s3 := `bbb
bbbb
bbb`
fmt.Printf("%v , %T\n", s1, s1)
fmt.Printf("%v , %T\n", s2, s2)
fmt.Printf("%v , %T\n", s3, s3)
fmt.Println(s2 + " + " + s3) // 文字列は「+」で文字連結可能
fmt.Println(reflect.TypeOf(s2))
fmt.Println(reflect.TypeOf(s3))
}
```
```:実行結果
$ go run string01.go
stringSample
, string
aaa , string
bbb
bbbb
bbb , string
aaa + bbb
bbbb
bbb
string
string
```
##論理値
論理値のゼロ値は"false"です。
```golang:論理値
bool 論理値 trueまたはfalse
```
```golang:bool01.go
package main
import (
"fmt"
"reflect"
)
func main() {
fmt.Println("bool sample")
var a bool
fmt.Printf("%v , %T\n", a, a)
fmt.Println(reflect.TypeOf(a))
}
```
```:実行結果
$ go run bool01.go
boolSample
false , bool
bool
```
##nil
`nil`は指し示す先が無いことを表す特別な値です。
※nullはありません。
#配列
特定の型の値の集まりです。
配列は固定長のため、一度定義したら長さを変えることができません。
長さを変える必要がある場合は、後続で紹介するスライスを使用します。
配列ではなくスライスを使うことが多いと思います。
定義のみで値を入れなかった場合は、ゼロ値が設定されます。
```golang:array01.go
package main
import (
"fmt"
)
func main() {
// 配列の定義
var a [3]int
// 定義のみだとゼロ値が設定される
fmt.Printf("%v , %T\n", a, a)
// 定義と同時に値を設定
b := [2]string{"aaa", "bbb"}
// インデックス(0始まり)を指定して取得可能
fmt.Println(b[1])
// 初期値を指定すると「...」で長さを省略できる
c := [...]int{1, 2}
// lenで長さの取得可能
fmt.Println(len(c))
// インデックスを指定して値を代入
d := [3]int{1: 1, 2: 10}
fmt.Println(d)
// 二重配列
var e [3][2]int
fmt.Printf("%v , %T\n", e, e)
e[1][0] = 2
e[1][1] = 3
fmt.Println(e)
// 二重配列を定義と同時に値を設定
f := [3][2]int{{0, 1}, {2, 3}, {4, 5}}
fmt.Println(f)
x1 := [2]int{1, 2}
x2 := [2]int{1, 2}
fmt.Println("x1 == x2 : ", x1 == x2)
x2[0] = 3
fmt.Println("x1 == x2 : ", x1 == x2)
hoge([2]int{1, 2})
// hoge([3]int{1, 2, 3}) // これはエラーになる
}
// 配列は長さ含めて区別される。(funcについては後続で説明します。)
func hoge(a [2]int) {
fmt.Println(a[0])
}
```
```:実行結果
$ go run array01.go
[0 0 0] , [3]int
bbb
2
[0 1 10]
[[0 0] [0 0] [0 0]] , [3][2]int
[[0 0] [2 3] [0 0]]
[[0 1] [2 3] [4 5]]
x1 == x2 : true
x1 == x2 : false
1
```
#スライス
可変長の配列です。
内部的には配列を参照しています。
```golang:slice01.go
package main
import (
"fmt"
)
func main() {
// スライスの定義
var a []int
// 配列と違いゼロ値は設定されない
fmt.Printf("%v, %T\n", a, a)
// 定義と同時に値を設定
b := []string{"hoge", "fuga"}
// インデックス(0始まり)を指定して取得可能
fmt.Println(b[0])
// 定義と同時に値を設定
c := []int{1, 2, 3, 4, 5}
// lenで長さの取得可能
fmt.Println(len(c))
// makeでスライスの長さを指定して定義可能
d := make([]int, 5)
// 長さを指定するとゼロ値が設定される
fmt.Println(d)
// appendでaに値を追加可能
fmt.Println(append(d, 6))
// dは変更されない
fmt.Println(d)
// appendで複数の値を追加
e := append(d, 6, 7, 8)
fmt.Println(e)
// インデックスを指定して値を代入
e[2] = 3
fmt.Println(e)
// 二重スライス
var f [][]int
g := []int{1, 2}
h := []int{3, 4, 5}
// 二重スライスに代入。長さ関係なく代入できる
i := append(f, g, h)
fmt.Println(append(i, []int{6, 7, 8, 9}))
// 比較はnilとしかできない
x1 := []int{1, 2}
var x2 []int
fmt.Println("x1 == nil : ", x1 == nil)
fmt.Println("x2 == nil : ", x2 == nil)
// x3 := []int{1, 2}
// fmt.Println("x1 == x3 : ", x1 == x3) // エラーになる。
hoge([]int{1, 2})
hoge([]int{3, 4, 5}) // 配列と違いエラーにならない
}
// スライスでは長さは区別されない。(funcについては後続で説明します。)
func hoge(a []int) {
fmt.Println(a[0])
}
```
```:実行結果
$ go run slice01.go
[], []int
hoge
5
[0 0 0 0 0]
[0 0 0 0 0 6]
[0 0 0 0 0]
[0 0 0 0 0 6 7 8]
[0 0 3 0 0 6 7 8]
[[1 2] [3 4 5] [6 7 8 9]]
x1 == nil : false
x2 == nil : true
1
3
```
#マップ
他の言語では辞書や連想配列などと呼ばれるものです。
```golang:map01.go
package main
import (
"fmt"
"reflect"
)
func main() {
// マップの定義
var a = make(map[string]int)
fmt.Printf("a : %v , %T\n", a, a)
fmt.Println(reflect.TypeOf(a))
// マップの定義
b := map[string]int{"a": 1, "b": 2}
fmt.Printf("b : %v , %T\n", b, b)
fmt.Println(reflect.TypeOf(b))
// 要素への代入
b["b"] = 10
// 要素の参照
fmt.Println(b["a"], b["b"])
// 存在しない要素を指定してもエラーにならない。
fmt.Println(b["c"])
}
```
```:実行結果
$ go run map01.go
a : map[] , map[string]int
map[string]int
b : map[a:1 b:2] , map[string]int
map[string]int
1 10
0
```
#関数定義
関数は`func`で定義します。
関数名の先頭文字によって異なるパッケージでインポートして使用できるできないが変わります。
・先頭が小文字:使用できない
・先頭を大文字:使用できる
返却値がある場合は`return`で返却します。
Golangでは複数返却することもできます。
```golang:関数定義まとめ
func hoge() {}
func Hoge() {}
func hoge(num int) {}
func hoge(num1 int, num2 int) {}
func fuga(num1, num2 int) {}
func hoge(num int) string {}
func hoge(num1, num2 int) (string, string) {}
func hoge(s ...string) {}
```
以下、それぞれのサンプルコードです。
```golang:func01.go
package main
import (
"fmt"
)
// 定義例
func hoge() {
// 処理
fmt.Println("hoge")
}
// 定義例
func Hoge() {
// 処理
fmt.Println("Hoge")
}
// 呼び出し例
func main() {
hoge()
Hoge()
}
```
```:実行結果
$ go run func01.go
hoge
Hoge
```
引数を受け取る場合は`(変数名 型)`で指定します。
```golang:func02.go
package main
import (
"fmt"
)
// 定義例
func hoge(num int) {
fmt.Println(num)
}
// 呼び出し例
func main() {
hoge(5)
}
```
```:実行結果
$ go run func02.go
5
```
引数を複数受け取る場合は`,`区切りで指定します。
引数は型が同じ場合は型宣言を1つにまとめられます。
```golang:func03.go
package main
import (
"fmt"
)
// 定義例
func hoge(num1 int, num2 int) {
fmt.Println(num1 + num2)
}
// 定義例
func fuga(num1, num2 int) {
fmt.Println(num1 - num2)
}
// 呼び出し例
func main() {
hoge(2, 3)
fuga(10, 5)
}
```
```:実行結果
$ go run func03.go
5
5
```
返却値がある場合は、返却値の型を指定します。
```golang:func04.go
package main
import (
"fmt"
"strings"
)
// 定義例
func hoge(num int) string {
str := strings.Repeat("Go", num)
return str
}
// 呼び出し例
func main() {
str := hoge(5)
fmt.Println(str)
}
```
```:実行結果
$ go run func04.go
GoGoGoGoGo
```
Golangでは複数返却値を返すことができます。複数返す場合は`()`で囲って指定します。
```golang:func05.go
package main
import (
"fmt"
"strings"
)
// 定義例
func hoge(num1, num2 int) (string, string) {
str1 := strings.Repeat("Go", num1)
str2 := strings.Repeat("Go", num2)
return str1, str2
}
// 呼び出し例
func main() {
str1, str2 := hoge(5, 10)
fmt.Println(str1)
fmt.Println(str2)
}
```
```:実行結果
$ go run func05.go
GoGoGoGoGo
GoGoGoGoGoGoGoGoGoGo
```
関数の返却値で使用しない値がある場合は`_`で使用しないことを明示します。
```golang:func06.go
package main
import (
"fmt"
"strings"
)
// 定義例
func hoge(num1, num2 int) (string, string) {
str1 := strings.Repeat("Go", num1)
str2 := strings.Repeat("Go", num2)
return str1, str2
}
// 呼び出し例
func main() {
str1, _ := hoge(5, 10)
fmt.Println(str1)
}
```
```:実行結果
$ go run func06.go
GoGoGoGoGo
```
使われていない変数がある場合は、以下の様にメッセージが出力されビルドまたは実行ができません。
```:変数は使われていないとエラーになる
$ go build func06.go
# command-line-arguments
./func06.go:17:8: str2 declared and not used
$ go run func06.go
# command-line-arguments
./func06.go:17:8: str2 declared and not used
```
引数の型を`...型`とすると引数を可変にすることができます。
```golang:func07.go
package main
import (
"fmt"
)
func main() {
hoge("aa")
hoge("bb", "cc", "dd")
}
func hoge(s ...string) {
// forについては後続で説明。
for i, v := range s {
fmt.Println(i, v)
}
}
```
```:実行結果
$ go run func07.go
0 aa
0 bb
1 cc
2 dd
```
#条件分岐
条件分岐は`if`と`switch`があります。
```golang:if01.go
package main
import (
"fmt"
)
func main() {
i := 1
if i < 5 {
fmt.Println("5以下")
}
}
```
```:実行結果
$ go run if01.go
5以下
```
`else`で条件にマッチしなかった場合の分岐ができます。
```golang:if02.go
package main
import (
"fmt"
)
func main() {
i := 10
if i >= 5 {
fmt.Println("5以上")
} else {
fmt.Println("5未満")
}
}
```
```:実行結果
$ go run if02.go
5以上
```
`else if`で、別の条件を指定できます。
```golang:if03.go
package main
import (
"fmt"
)
func main() {
i := 10
if i == 5 {
fmt.Println("5!")
} else if i == 10 {
fmt.Println("10!")
} else {
fmt.Println("5と10以外")
}
}
```
```:実行結果
$ go run if03.go
10!
```
`switch`はマッチしたcaseの処理のみ実行されます。
マッチした次のcaseの処理も実行したい場合は`fallthrough`を処理の最後に付けます。
```golang:switch01.go
package main
import (
"fmt"
)
func main() {
num := 1
switch num {
case 1:
fmt.Println("1!")
// fallthrough // これを付けると次のcase内容も実行される
case 2:
fmt.Println("2!")
case 3:
fmt.Println("3!")
default:
fmt.Printf("default!")
}
}
```
```:実行結果
$ go run switch01.go
1!
```
```:実行結果(fallthroughのコメントを外した場合)
$ go run switch01.go
1!
2!
```
#繰り返し(ループ)
Golangでは繰り返し(ループ)処理の構文は`for`のみです。
他の言語と違い`while`がありません。
```golang:for01
package main
import (
"fmt"
)
func main() {
// 基本的なfor文
for i := 0; i < 3; i++ {
fmt.Println(i)
}
fmt.Println()
// スライスの要素数だけループ
for i, v := range []int{5, 6, 7} {
fmt.Printf("i =%d, v =%d \n", i, v)
}
fmt.Println()
// while文と同じ
w := 0
for w < 2 {
fmt.Println(w)
w++
}
fmt.Println()
// 無限ループ
a := 0
for {
a++
if a > 5 {
break // ループを抜ける
} else if (a % 2) == 0 {
continue // ループを継続(後続処理を飛ばす)
}
fmt.Println(a)
}
}
```
```:実行結果
$ go run for01.go
0
1
2
i =0, v =5
i =1, v =6
i =2, v =7
0
1
1
3
5
```
#その他おまけTips
以降の内容はおまけです。知っていると役立ちそうな内容をピックアップしてみました。
※主に競技プログラミングで。
##標準入力
[fmt パッケージのドキュメント](https://golang.org/pkg/fmt/)
競技プログラミングをやる場合に欠かせない標準入力の仕方をいくつか紹介します。
```golang:inout.go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// フォーマットを指定して取得
// %dで整数(基数10)、%sで文字列を取得できる
var a int
var b string
fmt.Scanf("%d:%s", &a, &b) // 「数値:数値」を入力で取得
fmt.Println(a, b)
// 空白区切りで取得(例:「数値 数値」)
var d, e int
fmt.Scan(&d, &e)
fmt.Println(d, e)
// 一行取得
stdin := bufio.NewScanner(os.Stdin)
stdin.Scan()
s := stdin.Text()
fmt.Println(s)
}
```
```:実行結果
$ go run inout.go
1:2 ←入力
1 2
3 4 ←入力
3 4
aiu eo kaki kukeko sashi ←入力
aiu eo kaki kukeko sashi
```
##配列、スライス、文字列の切り出し(スライス)
配列、スライス、文字列の一部分を取り出すことができるので紹介します。
スライスをスライスなんてちょっとややこしいです。
```golang:cutout01.go
package main
import (
"fmt"
)
func main() {
// スライスをスライス
s := []int{1, 2, 3, 4, 5}
fmt.Println(s)
fmt.Printf("ex.1 type: %T, value: %v\n", s[0:], s[0:]) // 0番目からすべて取得
fmt.Printf("ex.2 type: %T, value: %v\n", s[:3], s[:3]) // 0番目から3番目まで取得
fmt.Printf("ex.3 type: %T, value: %v\n\n", s[1:3], s[1:3]) // 1番目から3番目まで取得
// 配列でもスライス
a := [5]int{1, 2, 3, 4, 5}
fmt.Printf("ex.4 type: %T, value: %v\n\n", a[1:3], a[1:3])
// 文字列もスライス
str := "aiueo"
fmt.Printf("ex.5 type: %T, value: %v\n", str[3:], str[3:])
// マップはできない
// m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
// fmt.Println(m[3:])
// 以下のようにエラーが出る
// # command-line-arguments
// ./sliceslice.go:23:15: cannot slice m (type map[string]int)
}
```
```:実行結果
$ go run cutout01.go
[1 2 3 4 5]
ex.1 type: []int, value: [1 2 3 4 5]
ex.2 type: []int, value: [1 2 3]
ex.3 type: []int, value: [2 3]
ex.4 type: []int, value: [2 3]
ex.5 type: string, value: eo
```
##数字から数値へ変換
数字と数値は加算など計算することできません。同じ型に変換する必要があります。
```golang:strconv01.go
package main
import (
"fmt"
"reflect"
"strconv"
)
func main() {
// 数字を数値に変換
a1 := "1"
a2, a3 := strconv.Atoi(a1)
fmt.Println(a1, reflect.TypeOf(a1))
fmt.Println(a2, reflect.TypeOf(a2), a3)
// 2つめの変数(a5)にはエラー内容が入る
// 成功した場合はnilが入る
a4, a5 := strconv.Atoi("a")
fmt.Println(a4, a5)
// 数値を数字に変換
b := 1
b1 := strconv.Itoa(b) // 文字列に変換の場合は返却は1つ
fmt.Println(b, reflect.TypeOf(b), b1, reflect.TypeOf(b1))
// fmt.Println(a1 + b) // 型が違うのでエラーになる
}
```
```:実行結果
$ go run strconv01.go
1 string
1 int <nil>
0 strconv.Atoi: parsing "a": invalid syntax
1 int 1 string
```
##文字列置換
```golang:replace01.go
package main
import (
"fmt"
"strings"
)
func main() {
// bbbをXXXに置換
s := "aaabbbcccaaabbbccc"
s1 := strings.Replace(s, "bbb", "XXX", -1)
fmt.Printf("%v\n%v\n", s, s1)
}
```
```:実行結果
$ go run replace01.go
aaabbbcccaaabbbccc
aaaXXXcccaaaXXXccc
```
##文字列カウント
```golang:count01.go
package main
import (
"fmt"
"strings"
)
func main() {
// 文字列中の「a」の個数をカウント
s := "aaabbbcccaaabbbccc"
s1 := strings.Count(s, "a")
fmt.Println(s1)
}
```
```:実行結果
$ go run count01.go
6
```
##文字列の分割(スプリット)
```golang:split01.go
package main
import (
"fmt"
"strings"
)
func main() {
// 「,」区切りで分割
s := "aaa,bbb,ccc,aaa,bbb,ccc"
s1 := strings.Split(s, ",")
fmt.Printf("%v\n%v\n", s, s1)
}
```
```:実行結果
$ go run split01.go
aaa,bbb,ccc,aaa,bbb,ccc
[aaa bbb ccc aaa bbb ccc]
```
##ソート
```golang:sort01.go
package main
import (
"fmt"
"sort"
)
func main() {
// intスライスを昇順にソート
a1 := []int{1, 5, 3, 2, 4}
sort.Ints(a1)
fmt.Println(a1)
// intのスライスを降順にソート(昇順にソートして反転)
a2 := []int{1, 5, 3, 2, 4}
sort.Sort(sort.Reverse(sort.IntSlice(a2)))
fmt.Println(a2)
//stringスライスを昇順にソート
s1 := []string{"cc", "bb", "aa", "CC", "AA", "BB"}
sort.Strings(s1)
fmt.Println(s1)
// stringスライスを降順にソート(昇順にソートして反転)
s2 := []string{"cc", "bb", "aa", "CC", "AA", "BB"}
sort.Sort(sort.Reverse(sort.StringSlice(s2)))
fmt.Println(s2)
// 配列のソート方法が見つからなかったので(sortに見当たらなかった)
// スライスに変換してソートすれば良さそうです。(自信ない)
a3 := [5]int{5, 8, 6, 9, 10}
a4 := a3[:]
sort.Ints(a4)
fmt.Println(a4)
}
```
```:実行結果
$ go run sort01.go
[1 2 3 4 5]
[5 4 3 2 1]
[AA BB CC aa bb cc]
[cc bb aa CC BB AA]
[5 6 8 9 10]
```
##スライスのコピーには注意する
スライスは配列と違い参照渡しです。別の変数にコピーしたり、関数に渡す場合に注意が必要です。
また、capという長さとは別に容量があり、appendした際にアドレスが変わる場合があるため注意が必要です。
```golang:slice02.go
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3}
b := a
// メモリ上では同じ値(アドレス)を参照している
// &で値のメモリ上でのアドレスが見れる
fmt.Printf("a:%v, %v\n", a[0], &a[0])
fmt.Printf("b:%v, %v\n", b[0], &b[0])
// コピー元のaの値を変更するとbも変更される
a[0] = 10
fmt.Printf("a:%v, %v\n", a[0], &a[0])
fmt.Printf("b:%v, %v\n", b[0], &b[0])
fmt.Println()
// スライスした場合も同じアドレスになる
c := a[:]
fmt.Printf("c:%v, %v\n", c[0], &c[0])
a[0] = 20
fmt.Printf("c:%v, %v\n", c[0], &c[0])
fmt.Println()
// 一部分をスライスしても同じ
d := a[:2]
fmt.Printf("d:%v, %v\n", d[0], &d[0])
a[0] = 30
fmt.Printf("d:%v, %v\n", d[0], &d[0])
fmt.Println()
// スライスを作成する際に第3引数でスライスの容量を指定できる。
// 容量を超えるまでは同じアドレスを使用し、超えると新しく作り直される
g := make([]int, 3, 3)
fmt.Printf("g:%v, %v, %v\n", g, g[0], &g[0])
g[0] = 1
fmt.Printf("g:%v, %v, %v\n", g, g[0], &g[0])
g1 := append(g, 4)
fmt.Printf("g1:%v, %v, %v\n", g1, g1[0], &g1[0])
fmt.Println()
// 新しいアドレスが割り当てられたことで変更してもコピー元には影響しない
g1[1] = 10
fmt.Printf("g:%v, %v, %v\n", g, g[1], &g[0])
fmt.Printf("g1:%v, %v, %v\n", g1, g1[1], &g1[0])
fmt.Println()
// capを多めにとった場合
h := make([]int, 3, 5)
fmt.Printf("h:%v, %v, %v\n", h, h[0], &h[0])
h[0] = 1
fmt.Printf("h:%v, %v, %v\n", h, h[0], &h[0])
h = append(h, 4)
fmt.Printf("h:%v, %v, %v\n", h, h[0], &h[0])
fmt.Println()
// 関数に渡す際もメモリ上のアドレスを渡している
z := cp(a)
fmt.Println("z:", &z[0])
fmt.Println("z:", z[0])
}
func cp(s []int) []int {
fmt.Println("s:", &s[0])
return s
}
```
```:実行結果
$ go run slice02.go
a:1, 0xc00006c140
b:1, 0xc00006c140
a:10, 0xc00006c140
b:10, 0xc00006c140
c:10, 0xc00006c140
c:20, 0xc00006c140
d:20, 0xc00006c140
d:30, 0xc00006c140
g:[0 0 0], 0, 0xc00006c180
g:[1 0 0], 1, 0xc00006c180
g1:[1 0 0 4], 1, 0xc000098030
g:[1 0 0], 0, 0xc00006c180
g1:[1 10 0 4], 10, 0xc000098030
h:[0 0 0], 0, 0xc000098060
h:[1 0 0], 1, 0xc000098060
h:[1 0 0 4], 1, 0xc000098060
s: 0xc00006c140
z: 0xc00006c140
z: 30
```
##パッケージ作成する場合の注意
パッケージを作成しインポートして使用する場合は`go env`コマンドで表示される、
`GOROOT`または`GOPATH`の配下に配置していないとエラーになります。
```:こんなファイル構成の時
ptest
|-hoge.go
|-pp
|-sample.go
```
```golang:hoge.go
package main
import (
"fmt"
"ptest/pp"
)
func main() {
fmt.Println(pp.A)
pp.Ptest()
fmt.Println("go")
}
```
```golang:pp/sample.go
package pp
import "fmt"
const A = 10
func Ptest() {
fmt.Println("Ptest", A)
}
```
```:GOROOTまたはGOPATHに配置していないとエラー例
$ go run hoge.go
hoge.go:5:2: cannot find package "ptest/pp" in any of:
c/go/src/ptest/pp (from $GOROOT)
c/home/vagrant/go/src/ptest/pp (from $GOPATH)
```
ちなみに、シンボリックリンクの作成でもエラーを回避できます。
1つのGitリポジトリで雑多なコードを管理している場合に便利です。Golang的に良い悪いはわかりませんが・・。
```:シンボリックリンク作成例(Linux)。work_spaceリポジトリのgolangディレクトリ配下にあるptestを実行できるようにする
$ cd $(go env GOPATH)/src
$ ln -s ~/work_space/golang/ptest/
$ cd ~/work_space/golang/ptest/
$ go run hoge.go
10
Ptest 10
vim-go
```
追記:Go Modulesを使うのが良さそう。
https://github.com/golang/go/wiki/Modules
##無名関数
Golangには無名関数も存在します。
サンプルコードなどで出てくる場合があるので簡単にですが紹介します。
```golang:nameless01.go
package main
import (
"fmt"
"strings"
"time"
)
func main() {
// 即時実行
func() {
fmt.Print("nameless ")
}()
// 変数に入れて実行できる
nameless1 := func() {
fmt.Println("function")
}
nameless1()
// 引数、返却値も通常の関数と同様に使用できる
nameless2 := func(a, b int) (string, string) {
aStr := strings.Repeat("GO", a)
bStr := strings.Repeat("go", b)
return aStr, bStr
}
aa, bb := nameless2(3, 5)
fmt.Println(aa, bb)
// 並行処理のサンプルでよく見かける気がする
a := 5
go func(a int) {
for i := 0; i < 5; i++ {
fmt.Println("hoge", i)
time.Sleep(1 * time.Second)
}
}(a)
time.Sleep(time.Duration(a) * time.Second)
}
```
```:実行結果
$ go run nameless01.go
nameless function
GOGOGO gogogogogo
hoge 0
hoge 1
hoge 2
hoge 3
hoge 4
```
#次の一歩
「[A Tour of Go](https://tour.golang.org/welcome/1)」というチュートリアルがGolangでは用意されています。
古めですが「[はじめてのGo言語](http://cuto.unirita.co.jp/gostudy/)」もわかりやすくお勧めです。
**Golang完全に理解した**という方はソースコードを読んでみるのも良いかもしれません。
Golangをインストールした場所の`src`に保存されています。
自分の環境では以下に保存されていました。
・Windowsの場合`C:\Go\src`
・Linuxの場合`/usr/local/go/src`
#おわり
触れられなかった内容や説明が足りていない部分も多々ありますが、今回は以上です。
次の一歩で紹介したチュートリアルやAtCoder、他のオンラインジャッジなどをやってコーディング力を伸ばしていきましょう。
# 参考
[The Go programming Language](https://golang.org/)
[golang.jp](http://golang.jp/)
[Go言語.com](https://xn--go-hh0g6u.com/)
[The Go Blog 日本語訳](https://www.ymotongpoo.com/works/goblog-ja/)
[Go CodeReviewComments 日本語翻訳 #golang](https://qiita.com/knsh14/items/8b73b31822c109d4c497)
[プログラミング経験者がGo言語を本格的に勉強する前に読むための本](https://www.amazon.co.jp/dp/B06XJ86BFZ)
[逆引きGolang](https://ashitani.jp/golangtips/index.html)
[はじめてのGo言語](http://cuto.unirita.co.jp/gostudy/)
[Go言語で幸せになれる10のテクニック](https://qiita.com/ksato9700/items/6228d4eb6d5b282f82f6)
[Go 言語(Golang) はまりどころと解決策](https://www.yunabe.jp/docs/golang_pitfall.html)
[Go初心者が気を付けること](http://golang.rdy.jp/2019/12/17/beware-newbies/)
[Effective Go](http://go.shibu.jp/effective_go.html)