ぎじろぐ

まとまらないんです

Golang ことはじめ 目的~基礎構文まで

目的

  • 要件的にLL系の処理速度できつい部分が増えてきたため、別言語の模索。
  • Golangでチームに新しい風をもたらす。

目標

ブログアプリを作成し、Herokuへデプロイ

学習ステップ

  1. インストール方法
  2. hello wolrd
  3. 基礎構文
  4. DB接続
  5. フレームワーク(Gin)
  6. View
  7. Model
  8. Controller
  9. マイグレーション
  10. シーダー
  11. ルーティング
  12. ログイン / セッション
  13. パーミッション
  14. Heroku on Go lang
  15. ブログアプリ作成

Golangとは?

google謹製の新しい言語、と言っても2009年からプロジェクトはあったらしい。解決する課題として、

  • シンプル
  • 高速
  • 並列性 + タイプセーフ

を重視している。

インストール

brewからサクッと入る。

brew install go

hello world

テキトーにディレクトリを作成し、そこからプログラムを書いてみる

mkdir ~/dev/go-sample
cd ~/dev/go-sample
touch hello.go
package main
import "fmt"

func main () {
    fmt.Printf("Hello, World")
}

実行

go run hello.go
> Hello, World

出力完了。

フォーマット

Goでは開発者間の無駄な争いを避けるため、最初からコーディング規約が整備されている。一つの例を言うと、ソフトタブ(半角スペース)ではなく、ハードタブ(タブ文字)でインデントを行うなど。

atomでは上記のgo fmtを含む便利なプラグインがあったので、インストールしておくと良い。

https://atom.io/packages/go-plus

追加でgithubからパッケ−ジを直接ダウンロードするように指示されるかもしれないが、下記のようにパスを通した上でインストールを行えば良い。

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

例えば、fmtを利用したい場合、⌘+shift+pからgo fmtと入力することで対象ファイルがフォーマットされる。

基礎構文

参考:

mainパッケージとimport

package main

import "fmt" //パッケージの追加

func main() {
    fmt.Println("HEELOO")
}

変数

// 宣言
var message string = "hello world"

// 一度に複数宣言をする
var foo, bar, baz string = "foo", "bar", "baz"

func main() {
    fmt.Println(message) //hello world
    fmt.Println(foo, bar, baz) //foo bar baz    
    
    // 型推論と初期化シンタックス
    message2 := "hello2"
    fmt.Printf(message2) //hello2

    // 定数
    const message3 string = "HEEEELOOOOO"
    fmt.Printf(message3)

    var i int //ゼロ値で初期化
    fmt.Println(i)
}

条件式 /ループ式

func main() {
    a, b := 10, 100
    // if後の()は必要なし、波かっこ省略パターンは存在しない
    // 三項演算子も存在しない、if elseを利用すること
    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")
    }
    
    // 繰り返しはfor意外存在しない
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }

    // continue, break
    n := 0
    for {
        n++
        if n > 10 {
            break //ループを抜ける
        }

        if n%2 == 0 {
            continue //偶数の場合、一つ飛ばす
        }

        fmt.Println(n) //奇数のみ表示
    }

    // switchでfizzbuzz
    s := 10
    switch s {
    case 15:
        fmt.Println("FizzBazz")
        // fallthrough <-- 明示的に次の処理へ移りたい場合
    case 5, 10:
        fmt.Println("Buzz")
    case 3, 6, 9:
        fmt.Println("Fizz")
    default:
        fmt.Println(s)
    }
    // 式のfizzbuzz
    switch {
    case s % 15 == 0:
        fmt.Println("FizzBazz")
    case s % 5 == 0:
        fmt.Println("Buzz")
    case s% 3 == 0:
        fmt.Println("Fizz")
    default:
        fmt.Println(s)
    }
}

関数

func main() {
    // 関数実行
    hello() //hello
    sum(2,3) //5
    fmt.Println(sum_return(5,4)) //9

    x, y := 3, 4

    // この時点でx=4, y=3と逆になっている
    x, y = sum_return_multiple(x, y)
    fmt.Println(x,y)

    // コンパイルエラー、2つある戻り値を一つしか受け取らないため
    x = sum_return_multiple(x, y)

    // _を利用することで第二引数を無視することができる
    x, _ = sum_return_multiple(x, y)
    fmt.Println(x) //3

    res, err := div(10,2)
    fmt.Println(res, err) //5, nill

    fmt.Println(literal())
}

