Golang 覚書 その1

  • 2
    Like
  • 0
    Comment
More than 1 year has passed since last update.

macでGo言語1.5入門

Go言語環境構築

Go言語のインストールを行う。
Go言語用のパッケージマネージャーあるらしいけど一先ず無視して、homebrewからインストール

brew install go

.zshrcに以下のexportを追加

export GOROOT=/usr/local/Cellar/go/1.5/libexec
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

GOROOT:homebrewでインストールしたGO言語の本体のパス
GOPATH:ワーキングディレクトリ

Go言語は1プロジェクトで1ワーキングディレクトリを設定する形になる。
その関係でGOPATHには複数の指定が可能となる。
複数のディレクトリがある場合Windowsはセミコロン、Linuxはコロンを使います。
ただ、複数を指定した場合"go get"コマンドで外部パッケージを読み込んだ時、先頭のディレクトリにダウンロードされる。
※ macはLinuxと同じでコロンを利用する
例:export GOPATH=$HOME/go:$HOME/_go

vimの設定

Go言語のvimプラグインとして有名なものがあるのでそちらを追加する。

NeoBundle 'fatih/vim-go'
"ハイライトの指定
let g:go_highlight_functions = 1
let g:go_highlight_methods = 1
let g:go_highlight_structs = 1
let g:go_highlight_operators = 1
let g:go_highlight_build_constraints = 1
let g:g:go_fmt_autosave = 0  " 保存時のオートフォーマットを無効
let g:go_fmt_command = "goimports"
let g:neocomplete#sources#omni#input_patterns.go = '\h\w\.\w*'

vim-go
vimでGo言語書く際のすごい(らしい)プラグイン
ビルド、ドキュメント表示などいろいろな物が詰まったオールインワンパッケージ
内部としてはGo言語のパッケージに依存をしているため、必要なパッケージだけをインストールする形でもよい。

※vim-goで依存しているパッケージはgitとMercurialにに依存しているらしいので
もし、Mercurialがインストールされていない場合先にインストールをしてやる必要がある。

準備ができたらvimを開いて

:NeoBundleInstall
:GoInstallBinaries

実行してやる。
GoInstallBinariesで必要なパッケージをDLしてくれる。

Goのコマンド

Command一覧

Vet

vetコマンドはGoのソースコードをチェックを行ってくれるツール。
ビルドで発見できないような問題を発見してくれることもあるらしい。

go vet -printf
go vet -methods
go vet -structtags
go vet -composites

vim-goのコマンド

コマンド 内容
:GoImport (パッケージ名) パッケージ記述追加
:Drop (パッケージ名) パッケージ記述削除
:GoDoc (パッケージ名) パッケージドキュメント閲覧
:GoDef (関数名など) 定義へジャンプ
:GoPlay play.golang.orgにファイルの内容を転送する
:GoVet (Option) vim上でgo vetを実行する
:GoBuild 現在のパッケージをビルドしてくれる
:GoRun ビルドして現在のパッケージを実行してくれる

Hello World

import

別パッケージのAPIを利用するためには対象のパッケージをインポートする必要がある。
書き方はいろいろあり

import "fmt" 
import(
    "fmt"
    "string"
)
import(
    . "fmt"
)

通常パッケージをインポートすると パッケージ名.関数名 で書くが、ドットを付けてインポートするとパッケージ名を省略して書くことが出来る。

import(
    f "fmt"
)

この書き方だとパッケージ名にエイリアスを付けて書くことができるようになる。
fmt. とパッケージ名を指定して書くコードを f. で関数の呼び出しが可能となる。

-

最小のプログラム

package main

import(
    "fmt"
)

func main(){
    fmt.Println( "Hello world" )
}

Go言語はmainパッケージ内のmain関数からプログラムの実行が開始される。
パッケージ名がmainではない、またはmain関数がないとエラーになる。

文法

セミコロン

Go言語の文法にセミコロンは必要だが、ソース上に書く必要はない。

-

変数

Go言語には変数の宣言方法が複数存在する。

var 変数名 型 = 初期値   // ①
var 変数名    = 初期値     // ②
変数名 := 初期値           // ③

Go言語は静的型言語の為①の様に変数の宣言時に型を指定する必要があるが、②③の場合は初期値により型推論をしてくれる。
その為初期値を伴わない変数宣言を行う場合は①の方法しか行えない。

var a int // OK
var b     // error
c :=      // error

複数の変数を宣言する記述

var 変数名, 変数名 型 = 初期値, 初期値
var 変数名, 変数名 = 初期値, 初期値
変数名, 変数名 := 初期値, 初期値

-

型名 内容
uint 32または64ビット符号なし
int 32または64ビット符号あり
string 文字列型
bool 理論値型 trueまたはfalse
uint8 符号なし8ビット整数
uint16 符号なし16ビット整数
uint32 符号なし32ビット整数
uint64 符号なし64ビット整数
int8 符号あり8ビット整数
int16 符号あり16ビット整数
int32 符号あり32ビット整数
int64 符号あり64ビット整数
float32 32ビット浮動小数値
float64 64ビット浮動小数値
complex64 float32の実数部と虚数部を持つ複素数
complex128 float64の実数部と虚数部を持つ複素数
byte uint8の別名
rune int32の別名
uintptr ポインタの値をそのまま格納するのに充分な大きさの符号なし整数

