Go言語事始め

昨日、yahoo!大阪さんのMixLeapでGo言語のハンズオンを体験したので、復習を兼ねて環境構築からやってみました。
※ハンズオンはMacOS環境でしたが、この記事はWindows10で環境構築から実施しました。

# Golang環境のインストール
## Chocolateyのインストール
Windowsキー＋cmdでコマンドプロンプトを管理者権限で起動します。

```terminal
Microsoft Windows [Version 10.0.17763.557]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\WINDOWS\system32>@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
Getting latest version of the Chocolatey package for download.
(省略)
You may need to shut down and restart powershell and/or consoles
 first prior to using choco.
Ensuring chocolatey commands are on the path
Ensuring chocolatey.nupkg is in the lib folder
```

## Golangのインストール

```terminal
C:\WINDOWS\system32>choco install golang
Chocolatey v0.10.15
Installing the following packages:
golang
By installing you accept licenses for the packages.
Progress: Downloading golang 1.12.5... 100%
(省略)
```
## インストールの確認
```terminal
C:\WINDOWS\system32>go version
go version go1.12.5 windows/amd64

C:\WINDOWS\system32>echo %GOPATH%
C:\Users\nandymak\go

C:\WINDOWS\system32>echo %GOROOT%
C:\Go\
```

## Goのパッケージ管理ツールのインストール
```terminal
C:\Users\nandymak>go get -u github.com/golang/dep/cmd/dep

C:\Users\nandymak>dep version
dep:
 version     : devel
 build date  :
 git hash    :
 go version  : go1.12.5
 go compiler : gc
 platform    : windows/amd64
 features    : ImportDuringSolve=false
```
# VS Code環境のセットアップ
## Microsoft謹製のExtensionをインストール
![go-vscode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223848/3740ed06-4192-1c55-8c49-7e758808f2bb.png)

## 関連する？？？をインストール
※この辺りはQiita等のコピペで内容は後日精査が必要(Todo)

```terminal
go get -u -v github.com/nsf/gocode
go get -u -v github.com/rogpeppe/godef
go get -u -v github.com/zmb3/gogetdoc
go get -u -v github.com/golang/lint/golint
go get -u -v github.com/lukehoban/go-outline
go get -u -v sourcegraph.com/sqs/goreturns
go get -u -v golang.org/x/tools/cmd/gorename
go get -u -v github.com/tpng/gopkgs
go get -u -v github.com/newhook/go-symbols
go get -u -v golang.org/x/tools/cmd/guru
go get -u -v github.com/cweill/gotests/...
```
※特に最後の「gotests/...」は理解できていません。

# ソースを書いてみる。
今回は日経 xTECHの[Pythonはコードが書きやすい？ご冗談でしょう](https://tech.nikkeibp.co.jp/atcl/nxt/column/18/00682/061100007/?P=3)をサンプルにさせて頂きました。

## サンプルソース(Python)

```factorial.py
def factorial(n):
  if n == 1:
    return 1
  else:
    return n * factorial(n - 1)

print(factorial(5))

>120
```

## Golang版

```factorial.go
package factorial

// Factor は与えられたnの値を1ずつ減らしながら掛けていきます
func Factor(n int) int {
	if n == 1 {
		return 1
	}
	return n * Factor(n-1)
}
```

```factorial_test.go
package factorial

import "testing"

// Factor は与えられたnの値を1ずつ減らしながら掛けていきます
func TestFactor(t *testing.T) {
	got := Factor(5)
	if got != 120 {
		t.Errorf("Error")
	}
}
```
```resault
nandymak@windows10:~/c/Users/nandymak/go/src/factorial$ go test -v
=== RUN   TestFactor
--- PASS: TestFactor (0.00s)
PASS
ok      _/home/i087128/c/Users/nandymak/go/src/factorial   0.042s
```

## 少しはまったところ
Pythonのコードに合わせてif-elseで記述すると、エラー(警告？)が出たので、上記のようにしています。

```factorial.go(？版)
package factorial

// Factor は与えられたnの値を1ずつ減らしながら掛けていきます
func Factor(n int) int {
	if n == 1 {
		return 1
	}
	return n * Factor(n-1)
}
```
```ErrorMessage.text
syntax error: unexpected else, expecting }
syntax error: unexpected return at end of statement
syntax error: non-declaration statement outside function body
```
奥が深いですね。
Pythonで同じように書き直しても同じように動くことまでは確認しました。

```factorial_a.py
def factorial(n):
  if n == 1:
    return 1
  else:
    return n * factorial(n - 1)
print(factorial(5))
>120
```
```factorial_b.py
def factorial(n):
  if n == 1:
    return 1
  return n * factorial(n - 1)
print(factorial(5))
>120
```

## テストコード・スケルトン自動生成
ついでにテストコードの自動生成も試してみました。
テスト対象のソース上で関数名を右クリックし、"Go:Genarate Unit Tests For Function"を実行。
以下のソースが生成されました。

```factorial_test.py
package factorial

import "testing"

// Factor は与えられたnの値を1ずつ減らしながら掛けていきます

func TestFactor(t *testing.T) {
	type args struct {
		n int
	}
	tests := []struct {
		name string
		args int
		want int
	}{
		// TODO: Add test cases.
		{"5の時120を返す", 5, 120},
		{"6の時185を返す", 6, 185},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Factor(tt.args); got != tt.want {
				t.Errorf("Factor() = %v, want %v", got, tt.want)
			}
		})
	}
}
```
"// TODO: Add test cases."の下にテストデータを追加していきます。
何故か、最終行にも「,」が必要です。
実行すると以下になります。

```terminal
Running tool: C:\Go\bin\go.exe test -timeout 30s factorial -run ^(TestFactor)$

--- FAIL: TestFactor (0.00s)
    --- FAIL: TestFactor/6の時185を返す (0.00s)
        c:\Users\999000_087128\go\src\factorial\factorial_test.go:23: Factor() = 720, want 185
FAIL
FAIL	factorial	2.195s
Error: Tests failed.
```

「6」の時は720が変えるので、エラーを検出しています。

---
## Todo
- WSLとVS Codeで動くようにする
- VS CodeのExtension関連と思われるGoのモジュールについて用途などを理解する。
- "go get -u -v github.com/cweill/gotests/..." の用法を理解する。
- if-elseでSyntaxErrorが発生する理由を理解する。