// 関数引数なし
func hello() {
    fmt.Println("hello")
}

// 引数あり、複数の引数が同一型の場合は、最後の一つにまとめることができる
func sum(i, j int) {
    fmt.Println(i+j)
}

// 戻り値
func sum_return (i, j int) int {
    return i+j
}

// 複数の値を返す
// カンマ区切りをすることで、returnした型を指定し、値を返すことが可能
func sum_return_multiple (i, j int) (int, int) {
    return j, i
}

// 名前付き戻り値
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 // 5
}

// 無名関数
func literal () {
  func (i, j int) {
    fmt.Println(i+j)
  }(2,4)
}

配列 / スライス

func main() {
    arr_func1()
    arr_func2()

    // エラー
    // arr1 := [4]string{"a","b","c","d"}
    // fn(arr1)

    // 値渡しなのでarr2内は [a b c d]のまま
    arr2 := [4]string{"a", "b", "c", "d"}
    call_by_value(arr2)
    fmt.Println(arr2)

    slice()

    // 可変長引数
    fmt.Println(sum(1, 3, 5, 7, 9))
}

// 配列は固定長
func arr_func1() {
    var arr [4]string

    arr[0] = "a"
    arr[1] = "b"
    arr[2] = "c"
    arr[3] = "d"

    fmt.Println(arr[0]) //a
}

// 初期化、暗黙的配列長
func arr_func2() {
    arr1 := [4]string{"a", "b", "c", "d"}
    arr2 := [...]string{"a", "b", "c", "d"}

    fmt.Println(arr1, arr2)
}

// 配列の引数は配列長まで見るため、下記に[4]を渡すとエラー
func fn(arr [5]string) {
    fmt.Println(arr)
}

// 配列は値渡し
func call_by_value(arr [4]string) {
    arr[0] = "x"
    fmt.Println(arr) //[x b c d]
}

// 可変長配列の場合はスライスを利用する
// 固定のarrayを使う機会はあまりない
func slice() {

    // []内に値を入れなければslice
    // eg. var s[]string
    a := []string{"a", "b", "c", "d"}
    fmt.Println(a[0]) //a

    // appendでsliceの後に値を追加
    a = append(a, "e", "f")
    fmt.Println(a) //[a b c d e f]

    // rangeを利用し、先頭から順に処理
    for i, s := range a {
        // i = 添字 s = 値
        fmt.Println(i, s)
    }

    // 値の部分切り出し
    //コロンで挟むとその範囲内の値
    fmt.Println(a[2:4]) //c d

    //始点・終点を省略するとそれぞれ先頭 / 末尾となる
    fmt.Println(a[1:]) //b c d e f
    fmt.Println(a[:4]) //a b c d
}

// 可変長引数
func sum(nums ...int) (result int) {
    // numは[]int型

    // keyは不要
    for _, n := range nums {
        result += n
    }
    return
}

map

func main() {
    map1()
}

func map1() {
    // key-value タイプ 初期化宣言
    // intのkeyにstringのvalue
    // 最後の,も必須な模様
    month := map[int]string{
        1: "January",
        2: "Febrary",
    }

    fmt.Println(month)

    // valueの取り出し
    jan := month[1]
    fmt.Println(jan) //January

    // 2つ目の引数も受け取ると、指定したキーがマップにあるかどうかをboolで受け取る
    _, ok := month[1]
    if ok {
        fmt.Println("data exists!!")
    }

    // mapからデータを削除する場合はdelete関数
    delete(month, 1)
    fmt.Println(month) // map[2:Febrary]

  // rangeでforを回すことも可能
  // ただしmapの場合は、順序が保障されない
  for key, value := range month {
    fmt.Printf("%d, %s\n", key, value)
  }
}

ポインタ

Goではポインタを利用することが可能。

func main () {
  var i int = 10
  callByValue(i) //値渡し
  fmt.Println(i) //10

  callByRef(&i) //参照渡し
  fmt.Println(i) //20
}

// 値渡し
func callByValue(i int) {
  i = 20
}

// 参照渡し
func callByRef(i *int) {
  *i = 20
}

次回は型についての学習をする。