Go言語の環境整備と基本文法を学んだ

環境準備

VSCode上で適当なワークスペースを用意した。 今回は「/Users/{わたし}/Documents/Projects/golang_introduction としてディレクトリを用意した。

golangのインストール

macOSを使用しているので、homebrewを使ってインストールする

$ brew info go
go: stable 1.11.1 (bottled), HEAD
Open source programming language to build simple/reliable/efficient software
https://golang.org
Not installed
From: /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/go.rb
==> Requirements
Required: macOS >= 10.10==> Options
--HEAD
    Install HEAD version
==> Caveats
A valid GOPATH is required to use the `go get` command.
If $GOPATH is not specified, $HOME/go will be used by default:
  https://golang.org/doc/code.html#GOPATH

You may wish to add the GOROOT-based install location to your PATH:
  export PATH=$PATH:/usr/local/opt/go/libexec/bin
==> Analytics
install: 91,529 (30d), 265,569 (90d), 943,528 (365d)
install_on_request: 64,960 (30d), 187,842 (90d), 594,605 (365d)
build_error: 0 (30d)

brewで入る最新は1.11.2だった。1.11自体が2018/08リリースと割と新しいものだったのでこのままインストール

$ brew install go

VSCodeについては .goファイルを作成してしまえばVSCodeからエクステンションのインストール案内が出るので そのままインストール。

入門

GOPATHの設定

export GOPATH=/Users/{わたし}/Documents/Project/golang_introduction/

golangはワークスペースプロジェクト単位で開発する。 基本的にコマンドやビルドを行う際にGOPATHを頼りに行うイメージかな? ディレクトリ(=プロジェクト)毎にGOPATHを設定したい等のニーズに答えるためにgoenvといったツールも用意されている。

Hello world

package main

import(
        "fmt"
)

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

実行

$go run hello_world.go
Hello world!

疑問:importしている「fmt」って何?

フォーマットから取ったパッケージ。 基本的にはC言語でいう「print」や 「scan」にあたる関数を提供する。

コメント

// 1行コメント

/*
複数行コメント

*/

import

import (
    "fmt"
    "math"
)

こっちも可

import "fmt"
import "math"

ただし、こちらは推奨されないらしい。

exported name

golangでは名前の先頭が大文字のものは、外部から利用できる。

package main

import (
    "fmt"
)

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

この場合だとPrintlnがそれにあたる。

変数の定義

var で宣言して初期化する事が出来る。 型を明示的に指定する場合、intなどの型名は変数名の後に指定する。

var x int = 10
var y int = 20

//こっちも可能
var xundefinedy int = 10undefined20

文字列の場合はダブルクォーテーションで括る

var foo string = "bar"
fmt.Println(foo) //bar

また、上記より短い宣言もできる

var foo = "bar"

varという初期化子をつけて変数に値を代入した場合、型を省略できる。 省略された変数の型は、代入された値によって自動的に割り当てられる。 (型推論)

他に、:=を使って宣言する方法がある。

foo2 := "bar"

var num1 = 10
num2 := 100

:= を使って宣言するのは型などをいい感じに認識してもらう「暗黙的宣言」という。 「暗黙的宣言」の場合は関数スコープ内でのみ利用が可能である。 つまり、関数の外では:=を使った暗黙的宣言はできない。

定数

最近のJavaScriptのように「const」をつけることで定数が宣言できる

const message = "hello?"

message = "Hi!" //エラー

定数(const)は、文字、文字列、boolean、数値でのみ使える。 定数の場合は := を使った宣言は利用できない。

ゼロ値

変数宣言時、何も値を指定しない場合、ゼロ値(zero value)が与えられる。 ゼロ値といっても数字の0(ゼロ)を入れるだけでなく、変数型によって異なる

  • 数値型の場合(int/float) ... 0
  • bool型の場合... false
  • string型の場合... "" (空文字列)

この辺りはnull判定のときなどに活用できそうだ。

関数,function

よくある感じでOK。引数を指定する場合は上述の通り型が後。

//func( [引数名] [型]) [戻り値の型] {}

func main() {
    calc(1undefined2) //3
}

func calc(a intundefined b int){
    var c int
    c = a + b
    fmt.Println(c)
}

マルチプルリザルト

golangの特徴で、関数が複数の戻り値を返す事ができる。

func main() {
    aundefined b := swap("hello"undefined "world")
    fmt.Println(aundefined b)  // hello__x world~~y
}

func swap(xundefined y string) (stringundefined string){
    x += "__x"
    y += "~~y"
    return xundefined y
}

if文

条件式は丸括弧()で括らない。

func main() {
    evaluation("test"undefined"test") //a==b
    evaluation("test"undefined"hoge") //a!=b
}

func evaluation(a stringundefined b string){

    if a==b {
        fmt.Println("a==b")
    }else{
        fmt.Println("a!=b")
    }
}

for文

よくあるfor文はこんな感じ。他の言語のように丸かっこ()で括る必要はない点に注意

for i :=0; i < count; i++ {
    fmt.Println(i)
}

//0
//1
//2
//3
//4
//5
//6
//7
//8
//9

他の言語でいうwhileっぽいやり方も出来る

var i = 0
for i < count {
    i++ 
    fmt.Println(i)
}

//0
//1
//2
//3
//4
//5
//6
//7
//8
//9

無限ループ

for {
  fmt.Println("forever!")
}

Array, 配列

配列は固定長。長さを変える事ができない。

func main() {
    var a [2]string
    a[0] = "Captain"
    a[1] = "America"
    fmt.Println(a[0]undefined a[1]) // Captain America
    fmt.Println(a) // [Captain America]

    primes := [6]int{2undefined 3undefined 5undefined 7undefined 11undefined 13}
    fmt.Println(primes) //[2 3 5 6 11 13]
}

個人的には変数そのものを出力するとカンマ区切りでなく スペース区切りになる点が気になる

スライス

こっちは可変長の配列だと思っていいらしい

func main() {

    names := [4]string{
        "John"undefined
        "Paul"undefined
        "George"undefined
        "Ringo"undefined
    }
    fmt.Println(names) // [Joh Paul George Ringo]

    a := names[0:2]
    b := names[1:3]
    fmt.Println(aundefined b) // [John Paul]  [ Paul George]  ...①

    b[0] = "XXX"
    fmt.Println(aundefined b)  // [John XXX] [ XXX George]  ...②
    fmt.Println(names) // [John XXX George Ringo]
}

他の言語もそうなのかもしれないけど、スライスは以下のような形で切り取って扱う事が出来る

0 John 1 Paul 2 George 3 Ringo

①では、最初に0〜2を指定している。つまり、以下の範囲を指定したことになる。 つまり、John とPaulが出力対象

0 John 1 Paul 2

次に、1〜3を指定している。つまり、以下の範囲で、PaulとGeorgeが取扱の対象。

1 Paul 2 George 3

スライスは配列への参照のようなもの (らしい)

スライスは配列への参照のようなものです。 スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示しています。 スライスの要素を変更すると、その元となる配列の対応する要素が変更されます。 同じ元となる配列を共有している他のスライスは、それらの変更が反映されます。

②で取り扱っているのがまさにその部分。 一度スライスを他の変数に代入し、他の編集の値を書き換えたとしても、大元のスライスにもその変更が反映される。

/以上