もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

Go言語 勉強(基礎編1)

はじめに

こんばんは。 今年も早くも10ヶ月たって、全く成長してない僕です。

今年のはじめに決めた目標はブレブレになってしまって、どれもこれも中途半端という不甲斐ない結果になっております。

まぁ嘆いてもしょうがないので、今月から気を引き締めて、またやっていこうと思います。

とりあえず今年10月から来年も踏まえて、Go、PythonJavascriptの3つを重点的に勉強しようと思ってます。

比率的には

Go: 4 Javascript: 4 Python: 2

といった比率で勉強しようと思ってます。

なんでこの比率かというと、

Goは、もともと勉強したいというのがあったのですが、知人からGoの案件を紹介してもらったことがきっかけでした。その時は実務経験も勉強もしてない状態だったのでスキル的にミスマッチだったので断ったのですが、せっかくのお誘いを自分のスキル不足で台無しにしてしまうのが悔しかったので、やろうと思いました。

Javascriptは、今年の目標の一つで、フロントエンド側の知見を貯めるというのに続いています。何回かReactやVue.jsの勉強やりましたが、実践で使えるほどの知見は全然溜まってないので、もう少し踏み込んで勉強をしていこうと思っています。FWはReactに絞って勉強しようと思ってます。

Pythonに関しては、唯一成長したというか、調べつつ書けるようになってきている実感があるので、ある程度勉強は続けていこうと思いこの比率です。

あとはSwiftも個人的にやってた時期があるので、どうにか滑り込ませたいなと思ってますが、時間的に厳しいかも。。。

とりあえず今回はGo言語の勉強ですね。

大昔にTour of Goやったくらいなので、さっぱり忘れてます。

さっそくいきます。

やってみた

定番のHello World

package main

import "fmt"

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

go build goファイルで、ビルドされたバイナリが作成されて、そのバイナリを実行すれば、Hello Worldが表示される

$ go build hello.go
$ ./hello
Hello World

$ ls -la
total 3920
drwxr-xr-x   4 kojirock  staff      128 10  3 00:07 .
drwxr-xr-x  21 kojirock  staff      672 10  2 23:54 ..
-rwxr-xr-x   1 kojirock  staff  1999368 10  3 00:07 hello
-rw-r--r--   1 kojirock  staff       72 10  3 00:01 hello.go

go run goファイル名で、ビルドと実行が同時に行われるけど、対象のバイナリは作られない

$ go run hello.go
Hello World

$ ls -la
total 8
drwxr-xr-x   3 kojirock  staff   96 10  3 00:03 .
drwxr-xr-x  21 kojirock  staff  672 10  2 23:54 ..
-rw-r--r--   1 kojirock  staff   72 10  3 00:01 hello.go

制御構文

  1. 繰り返し処理はforのみ、while, foreach, do whileなどはない。でもforのみで再現できる。
// 普通のfor
for i:=0; i < 5; i++ {
    fmt.Println(i)
}

// while
count := 0
for count < 10 {
    fmt.Println(count)
    count++
}

// foreach的なやつ
data_list := map[string]int{
    "aaa": 10,
    "bbb": 20,
    "ccc": 30,
}
for key, value := range data_list {
    fmt.Println("key => ", key, " value => ", value)
}

衝撃だったけど、forしかないことで逆にシンプルだと思った。

break continue は通常通り使えるとのこと。

関数とか変数とか

  1. 頭文字が小文字のメンバは、同パッケージ内からのみアクセス可能
  2. 頭文字が大文字のメンバは、別パッケージからもアクセス可能
  3. 基本的に変数はvar, 関数はfuncで定義
  4. 変数は型推論があるので、型の省略も可能
  5. ローカル変数に関しては、型を指定できず、宣言も := で行う
  6. const定義も可能で、かつ、型を持たない定数が可能。また、関数内でも定義できる。
  7. iotaという列挙型みたいなものをconstでは指定できる。

テストのために自分で作成したローカルパッケージを作成してimportする

f:id:kojirooooocks:20181003025445p:plain

test_01.go

package package_test

// All package_testの全メソッドを呼ぶ
func All() {
    aaa()
    bbb()
    ccc()
    ddd()
    println(bbbvar)
    println(dddconst01)
}

