エンジニアもどきの雑記

メモ帳です。きっと。macアプリ配信windowsでもできるようになるまで絶対にメイン機にしないマン。特筆しない限り環境は【win10home,i5-7200u,8g,64bit,巨乳が好き】

Goのencoding/jsonを使ってみる


Goでapiサーバーを作るんだけど、
その上で必須ともいえるjsonエンコードについて少し学ぶ。


json - The Go Programming Language

デコードするにはUnmarshalを利用する。
エンコードするにはMarshal

とりあえず使用するためにImportする

import "encoding/json"


形成用、受け取り用の構造体の定義
今回はよくあるUserで

 type User struct {
      Id int `json:"id"`
      Name string `json:"name"`
      Email string `json:"email"`
  }


それぞれの後ろのついているのはタグjson:"hoge"
これを指定してやることで、Idではなくidjsonを整えてくれる。
そういう機能。

今回はどこからか取得するわけではないので、
同じファイル内にサンプルを置いておく。
以下

 var sampleJson = `
[
{
        "id": 1,
        "name": "hoge",
        "email": "hogehoge@hoge.com"
},
{
        "id": 2,
        "name": "fuga",
        "email": "fugafuga@fuga.com"
},
{
        "id": 3,
        "name": "foo",
        "email": "foofoo@foo.com"
}
]
`



とりあえずUnmarshalしてみる

package main

import (
        "encoding/json"
        "fmt"
)

type User struct {
        Id    int    `json:"id"`
        Name  string `json:"name"`
        Email string `json:"email"`
}

var sampleJson = `
[
{
        "id": 1,
        "name": "hoge",
        "email": "hogehoge@hoge.com"
},
{
        "id": 2,
        "name": "fuga",
        "email": "fugafuga@fuga.com"
},
{
        "id": 3,
        "name": "foo",
        "email": "foofoo@foo.com"
}
]
`

func main() {
        jsonData := ([]byte)(sampleJson)
        var user []User
        if err := json.Unmarshal(jsonData, &user); err != nil {
                fmt.Println(err)
                return
        }

        for _, u := range user {
                fmt.Println(u.Id, u.Name, u.Email)
        }
}


結果

[(゜-゜):daichi]@:json_test$ go run main.go
1 hoge hogehoge@hoge.com
2 fuga fugafuga@fuga.com
3 foo foofoo@foo.com


なんかjsonの宣言にすげぇ時間とられた。,とか諸々でエラーでまくってて悩んでた。

デコードでけた。

じゃあMarshalもやってみる。

上記コードのuserが以下

[{1 hoge hogehoge@hoge.com} {2 fuga fugafuga@fuga.com} {3 foo foofoo@foo.com}]

なのでそのまま利用してみる
先ほどはったMarshalのリンク先を見ると、Marshalの引数はinterface{}であることがわかる。
func Marshal(v interface{}) ([]byte, error)

とりあえず突っ込むものを作ってそれからやることにする。

package main

import (
        "encoding/json"
        "fmt"
)

type User struct {
        Id    int    `json:"id"`
        Name  string `json:"name"`
        Email string `json:"email"`
}

var sampleJson = `
[
{
        "id": 1,
        "name": "hoge",
        "email": "hogehoge@hoge.com"
},
{
        "id": 2,
        "name": "fuga",
        "email": "fugafuga@fuga.com"
},
{
        "id": 3,
        "name": "foo",
        "email": "foofoo@foo.com"
}
]
`

type mapSli []User

func main() {
        jsonData := ([]byte)(sampleJson)
        var user []User
        if err := json.Unmarshal(jsonData, &user); err != nil {
                fmt.Println(err)
                return
        }

        var jsonArr mapSli
        for _, u := range user {
                jsonArr = append(jsonArr, User{
                        Id:    u.Id,
                        Name:  u.Name,
                        Email: u.Email,
                })
        }

        s, err := json.Marshal(jsonArr)
        if err != nil {
                fmt.Println(err)
                return
        }

        fmt.Println(string(s))
}


そうしてできたのがこちら。
go難しい・・・
まだまだよくわかってないんだなぁ、と書いてて思う。
頑張ろう

そして実行

[(゜-゜):daichi]@:json_test$ go run main.go
[{"id":1,"name":"hoge","email":"hogehoge@hoge.com"},{"id":2,"name":"fuga","email":"fugafuga@fuga.com"},{"id":3,"name":"foo","email":"foofoo@foo.com"}]


ういい。
type mapSli []Userを宣言。いちいちtypeにしなくてもよかっただろうけどゆるちて。いろいろやりたい年ごろ。

unmarshalで作ったuserをjsonArrに代入。これでスライスになる。
準備はできたので、そのスライスをMarshalする。それを表示。
以上!