-

定数

const 定数名 = 定数式

定数宣言では変数と違い型を宣言する必要が無い。
また、Go言語は暗黙的型変換を許容してないが定数は型が決まっていないため変数に代入するときキャストを行わないで代入することが可能となっている。

const(
    定数名 = 定数式
    定数名 = 定数式
    定数名 = 定数式
)

この様に記述することで複数の定数を一括で宣言することも可能となっている。

Go言語の定数はこんばいる時に生成される。
そのため定数はこんばいるによって評価可能な定数式である必要がある。

-

制御構造

  • if
if 条件式 {
    ステートメント
}else if 条件式 {
    ステートメント
}else{
    ステートメント
}

ステートメントを記述することができ、ifのスコープ内でのみ有効なローカル変数の準備をすることができる。

if ステートメント; 条件式 {
    ステートメント
}
  • switch
switch ステートメント{
case 条件式: ステートメント
default: ステートメント
}

Go言語のswitch-caseのデフォルト動作はfallthroughではない。
その点を除けばC言語と同じように記載をすることが可能。
ただ、fallthroughではないため、複合条件を書きたい場合C言語と違い","で復数の条件を指定する。

value := 0
switch value{
case 0: fmt.Println( "0" )
case 1, 2: fmt.Println( "1 or 2" )
default: fmt.Println( "Default" )
}

また、switch式は定数や整数である必要がなく、条件式を記載が可能でif-elseの様に扱うことができる。

num := 15 
switch {
case num == 15:
    fmt.Println( "15" )
default:
    fmt.Println( "Default" )
}
  • for Go言語でのループはfor文のみで、whileは存在しない。
// 基本的なfor分
for 初期化; 条件式; 再初期化式 {
}

// C等のように条件式だけを指定が可能
for 条件式 {
}

// 条件式すら省略した場合無限ループになる
for {
}

-

関数

書き方と特徴

func 関数名( 変数名 型 ) 戻り値{
}
func 関数名( 変数名 型, 変数名 型 ) ( 戻り値型, 戻り値型 ){
}

Go言語の関数はこのような形式で書いていく。
関数の戻り値に1つという制約がなく、復数戻り値を返すことができる。
例えば関数で成功の可否と失敗の理由を返すような関数を作ることができる。

a, b := tmp()

戻り値が復数ある関数で戻り値を受け取る場合、この様に記載してやる。
Go言語の制約として変数を定義した場合、その変数を必ず利用してやる必要がある。
こうして書いた時に変数bの情報を利用しない場合、エラーを回避するために余計なステートメントが増えてしまうため必要の無い戻り値を受け取る場合ブランク識別子( _ )に代入してやる。
ブランク識別子で受け取ると、その宣言で値がバインドされないため受け取った以降の利用しなくても問題がない。

a, _ := tmp()

-

名前付き結果パラメータ

Go言語の関数では戻り値に名前を付けることができる。
名前付き結果を利用すると引数の様に通常の変数として利用することができる。
関数が呼び出された時にその型のゼロ値で初期化される。
引数を持たないreturnステートメントを実行した時は、その時点の結果変数にバインドされている値が戻り値となる。

func NameResult()( value1 int, value2 int ){
    value1 = 10
    value2 = 20

    return value1, value2
}
func NameResult2()( value1 int, value2 int ){
    value1 = 10
    value2 = 20

    return 
}

-

Defer

Go言語にはdeferというものがある。
これはdeferを付けて呼び出した関数が処理が終わる直前に指定した関数の呼び出しが行われる様にスケジューリングする。
通常ファイルを開いて、ファイルアクセスが終わった後に閉じるという流れで記載するプログラムを開く→閉じるスケジュール→アクセスこの様に記載することが可能となる。
ファイルのように必ず終了処理が必要なAPIを利用するときに、先に終了処理を予約しておくことができるから最初はちゃんと書いていても、関数の変更を行う時に終了処理を忘れることが無いことが保証できる。

func DeferTest(){
    defer fmt.Println( "Defer" );

    fmt.Println( "task" )
}

deferを指定しか関数の評価は、実行時(要は関数終了時)に行われわけではない。
defer実行をした時に評価される。そのため、変数の値が変更されているなどといったことに注意をする必要はない。
deferは関数内に復数指定することが可能となっている。
復数指定した場合、最後にLIFO順に実行される。

for i := 0; i < 5; i++ {
    defer fmt.Printf("%d ", i)  // 4, 3, 2, 1, 0
}

このようなdeferを行った場合[4, 3, 2, 1, 0]という順番で出力される。

入門用参考URL

Go言語ドキュメントは公式に日本語訳のページがあるのでこちらを見るのが良さそう。