func aaa() {
    println("aaa")
}

test_02.go

package package_test

var bbbvar = 100

func bbb() {
    println("bbb")
}

test_03.go

package package_test

func ccc() {
    cccvar := 200
    println("ccc")
    println(cccvar)
}

test_04.go

package package_test

const dddconst01 = "dddconst01"

func ddd() {
    const dddconst02 = 2
    println(dddconst02)
    println("ddd")
}

hello.go

package main

import "./package_test"

const (
    Red = iota
    Blue
    Yellow
)

func main() {
    package_test.All()
    println(Red)
    println(Blue)
    println(Yellow)
}
$ go run hello.go
aaa
bbb
ccc
200
2
ddd
100
dddconst01
0
1
2

いろんな型

配列型

固定長の配列を作成する。

// 定義1
var data_array [3]int

// 定義2(初期値を指定できる)
var data_array = [3]int{10, 20, 30}

// 定義3(初期値を指定すると、長さの宣言は...で省略できる)
var data_array = [...]int{10, 20, 30}

// 定義4(indexを指定して初期値を設定できる)
var data_array = [...]int{0: 10, 1: 20, 2: 30}

スライス型

可変長の配列を作成する。

// 定義1
var data_array []int

// 定義2(初期値を指定できる)
var data_array = []int{10, 20}

// 追加方法はappend()
var data_array = []int{10, 20}
data_array = append(data_array, 30)
println(data_array[2])
// => 30

マップ型

phpでいう連想配列かな

var data_array = map[string]int{"aaa": 10, "bbb": 20}
data_array["ccc"] = 30 // 代入は簡単

型宣言(type)

独自の型を作成できる。

reflectパッケージの TypeOf() で指定のものがなんの型がわかる。

package main

import "fmt"
import "reflect"

type TestType int

var param1 TestType = 100
var param2 int = 200

func main() {
    fmt.Println(param1)
    fmt.Println(param2)
    fmt.Println(reflect.TypeOf(param1))
    fmt.Println(reflect.TypeOf(param2))
}
$ go run type.go
100
200
main.TestType
int

学生時代C言語やってたときにこんな事やった記憶があるような気がする。

構造体(Struct)

Goはクラスがないから構造体。めちゃ懐かしい。

  1. math/randパッケージを使用して乱数を生成している
  2. strconvパッケージを使用して、数値から文字列へのキャストをこなっている
package main

import (
    "fmt"
    "math/rand"
    "strconv"
    "time"
)

type Player struct {
    life   int
    attack int
    luck   int
}

func (p *Player) is_die() bool {
    return p.life == 0
}
func (p *Player) damege(point int) {
    p.life -= point
    if p.life < 0 {
        p.life = 0
    }
}

type Enemy struct {
    life   int
    attack int
    luck   int
}

func (e *Enemy) is_die() bool {
    return e.life == 0
}
func (e *Enemy) damege(point int) {
    e.life -= point
    if e.life < 0 {
        e.life = 0
    }
}

func main() {
    var player Player
    var enemy Enemy

    player.life = 10
    player.attack = 1
    player.luck = 5

    enemy.life = 10
    enemy.attack = 1
    enemy.luck = 5

    // 無限ループはこの書き方
    for {
        player_attack_offet := 0
        enemy_attack_offet := 0
        rand.Seed(time.Now().UnixNano())
        if player.luck >= rand.Intn(10) {
            // 会心の一撃出す
            player_attack_offet++
        }

        if enemy.luck >= rand.Intn(10) {
            // 痛恨の一撃出す
            enemy_attack_offet++
        }

        enemy.damege(player.attack + player_attack_offet)
        fmt.Println("プレイヤーの攻撃!敵に" + strconv.Itoa(player.attack+player_attack_offet) + "のダメージを与えた!")

        player.damege(enemy.attack + enemy_attack_offet)
        fmt.Println("敵の攻撃!プレイヤーに" + strconv.Itoa(enemy.attack+enemy_attack_offet) + "のダメージを与えた!")

        if player.is_die() == true {
            fmt.Println("敵の勝ち!")
            break
        } else if enemy.is_die() == true {
            fmt.Println("プレイヤーの勝ち!")
            break
        }
    }
}
$ go run struct.go
プレイヤーの攻撃!敵に2のダメージを与えた!
敵の攻撃!プレイヤーに1のダメージを与えた!
プレイヤーの攻撃!敵に2のダメージを与えた!
敵の攻撃!プレイヤーに1のダメージを与えた!
プレイヤーの攻撃!敵に1のダメージを与えた!
敵の攻撃!プレイヤーに1のダメージを与えた!
プレイヤーの攻撃!敵に1のダメージを与えた!
敵の攻撃!プレイヤーに2のダメージを与えた!
プレイヤーの攻撃!敵に2のダメージを与えた!
敵の攻撃!プレイヤーに1のダメージを与えた!
プレイヤーの攻撃!敵に2のダメージを与えた!
敵の攻撃!プレイヤーに1のダメージを与えた!
プレイヤーの勝ち!