ういうい。
毎日少しずつでも頑張る

巨乳のお姉さんに手取り足取りgoを教えてもらいたい。

【日曜日恒例】絵が下手すぎるので書き始めようと思いますpart29

前回
dddpga1.hateblo.jp


スマホから更新なので普段みたいな前回次回は後で編集します。
しました

タグも後で。


レナたそ誕生日おめでとうってことで


f:id:dddpga1:20180513232356j:plain
いつも以上に書き込まずに終わってるけど

体調不良続いてるんだ、ゆるちて


あ、星海祭参加します。


よろしくね


ではまた次回

次回
dddpga1.hateblo.jp


GoLangやるpart8

前回
dddpga1.hateblo.jp

c言語初学生の鼻を叩きおってきたポインタとかそこらへんやる。
前にc#のこんな感じの記事を書いた時に少しだけ詳しくやったけど、
俺も正直まだよくわかってないと思う。

ポインタとは、メモリ上の型と値の場所。
ってとりあえず覚えてる。かも。


実際、他の言語の他の機能でもそうだけど、
これって何のためにあるの? ってのを理解しないと話にならない。
俺はいまだにjsのcloneを勢いで書いている(冗談だよ! マジだった時もあるけど

で、goのポインタは何に使われるかっていうと
ほぼcの互換だと思う。
フレームワークとか触るとわりと頻繁にでてはくるんだけど、
結局はそういうとこにたどり着いてる気がする。

main.go

package main

import "fmt"

var i int

func main() {
        p := &i
        i = 5

        fmt.Println(*p)

        *p = 10
        fmt.Println(i)
}


&*についてまず理解しないといけない。
まず*だが、このアスタリスクはポインタ型の宣言。型だ。型なんだ。
&の方はアドレス演算子演算子だ。演算子なんだ。

上記コードで見ていく。
var i int
まずはmain packageの変数としてint型のiを定義。

p := &i
は、int型のiを&を使って、ポインタ型を生成し、pに代入している。
どういうことか一旦、pを出力してみる。
pの直後にfmt.Println(p)を入れて実行

以下がでる

0x545e20

これが、iが割り当てられているメモリのアドレス(住所っていうけど位置の方がわかりやすい気がする)になる。

次のi = 5はまぁ今までやった通りで、iに対して5を代入している。
問題の次の行。
fmt.Println(*p)

これが、デリファレンスである。
ポインタ型の宣言が→var 変数名 *int
ポインタ型のデリファレンスが→*変数型

これがまぁ最初はわかりづらいづらいづらい。
変数pはp := &iで変数型が入ってる。なのでpの前にを付けることで
デリファレンスができる。

デリファレンスって何かっていうと、
ポインタ型が保持するメモリ上のアドレス(今回でいう0x545e20)を経由してデータ自体を参照する仕組み。

なので、fmt.Println(
p)の結果はiの中身になる。直前で5を代入しているので、まず5が表示される。

で、次。
*p = 5。ここでたった今5を表示したpに10を代入している。
そしてその次の行でfmt.Println(i)としている。そこで表示されるのは10

先述の通り、
pはデリファレンスでiに入っている値をpのiのアドレス経由で参照している。
そこに10を入れてるので、結果iに10が入っているってだけ。

所謂参照渡しの機能である。・・・よね?

例のごとくphpで言うけど、phpは引数に&を付けるだけで参照渡しを自動で行ってくれる。
それの内部的なものって考えるとすごいわかりやすい気がする。

とりあえずこれだけ。*と&だけわかってれば今はいいや。
配列で使ったりするともっとやっかいなんだけど、各々やろう。


構造体

struct
他でいうenumみたいな。

その前にtypeをやっとく。
これは型のエイリアスを指定できるって機能。

main.go

package main

import "fmt"

type myArr [3]int

var i myArr

func init() {
        i[0] = 1
        i[1] = 2
        i[2] = 3
}

func main() {
        fmt.Println(i)
}


実行

[1, 2, 3]


大事なのは

type myArr [3]int

var i myArr



type 名前 型で型のaliasができる。
なのでvar i myArrvar i [3]intと同義になる
以上。

俺の書いてきたどれでも当てはまるんだけど、もっと細かい仕様やら
条件やら沢山あるんだ。でもそれはここで書ききれない。

じゃあstructやる。typeが必要だったんだ。

定義

type type名 struct {
    //定義
}


実際に動きとかを見てみる。


main.go

package main

import "fmt"

type Point struct {
        X, Y int
}

func main() {
        poi := Point{1, 2}
        fmt.Println(poi.X)
        fmt.Println(poi.Y)

        poi.X = 10
        poi.Y = 20
        fmt.Println(poi.X)
        fmt.Println(poi.Y)

}


実行

1
2
10
20


みただけだとmapとやってることは変わらないかもしれないけど、
これが重要だったりする。特にgoだと。

構造体に構造体を入れこんだりもできるし、言ってしまえば日本語も使える。
わかりやすいリスト程度で最初はいいと思う。使っていく内に重要性がわかると思うし、
今からやるinterface{}でさらにわかると思う。

ゲームとかつくるとすごいわかりやすいんだけど、構造体を用意しておいて引数とかに渡したところで宣言した構造体の値が変化するわけではないので、先ほどの参照渡しでいろいろと値を渡してあげて、ってのが重要になる。作ろう。

さて、作るのには時間がかかるのでとりあえずnew演算子やる

new演算子


動作的には&と変わらないと思う。
指定した型のポインタ型を生成する機能、とのこと。

main.go

package main

import "fmt"

type Point struct {
        X, Y int
}

func main() {
        p := new(Point)

        p.X = 1
        p.Y = 2

        fmt.Println(*p)
}


実行

{1, 2}



メソッド


他でいう関数、とは少し違う。
任意の型に特化した関数を定義するための仕組み、とのこと。

func (レシーバー名 レシーバー型) 関数名 [返り値型] {
    //処理
}


レシーバーってなんぞやっていうと
上記した特化する型とそれの変数名になる。

とりあえずレシーバーの定義があるかないかでしか通常の関数と変わりない。

どういうことができるかってのが以下

main.go

package main

import "fmt"

type Point struct {
        X, Y int
}
type IntArr [3]int
type FloatSli []float64

func (p *Point) Test() {
        fmt.Println(*p)
}

func (i IntArr) Test() {
        fmt.Println(i)
}

func (f FloatSli) Test() {
        fmt.Println(f)
}

func main() {
        p := Point{1, 2}
        p.Test()

        ia := IntArr{1, 2, 3}
        ia.Test()

        fs := FloatSli{1.11, 2.22, 3.33}
        fs.Test()
}


実行

{1 2}
[1 2 3]
[1.11 2.22 3.33]


えぇ・・・同じ名前の関数が三つも・・・
しかもそれぞれ型に属して呼び出されているような・・・
ってなるよね

っていうのがメソッド。
型に対して定義がされて、型から呼び出されているような感覚。
この型の変数だった場合は一致するレシーバーの型を見て、って感じで。


そしてこれに付随するように型のコンストラクタってのがあるからやっておく。
type、構造体、メソッドときて頭がこんがらがる。

main.go

package main

import "fmt"

type Point struct {
        X, Y int
}

func NewPoint(x, y int) *Point {
        p := new(Point)
        p.X = x
        p.Y = y

        return p
}

func main() {
        fmt.Println(NewPoint(11, 22))
}


実行

&{11 22}


Point構造体に対して初期化を行ってる。
これどう便利なのかとかどうとかは下記でわかる気がする。
そもそもmain()内でhoge := Pointみたいなことができない。
以上。

やってみればわかる。書くんだ(説明ができない力不足でごめん


構造体とスライス。

便利。goを書く上でよく使われるパターンらしい(自分は技術力が足りな過ぎてあまり書いてない気がする


main.go

package main

import "fmt"

type Point struct {
        X, Y int
}

func main() {
        pt := make([]Point, 5)

        for _, p := range pt {
                fmt.Println(p.X, p.Y)
        }

}


実行

0 0
0 0
0 0
0 0
0 0


簡単に作れるよって話。



interface



本題。
個人的には学び始めてからこいつがなぜか一番やっかいだった。

interfaceとは、どんな型にも対応した型。
ってのが簡単にいうとなんだけど、当然ただそんな考えで理解はできない。
俺はできなかった。

本ほぼそのままだけど以下

main.go

package main

import "fmt"

type Stringify interface {
        ToString() string
}

type Person struct {
        Name string
        Age  int
}

func (p *Person) ToString() string {
        return fmt.Sprintf("%s(%d)", p.Name, p.Age)
}

type Car struct {
        Number string
        Model  string
}

func (c *Car) ToString() string {
        return fmt.Sprintf("[%s] %s", c.Number, c.Model)
}

func main() {
        vs := []Stringify{
                &Person{Name: "hoge", Age: 12},
                &Car{Number: "1234", Model: "5678"},
        }

        for _, v := range vs {
                fmt.Println(v.ToString())
        }
}


実行

hoge(12)
[1234] 5678


Stringify interfaceを定義。
Person構造体とCar構造体を宣言してある。それぞれがそれぞれの属性を持ってる。
そしてそれぞれにToStringメソッドを作成してやる。

それらをStrigifyで内包してやって、それらを実行してる。

簡単にいうと、別々の型をまとめて総称つけてあげたり、似たような属性を持たせるためのもの。
それぞれを呼べば処理的にはそれで済むが、こういうのも便利機能。


とりあえず、うん。
ここまでで終了。

もっとこれからgoをしっかりと、深く勉強していこう。
いい復習になりました。まだまだ勢いで書いてるとこはあるけど。

じゃあまた別の記事で。

GoLangやるpart7

前回
dddpga1.hateblo.jp

本来もう少し先にやるべきだった
for文、switch文、if文、定数をやろう。そうしよう。
多分すぐ終わるからgoroutineもやろう。
deferもって思ったけどまぁ、いつか。やらないかも

今言ったばかりだけどまじでもう少し先にやるべきだった。


定数


他の言語と同様の定数がある。
使い方は言うまでもないだろうけど、決め打ちの値を最初から入れて置ける変数。
変数をやった時にやるべきだったね。うん。

とりあえず書き方を覚えれば・・・。

main.go

package main

import "fmt"

const X = 1
const (
        Y = 2
        Z = 3
)

func plus(a, b int) int {
        const W = 4

        return (a + b) * W
}

func main() {
        res := plus(X, Y)
        fmt.Println(res * Z)
}


実行

36


これがすべてだ。
この記事のシリーズでスコープをやってはいないから、説明を端折るけど、
再代入できない変数が定数。式も入れられる。以上。


if文 switch文


どの言語にもあるけど、
もし、〇〇だったら△の処理、■■だったら◇◇の処理
っていうのがif文。そしてswitch文

これも書き方を覚えよう。


main.go

package main

import "fmt"

func main() {
        x := 5

        if x > 1 {
                fmt.Println("xは1より大きい")
        } else if x == 6 {
                fmt.Println("xは6")
        } else {
                fmt.Println("誰だお前は")
        }

        if a, b := 1, 2; a < b {
                fmt.Println("使いどころを見つけて使わないと覚えないよね")
        }

        switch x {
        case 1:
                fmt.Println("xは1")

        case 5:
                fmt.Println("xは5")

        default:
                fmt.Println("hoge")
        }

        switch c := 5; {
        case c > 1:
                fmt.Println("cは1より大きい")

        case c < 10:
                fmt.Println("xは10より小さい")
        }
}


実行

xは1より大きい
使いどころを見つけて使わないと覚えないよね
xは5
cは1より大きい


面白いのは、というか他と違うのは簡易式が使えるってところ。

if 式; 条件 {
    //処理
}

switch 式; 条件 {
case 条件:
    //処理
}


みたいな。
最後のswitchは式だけ書いて条件をcaseで行ってる。

さて次。

for文



これも書く。

main.go

package main

import "fmt"

func main() {
        stop := 0
        for {
                fmt.Println("無限ルーーーープ")
                stop++

                if stop == 3 {
                        fmt.Println("しょうもないので抜けます")
                        break
                }

        }

        for i := 0; i < 10; i++ {
                fmt.Println("よくあるルーーーープ")
        }

        testArr := map[int]string{1: "hoge", 2: "fuga", 3: "foo"}
        for x, y := range testArr {
                fmt.Println("foreach的な")
                fmt.Println(x)
                fmt.Println(y)
        }

}


実行

無限ルーーーープ
無限ルーーーープ
無限ルーーーープ
しょうもないので抜けます
よくあるルーーーープ
よくあるルーーーープ
よくあるルーーーープ
よくあるルーーーープ
よくあるルーーーープ
よくあるルーーーープ
よくあるルーーーープ
よくあるルーーーープ
よくあるルーーーープ
よくあるルーーーープ
foreach的な
1
hoge
foreach的な
2
fuga
foreach的な
3
foo


pythonとかでもみかけるrangeがある
forEachは実質ないので注意。
whileにしても何にしてもforで書けってのがgoのマナー。

rangeの使いかたは以下

for インデックス, value := range 配列なりsliceなり{
    //処理
}


こういった文法の少なさは学習コストの低さにつながって非常にいいと思う。
他に憶えなきゃいけないこと沢山あるし。

と、goroutineはとりあえず前回のでいい気がしてきた。
のでやらない。

ので代わりにinitやる


init


どういうものかというと所謂コンストラクタ的なもの。
前に書いた通り、goはmain packageのmain関数から動くが、それよりも前に
先に初期化処理を走らせることができる。

initはパッケージの初期化なので、他のパッケージにしても呼び出される時に実行されるので
mainだけじゃない。

とりあえず書く

main.go

package main

import "fmt"

var x = ""

func init() {
        x += "ABC"
}

func main() {
        fmt.Println(x)
}


実行

ABC


mainより先に処理が走ってるのがわかる。


他の言語でもわりと当たり前に使う文法だったから少し軽い感じだけど、
とりあえずここまで。

また次回。

最終回
dddpga1.hateblo.jp

GoLangやるpart6

前回
dddpga1.hateblo.jp

宣言通りmapをやる。
参照型の続き。

phpでいう連想配列c#でいうdictionaryみたいな。
これもmakeを使う。
mapの時のmakeは以下。

make(名前)
make(名前, 要素数)


挙動は使って確認する。
わかりやすく、まずはphp連想配列を見てみよう

$arr = [
    "hoge" => 1,
    "fuga" => 5,
];

var_dump($arr["hoge"]);
// 1


いわゆるkey&value
スライスや配列に入る値に対して、インデックスの代わりに指定するkeyが入る。
これが連想配列でそれのようなことをgoではmapとして使える。


main.go

package main

import "fmt"

func main() {
        m := make(map[string]int)

        m["hoge"] = 1
        m["fuga"] = 5

        fmt.Println(m)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
map[hoge:1 fuga:5]


var m map[keyの型]valueの型で宣言する。
呼び出し型も今までと同様。
代入も同様。
で、スライスや配列のように最初から値の指定も当然できる。
それが以下

main.go

package main

import "fmt"

func main() {
        m := map[int]string{1: "hoge", 5: "fuga"}

        fmt.Println(m)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
map[1:hoge 5:fuga]


なんとなく想像はできると思うけど、やり方は以下
map[keyの型]valueの型{key: value[, key: value...]}

phpばっかりやってると関数のスコープ以外で{}を使うのって
すごい違和感なんだよね。最近慣れてきたけど。
で、上記のリテラルを改行で書く時、最近はやりの最後でも,を付けるってのをやらないと
エラーになる。
diff見るときにわかりやすくなるらしいね。


var m map[int]int{
    1:1,
    2:2,
    3:3,
}

みたいにしないとダメ。

そして想像できる限りの入れ子が当然できる。
mapの中にslice、mapの中に配列。mapの中にmapなど。

main.go

package main

import "fmt"

func main() {
        m := map[int][]int{
                1: {1},
                2: {1, 2},
                3: {1, 2, 3},
        }

        fmt.Println(m)

        m2 := map[string]map[int]string{
                "hoge": {1: "hhhh"},
                "fuga": {2: "ffff"},
        }

        fmt.Println(m2)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
map[1:[1] 2:[1 2] 3:[1 2 3]]
map[hoge:map[1:hhhh] fuga:map[2:ffff]]


[]とか型とかが続いて見づらいけど、最初にやった宣言の仕方で何も変わりない。
しっかりと読むことが大事。


また、参照の仕方とかは相変わらず同じなんだけど、存在チェックが少し特殊なので
やっておく。

main.go

package main

import "fmt"

func main() {
        m := map[int][]int{
                1: {1},
                2: {1, 2},
                3: {1, 2, 3},
        }

        mv, ok := m[1]
        fmt.Println(ok)
        fmt.Println(mv)

        m2 := map[string]map[int]string{
                "hoge": {1: "hhhh"},
                "fuga": {2: "ffff"},
        }

        mv2, ok := m2["hoge"]
        fmt.Println(ok)
        fmt.Println(mv2)

        mv3, ok := m2["foo"]
        fmt.Println(ok)
        fmt.Println(mv3)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
true
[1]
-----
true
map[1:hhhh]
-----
false
map[]


okという二つ目の値を入れてやる。
それのboolean値であるかないかの判断ができる。
okを使うのがマナーらしいのでここは基本okで

なんでこんな便利なもんがあるかっていうと、null的なものが初期で入っているわけではないため。
それが、一番最後のmap[]ってとこを見ればわかる。
ない値なのに、nilが返ってくるわけでもない。

そういったもののミスを減らすためだと思う。

mapから値を削除する。

deleteって関数がある
使い方は以下

delete(配列, key)

keyで指定して削除するだけ。
書かないことには覚えないので書く。

main.go

package main

import "fmt"

func main() {
        m := map[int][]int{
                1: {1},
                2: {1, 2},
                3: {1, 2, 3},
        }

        fmt.Println(m)

        delete(m, 2)

        fmt.Println(m)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
map[3:[1 2 3] 1:[1] 2:[1 2]]
map[1:[1] 3:[1 2 3]]


うい。
さて、次にchannelやる


channel


これをやる前にgo routineをまず憶えなきゃいけない。
なのでgoroutineをやる。

async/await。
いわば非同期処理を簡単に実行できる機能がゴルーチン。
そしてそれを表現するためにfor文を使う。
ので、先に簡単に説明する。

他の言語でもある通りのfor文がgoでも利用できる。
利用方法が少し特殊な場合もあるけど、今まで同様に書ける。

また、それを用いてゴルーチンがどういったものかとりあえず以下に。

main.go

package main

import "fmt"

func routine(a string) {
        for {
                fmt.Println(a)
        }
}

func main() {
        go routine("routine")
        for {
                fmt.Println("main")
        }
}


実行

は、ちょっと貼り付けられない状態なので説明すると、
mainとroutineが不規則に表示される。
goがゴルーチンを実行させる予約語
上記はroutine関数をゴルーチンで実行させてる。
forは繰り返しね。


で、channel
channelゴルーチン間で値の受け渡しをするためのもの。

var 変数名 chan 型

これもmakeを使って宣言できる。

make(名前[, buffer size])
バッファーのサイズは指定しないと0がデフォルトらしい。

チャネルはqueueの性質を持つ。
FIFOで先に入れたデータを先に取り出す、ロケットエンピツ(最近知らない人が増えたとか・・・)
そのデータがはいっていられる場所がバッファのサイズ。

ゴルーチン間で利用するものであって、送信用と受信用のものの宣言がある。
送信<-chan受信chan<-アローの場所受けるか、出すか、みたいな。

そしてゴルーチンで利用するためのものなのでゴルーチンがないと当然動かない。
とりあえず動かそう。

main.go

package main

import "fmt"

func routine(ch <-chan int) {
        for {
                i := <-ch
                fmt.Println(i)
        }
}

func main() {
        ch := make(chan int)

        go routine(ch)

        i := 0
        for i < 50 {
                ch <- i
                i++
        }
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49


ゴルーチンの意味のない処理だけど。

func routine(ch <-chan int) {

のroutineの引数は<-chanch<-chanを受信する。
<-の受信、送信に注意したい。

main()関数の中でもmakeでチャンネルを生成してchに代入し、
iをchに入れてやる。その時も:=ではなく<-であることに注意。
chan型のものに入れるから<-を使ってる。

routine関数内でi := <-chってやってるから余計にわかりづらいけど、
こっちは受け取った<-ch(送信)をiに代入している。
ここも上手く説明できないからまだまだ力不足。
頑張ろう。

結果的にはゴルーチンで非同期に実行され続けてるだけ。


channelのclose

ファイル操作の時みたいにopen,closeという概念がchannelにはある。
makeで宣言した時、生成した時にはopen状態。
open状態だと<-で送信されたものをバッファサイズ分受け入れ放題。
が、それを閉じることができる。それがclose


例を作ってみる。

main.go

package main

import "fmt"

func routine(ch <-chan int) {
        for {
                i := <-ch
                fmt.Println(i)
        }
}

func main() {
        ch := make(chan int)

        go routine(ch)

        i := 0
        for i < 50 {
                ch <- i
                i++

                if i == 30 {
                        close(ch)
                }
        }
}


実行

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0
0
panic: send on closed channel

goroutine 1 [running]:
main.main()
        /home/daichi/go_dev/src/sample/main.go:19 +0x8b
exit status 2


closeはchannelの入り口に蓋をするような状態。
closeしたchは送信を受けられなくなる。()
が、queueにたまっている値は受け取り続けることはできたりする。
蓋だけ閉じられたけど、出す部分は生きてるので中身がある限り出し続けます
みたいな状態。



と、今回はここまで。
次回以降gorutineをちゃんとと、for文while文とかやると思う。
そのあとにinterfaceとか構造体、ポインタとかかな。

それでとりあえず終わり。
↑のエラーで出てるpanicとか、gotoとかあんまり利用しないものは飛ばすつもり。
ラベルはやりたいけど。

この記事を書いてから気づいたけど、
自分で書くのもそうだけど読み返すのも復習になっていいなって思った。
ガリガリ更新してこう。

また次回。

次回
dddpga1.hateblo.jp

GoLangやるpart5

前回
dddpga1.hateblo.jp
前に配列をちょこっとした時にちょこっとだけ話した参照型をやる。


slice,map,channelの三つが定義されてる。
と、それぞれを作成するためのmakeって関数がある。

じゃあ一個ずつ見てく。

slice


var s []int c#とかで見るものによく似てるね。

main.go

package main

import "fmt"

func main() {
        s := make([]int, 10)
        fmt.Println(s)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
[0 0 0 0 0 0 0 0 0 0]

sliceのmakeの使い方が以下

make(型, 要素数と容量)
make(型, 要素数, 容量)


型は配列と同様[]を付けるのを忘れずに。
配列に似てる。
代入と参照も配列と同様。

けど内部的、というかプログラム的には配列とは全く別のものなので注意。
pythonもタプルだのなんだのってこういうの多いよね。
使いこなせばすごい便利なのだろうけど自分はまだまだ。

で、要素数はわかるけど(配列でいう[]の中に書いてる数字だよね?)、容量ってのは何か。
要するに、宣言したsliceが確保しているメモリの範囲。っていうか容量か。

確認してみる。
len()cap()って関数がある。
それぞれが要素数と容量を取得する関数。

main.go

package main

import "fmt"

func main() {
        s := make([]int, 10)
        fmt.Println(len(s))

        s2 := make([]int, 3, 5)
        fmt.Println(len(s2))
        fmt.Println(cap(s2))
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
10
3
5


s2は要素数は3でintのデフォルト値0が三つ入ったスライスが出来上がるけど、
メモリ上は5つ分まで確保していて、後に拡張する必要がないってこと。
多分。

容量と要素数が同じスライスを拡張しようとする時、
また別の容量を持ったメモリ上にそのままコピーするため、
大きなパフォーマンスになるため、こういった容量指定が大事になる。
らしい。
こういうこと書いてある本って非常に良い。

配列みたいな以下のような書き方もできる

s := []int{1,2,3}

配列の[...]intみたいな書き方と似すぎてて最初は困惑する。


簡易スライス式


cとかpythonでできるhoge[0:2]みたいなのができる。
cに近い書き方って言われるくらいだし当たり前か。

main.go

package main

import "fmt"

func main() {
        s := []int{1, 2, 3, 4, 5}
        one := s[0:2]
        two := s[0:]
        three := s[:3]
        four := s[:]

        fmt.Println(one)
        fmt.Println(two)
        fmt.Println(three)
        fmt.Println(four)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
[1 2]
[1 2 3 4 5]
[1 2 3]
[1 2 3 4 5]


基本的に[インデックスのどこから:インデックスのどこまで]で、指定がなかったら
最初から最後まで。
また、要素数外の数を指定するとエラー。
で、切り取ることができる。
ptyhonの何桁区切り、って書き方はできないのかな?
調べてみよう

ちょいとググってきたけどどうやらなさそう。
pythonだと[0:0:3]みたいにもう一つ指定できてインデックスをその数字区切りで取得できたりする。
あんまり使ったことないけど。


さて、前に要素数の違う配列を宣言した別の配列に代入したら型が違うとエラー、ってのを
やったと思うんだけど、どうやら要素数まで含めて型になるらしいからあのようなエラーがでるみたい。

が、スライスは可変長配列を表現するものなのでそこが少し違う。
っていうか配列との一番の違いはそこだ。


スライスへの要素の追加をする。

main.go

package main

import "fmt"

func main() {
        s := []int{1, 2, 3, 4, 5}

        s = append(s, 10)
        fmt.Println(s)

        s2 := append(s, 22, 33, 44)
        fmt.Println(s2)

        s3 := make([]int, 0, 10)
        s3 = append(s3, 111, 222)
        fmt.Println(s3)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
[1 2 3 4 5 10]
[1 2 3 4 5 10 22 33 44]
[111 222]


見たまんまなんだけど、注意しないといけないのは、append()は代入しないとエラーになる。
そこだけ。そこは不便な気がする。
append自体は末尾に追加。
s2へ代入しているのは複数を追加しているのがわかる。
また、s3については先ほどやった要素数の確保のやつで一応やってみた

と、スライスは↑を見てわかる通り、容量を超えた場合自動で容量を増やしてくれる。
故の可変長。


copy


名前の通り一括でコピーできる関数が用意されている。
とりあえず触ってみる。

main.go

package main

import "fmt"

func main() {
        s := []int{1, 2, 3, 4, 5}
        s2 := []int{22, 44}

        s3 := copy(s, s2)
        fmt.Println(s3)
        fmt.Println(s)

        s4 := make([]int, 3)
        copy(s4, []int{1, 2, 3})
        fmt.Println(s4)
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
2
[22 44 3 4 5]
[1 2 3]


s3の値が2になってるのは、copyが返却しているのはコピーした数(今回だとs2の要素数)を返すから。
その次に出しているのが実行後の結果。
また、appendと違って代入しなくていい。っていうのが最後のs4のところ。
返してる数字があれだからか。

copyはスライスに対してスライスの値をコピーする。
素数が足りなくても上書く。


完全スライス式


先ほど簡易スライス式の時に、pythonの[0:0:4]みたいな書き方の紹介を
少しだけしたけど、同じ書き方で違う使い方のものがあった。

簡単にいうと容量の指定。
というのも、簡易スライス式で出したスライスは、式の実行前に定義した状態の容量がそのままになってる。
それを変更するための三つ目の数字を与えることができる。

実際に見てみる。

main.go

package main

import "fmt"

func main() {
        s := []int{1, 2, 3, 4, 5}
        fmt.Println(len(s))
        fmt.Println(cap(s))

        s = append(s, 6, 7)
        fmt.Println(len(s))
        fmt.Println(cap(s))

        s2 := s[1:3]
        fmt.Println(len(s2))
        fmt.Println(cap(s2))

        s3 := s[1:3:4]
        fmt.Println(len(s3))
        fmt.Println(cap(s3))
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
5
5
------
7
10
------
2
9
------
2
3


ちょっと見づらいから-----って入れた。
先ほど使用したlencapで要素数と容量をそれぞれ確認している。
見てほしいのはs2s3の結果。
同じだけ抜き出してるけどcapの結果が違う。これはmaxで容量を調整しているため。
また、max自体は要素数の最初からどこまで、の数字であるので注意。

スクリプト言語だともう少し踏み込まないと意識しないメモリへの意識が早めにくるけど
こっちが本来のプログラミングだと思う。
実際現場出てると(人とプログラミングしてると)php書けるけど動きはわかりません、って人も多い。
もっと言えば読めるけど一から書けませんってのも超多いけど。
これが最近のウェブ業界未経験から育てます、の弊害な気もする。

まぁ俺もそういう会社で業界入ったんだけど。
ようはやる気の問題。
俺もまだまだわからないことだらけだけど。


ちょっと長くなってきたのでここまで。
次回はmapと、できたらchannelも。
前回interfaceもしたいって書いたけどもっと後になりそう。
参照型の後にfor文とかになりそう。

では次回。

次回
dddpga1.hateblo.jp


GoLangやるpart4

前回
dddpga1.hateblo.jp

前回の最後でちょろっと触れた関数について踏み込む。

無名関数


jsでよく見るあの形がある。

main.go

package main

import "fmt"

func main() {
        plus := func(x, y int) int { return x + y }
        res := plus(1, 2)
        fmt.Println(res)
}


関数名のない関数。
値みたいに引数にしたり変数に入れたりして扱うアレ。

[(゜-゜):daichi]@:sample$ go run main.go
3


関数を返す関数

main.go

package main

import "fmt"

func returnFunc() func() {
        return func() {
                fmt.Println("hoge")
        }
}

func main() {
        f := returnFunc()
        f()
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
hoge


func returnFunc() func() {...
返り値の型のfunc()がすっごいわかりづらいけどこれは他の関数と同じであくまで
返り値の型。func()(関数)を返すよって指定。


関数が引数の関数

main.go

package main

import "fmt"

func argFunc(f func()) {
        f()
}

func main() {
        argFunc(func() {
                fmt.Println("hoge")
        })
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
hoge


無名関数をargFuncに渡してあげてfに代入してargFuncで実行させる。
渡してる関数は表示だけの返り値の型がないので記述なし。


クロージャ

goの無名関数はクロージャ
クロージャとは→なぜクロージャ(Closure)と言うのか? - Qiita

例(本のほぼそのままだけどこれがわかりやすい。。。
main.go

package main

import "fmt"

func delay() func(string) string {
        var store string

        return func(next string) string {
                s := store
                store = next
                return s
        }
}

func main() {
        f := delay()

        fmt.Println(f("1"))
        fmt.Println(f("2"))
        fmt.Println(f("3"))
        fmt.Println(f("4"))
        fmt.Println(f("5"))
}


実行

[(゜-゜):daichi]@:sample$ go run main.go
    ←ここが空
1
2
3
4


結果として、一個ずつ遅れて表示されている。

func main内でdelay()を実行しているのは今まで通り。
では、delay()ではどうなってるか。

func delay() func(string) string {
        var store string

        return func(next string) string {
                s := store
                store = next
                return s
        }
}


delay()にはstringを引数にとり、string型の値を返す無名関数が指定されてる。
この書き方は返り値を指定できるあれ、だよね? あってる?
delay()の(無名関数の)local変数としてvar store stringを宣言。

返却しているのが

 return func(next string) string {
        s := store
        store = next
        return s
}


この部分がクロージャ。だよね?
通常、delay()のlocal変数であるstoreは一回ごとに破棄されるはず? だけど
このクロージャに参照され続けることによって値が保持され続けてる。
とのこと。

ここら辺なんとなくはわかってるんだど書き方から流れから細かく説明できない力不足。

で、このクロージャを使ってできるのがジェネレータ。
これはphp仕事にしてるんで流石にわかる。

main.go

package main

import "fmt"

func delay() func() int {
        i := 0

        return func() int {
                i += 1
                return i
        }
}

func main() {
        f := delay()

        fmt.Println(f())
        fmt.Println(f())
        fmt.Println(f())
        fmt.Println(f())
        fmt.Println(f())
}


先ほどのmain.goを少し書き換える。

[(゜-゜):daichi]@:sample$ go run main.go
1
2
3
4
5



とりあえず今日はここまで。
関数のlocal変数をクロージャ内で参照すると値を保持したままにできる。
ってことで。

次回は定数とinterfaceか、定数と参照型やりたい。それぞれ順番かも。

次回
dddpga1.hateblo.jp