ダラダラと長くなってしまったけど、こんな感じなのかな?

ポインタ型

C言語でお馴染みのポインタ。確か僕が挫折したのがポインタ。

ただ、ロジックの見通しよくするためには、必要だし、苦手意識は克服しなければ。

func main() {
    var player Player
    var enemy Enemy

    player.life = 10
    player.attack = 1
    player.luck = 5

    enemy.life = 10
    enemy.attack = 1
    enemy.luck = 5

    // 無限ループはこの書き方
    for {
        battle(&player, &enemy)
        if player.is_die() == true {
            fmt.Println("敵の勝ち!")
            break
        } else if enemy.is_die() == true {
            fmt.Println("プレイヤーの勝ち!")
            break
        }
    }
}

func battle(player *Player, enemy *Enemy) {
    player_attack_offet := 0
    enemy_attack_offet := 0
    rand.Seed(time.Now().UnixNano())
    if player.luck >= rand.Intn(10) {
        // 会心の一撃出す
        player_attack_offet++
    }

    if enemy.luck >= rand.Intn(10) {
        // 痛恨の一撃出す
        enemy_attack_offet++
    }

    enemy.damege(player.attack + player_attack_offet)
    fmt.Println("プレイヤーの攻撃!敵に" + strconv.Itoa(player.attack+player_attack_offet) + "のダメージを与えた!")

    player.damege(enemy.attack + enemy_attack_offet)
    fmt.Println("敵の攻撃!プレイヤーに" + strconv.Itoa(enemy.attack+enemy_attack_offet) + "のダメージを与えた!")
}
$ go run struct.go
プレイヤーの攻撃!敵に1のダメージを与えた!
敵の攻撃!プレイヤーに1のダメージを与えた!
プレイヤーの攻撃!敵に1のダメージを与えた!
敵の攻撃!プレイヤーに2のダメージを与えた!
プレイヤーの攻撃!敵に2のダメージを与えた!
敵の攻撃!プレイヤーに2のダメージを与えた!
プレイヤーの攻撃!敵に1のダメージを与えた!
敵の攻撃!プレイヤーに2のダメージを与えた!
プレイヤーの攻撃!敵に2のダメージを与えた!
敵の攻撃!プレイヤーに2のダメージを与えた!
プレイヤーの攻撃!敵に1のダメージを与えた!
敵の攻撃!プレイヤーに2のダメージを与えた!
敵の勝ち!

おわりに

とりあえず今日はここまでです。

やってみて思ったけど、すごくシンプルで覚えることも少なそうだけど、シンプルだからこそ、設計力が求められそうな感じだと思いました。

でも、Go言語でガッツリwebサービス作るっていうよりはAPI的な立ち位置で使ってるケースが多いからそこまで大規模に使うものでもないのかもしれません。

でもGoのフレームワークもあるみたいだし、せっかくだからwebサービス作る方向で勉強してみようとおもいます。

おすすめの本などあれば教えてください。

そういえば、最近やっとsublimeからvscodeに乗り換えたんですけど、すごくいいですね。同じElectronで作られてるAtomはえらい違いです。

php or jsはphpstorm

pythonはspyder

それ意外のものに関してはvscode

って感じで定着しそうです(もはやpythonvscodeになりそう)

おやすみなさい。