ludwig125のブログ

頑張りすぎずに頑張る父

相関係数について

相関係数について

下記相関係数の導出までの説明

正の相関と負の相関

  • データ列1のように、年月日に対して値が増加している場合、これを「正の相関がある」という
  • データ列2のように、年月日に対して値が増加している場合、これを「負の相関がある」という
年月日 20140101 20140102 20140103 20140104 20140105 20140106 20140107 20140108 20140109 20140110
データ列1 正の相関 101 102 103 104 105 106 107 108 109 110
データ列2 負の相関 110 109 108 107 106 105 104 103 102 101

01

相関を数値で表す

参考:http://mcn-www.jwu.ac.jp/~yokamoto/ccc/stat/p22covcor/

年月日を変数x、相関関係を考えたい数値を変数yとする
変数xの平均をm_x、変数yの平均をm_yとする
変数の数をNとする
  • Σ(x-m_x)(y-m_y)/N を計算すると、xとyに正の相関があるか、負の相関があるか分かる ⇒ 上記をxとyの 共分散 という
  • 例えばデータ列1の例では、年月日をx、データ列1の数値をyとすると下記のような計算になる
xの平均 = (20140101 + 20140102 + ・・・ + 20140110)/10 = 20140105.5
yの平均 = (101 + 102 + ・・・ + 110)/10 = 105.5

xとyの共分散 = Σ(x-m_x)(y-m_y)/N
             = { (20140101-20140105.5) × (101-105.5) + ・・・ +(20140110-20140105.5) × (110-105.5) }/10 = 8.25

データ列1とデータ列2の共分散を計算すると下記の通り(エクセルではCOVAR関数を使用)

テータ列 相関 共分散
データ列1 正の相関 8.25
データ列2 負の相関 -8.25

共分散で相関係数を考える場合の問題点

  • 共分散は変数が等倍されるだけで値が変わってしまうため、
  • 複数の単位が異なったデータ列について相関の大きさを比較するには向いていない
  • データ列1やデータ列2の数値を100倍したものをそれぞれデータ列3、データ列4とする
年月日 20140101 20140102 20140103 20140104 20140105 20140106 20140107 20140108 20140109 20140110
データ列3 正の相関(数値が100倍) 10100 10200 10300 10400 10500 10600 10700 10800 10900 11000
データ列4 負の相関(数値が100倍) 11000 10900 10800 10700 10600 10500 10400 10300 10200 10100

02

  • データ列3とデータ列4の共分散
テータ列 相関 共分散
データ列3 正の相関(数値が100倍) 825
データ列4 負の相関(数値が100倍) -825

⇒ 数値が100倍だと共分散も100倍になってしまう

共分散を相関の大きさだと考えた時の問題点として データ列1、2の数値の単位がメートルだとしたとき、 それをセンチメートル単位で見たものをデータ列3、4とすると、 同じ数字を見ているにもかかわらず、2つの場合で相関の大きさが変化するということになってしまう

相関係数

  • 共分散を規格化して、単位をによらない無次元の量としたものを相関係数といい、下記の通り定義する
相関係数 = Σ(x-m_x)(y-m_y)/N ÷ (√(Σ(x-m_x)^2) ÷ (√(Σ(y-m_y)^2)
※相関係数はxとyの共分散をそれぞれの標準偏差で割ったもの

03

参考:http://mcn-www.jwu.ac.jp/~yokamoto/ccc/stat/p22covcor/

この相関係数でデータ列1~4を計算すると下記の通り(エクセルではCORREL関数を使用)

テータ列 相関 相関係数
データ列1 正の相関 1
データ列2 負の相関 -1
データ列3 正の相関(数値が100倍) 1
データ列4 負の相関(数値が100倍) -1

相関係数は単位によらない値になっている。また、最大値が1、最小値が-1となるよう規格化されている

数関係数=Rとした時の表

範囲 いえること
1.0≧|R|≧0.7 高い相関がある
0.7≧|R|≧0.5 かなり高い相関がある
0.5≧|R|≧0.4 中程度の相関がある
0.4≧|R|≧0.3 ある程度の相関がある
0.3≧|R|≧0.2 弱い相関がある
0.2≧|R|≧0.0 ほとんど相関がない

相関がない場合

下記のようなデータ列5は年月日と全く関係のない値をとっているため、相関係数は0に近くなる

年月日 20140101 20140102 20140103 20140104 20140105 20140106 20140107 20140108 20140109 20140110
データ列5 相関なし 101 12 390 -236 1 12 342 -80 100 120

04

テータ列 相関 相関係数
データ列5 相関なし 0.018052993

相関が発見できない場合(シンプソンのパラドックス)

相関係数は変数同士の比例、反比例の関係のみ判断できるので、 下記のデータ列6のように半分に区間を区切れば相関があるデータについて見過ごしてしまう危険性がある

年月日 20140101 20140102 20140103 20140104 20140105 20140106 20140107 20140108 20140109 20140110
データ列6 相関が発見できない 101 102 103 104 105 105 104 103 102 101

05

テータ列 相関 相関係数
データ列6 相関があるのに発見できない 0
集団を2つに分けた場合にある仮説が成立しても、集団全体では正反対の仮説が成立することがある
 ⇒ シンプソンのパラドックス

単純にデータを切り出してその相関係数のみを考えることは危険!

【付録】相関係数はcosθを意味する 下記のようなベクトルを定義すると相関係数はcosθと同じことがわかる

  • x = (x1-m_x),(x2-m_x)・・・(xN-m_x)
  • y = (y1-m_y),(y2-m_y)・・・(yN-m_y) (以下略)

pythonアルゴリズムなどメモ

内容

自分がよく使うpython3の書き方のまとめ 大体はpython2でも使えるはず(?)

Tips

文字列を受け取って整数にする

N = int(input())

文字列を受け取って整数の配列にする

A = list(map(int, input().split()))

またはリスト内包表記を使って A = [int(a) for a in input().split()]

文字列を受け取って整数の変数に個別に代入

以下のようにリストの左辺をカンマ区切りで別々の変数にすればそれぞれに格納される

N, A, B = [int(x) for x in input().split()]
print(N)
print(A)
print(B)
3 53 94 ← 入力値
3
53
94

a ~ bまでfor

0~3をfor

for x in range(4):
  print(x)
0
1
2
3

1から始めて4まで出したい場合は 「1, 4+1」を指定する

for x in range(1, 5):
  print(x)
1
2
3
4

listに対してfor

l=[1, 2, 3]
for i in l:
    print(i)

結果

1
2
3

数字の各桁をすべて足す

考えた方法1.

以下ではlist(map(int, list(str(n))) で、 いったん数字nを文字列にしてからそれをlistで一文字ずつ区切ったリストにして、それらをintに戻している

n=12345
summary = 0
for i in list(map(int, list(str(n)))):
   summary += i

summary : 15

考えた方法2.こちらのほうがわかりやすい
s = 0
while n > 0:
    s += n % 10
    n //= 10

s: 15

ファイル読み込み

以下をopen.pyの名前で保存

import sys

args = sys.argv
#print(args[1])
path = args[1]

with open(path) as f:
    print(f.read())

python open.py <対象ファイル>

go言語の並行処理

概要

「Go言語による並行処理」を読んだのでメモ ※この本に書いてないことも以下では取り上げている

github.com

goroutineとsync

goroutineの書き方

一番単純なgoroutine(これは期待した通り動かない)

package main

import "fmt"

func main() {
        hello := func() {
                fmt.Println("hello")
        }
        go hello()
}

無名関数helloを定義して、「go 関数()」で実行する しかしこれは実行しても何も出力されない

$go run ex1.go
$

その理由は、main goroutineとhello goroutineはそれぞれ別個に動くので、 mainが終わる前に子の処理のhelloを待たないため。

そこで、意図したとおりにするためには、syncで待ち合わせをすることが一番簡単な実現方法となる

syncで待ち合わせをする(期待通りの挙動になる)

syncパッケージを使って待ち合わせをする場合は以下のようにする

  • wg.Add(待ちたい処理の数)を定義
  • hello goroutineが終わる前に、このgoroutineの終了を伝えるwg.Doneをdeferで実行
  • hello goroutineが終わるまで、wg.Wait()で待つ
$cat ex2.go
package main

import (
        "fmt"
        "sync"
)

func main() {
        var wg sync.WaitGroup

        wg.Add(1)
        hello := func() {
                defer wg.Done()
                fmt.Println("hello")
        }
        go hello()

        wg.Wait()
}

これを実行すると以下の通り出力される

$go run ex2.go                                                          
hello
$  

syncで待ち合わせをする(helloを定義しない)

上の例はより簡単に、以下のようにも書ける

  • かっこの最後に 「 }()」がついていることに注意
package main

import (
        "fmt"
        "sync"
)

func main() {
        var wg sync.WaitGroup

        wg.Add(1)
        go func() {
                defer wg.Done()
                fmt.Println("hello")
        }()

        wg.Wait()
}

syncで待ち合わせをする(helloを通常の関数として定義する)

helloを通常の関数として定義する場合には以下のように、WaitGroupをポインタとして渡すことが必要になる

package main

import (
        "fmt"
        "sync"
)

func main() {
        var wg sync.WaitGroup

        wg.Add(1)
        go hello(&wg)

        wg.Wait()
}

func hello(wg *sync.WaitGroup) {
        defer wg.Done()
        fmt.Println("hello")
}

syncで複数の待ち合わせをする

waitNum := 3 と定義して、その回数分だけ待つ

package main

import (
        "fmt"
        "sync"
)

func main() {
        var wg sync.WaitGroup

        waitNum := 3
        wg.Add(waitNum)
        hello := func() {
                defer wg.Done()
                fmt.Println("hello")
        }

        for i := 0; i < waitNum; i++ {
                go hello()
        }

        wg.Wait()
}

結果

hello
hello
hello

goroutineに引数を渡す

通常の関数と同様に、goroutineにも引数を渡せる

$cat ex3-2.go
package main

import (
        "fmt"
        "sync"
)

func main() {
        var wg sync.WaitGroup

        waitNum := 3
        wg.Add(waitNum)
        hello := func(i int) {
                defer wg.Done()
                fmt.Printf("hello %d\n", i)
        }

        for i := 0; i < waitNum; i++ {
                go hello(i)
        }

        wg.Wait()
}

結果

$go run ex3-2.go             
hello 2
hello 0
hello 1

channel

最も簡単なchannel

一番簡単な例

  • channelは複数のgoroutine間でデータを共有するためにあるので、ふつうこんな使い方はしない
package main

import "fmt"

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

    // channelに5を送信
    ch <- 5

    // channelから整数値を受信
    i := <-ch
    fmt.Println(i)                     
}

実行結果

$go run chan.go
5

goroutineでのchannel

goroutineでchanelをやり取りする例

package main

import "fmt"

func main() {
    ch := make(chan int)                   
    go func() {
        // channelに5を送信
        ch <- 5
    }()

    // channelから5を受信
    fmt.Println(<-ch)
}

実行結果

5

複数のchannelを送受信することもできる

package main

import "fmt"

func main() {
    ch := make(chan int)
    n := 3
    go func() {
        for i := 0; i < n; i++ {
            ch <- i
        }
    }()

    for i := 0; i < n; i++ {
        fmt.Println(<-ch)
    }
}

実行結果

0
1
2

for rangeを使ったchannelの受信

for rangeを使ってchannelを受信する書き方もできる

ただし、for rangeを使って受信する場合、channelの終了がわからず永遠に待ち続けるので、goroutine側がこれ以上channelに送信しなくなった時点でchannelをcloseすることが必要

package main

import "fmt"

func main() {
    ch := make(chan int)
    n := 3
    go func() {
        defer close(ch) // channelの終了を送信
        for i := 0; i < n; i++ {
            ch <- i
        }
    }()

    for i := range ch {
        fmt.Println(i)
    }
}

実行結果

0
1
2

deferを忘れると、待ち続けてdeadlockになる

deferをつけない場合の実行結果

0
1
2
fatal error: all goroutines are asleep - deadlock!
channelのcloseを確認する

channelからは、2番目の返り値としてchannelがclose済みかそうでないかを表すbool値が取得できる 慣習的にこの2番目の返り値はokとする

上の例で言えば、chから得られた値がi、chがclose済みかどうかがok(closeされていればfalse)となる。このokはとっても取らなくてもいい i, ok := <-ch

参考 - A Tour of Go

上の例を、okがfalseとなるまでchの中身を出力し続けるようにした場合、以下のようになる

  • ※上のfor rangeを使った書き方より冗長だしミスると無限ループになるからめったに使う必要ないと思う
package main

import "fmt"

func main() {
    ch := make(chan int)
    n := 3
    go func() {
        defer close(ch) // channelの終了を送信
        for i := 0; i < n; i++ {
            ch <- i
        }
    }()

    for {
        i, ok := <-ch
        if !ok {
            fmt.Println("ch closed")
            return // このreturnを忘れると無限ループになる
        }
        fmt.Println(i)
    }
}

実行結果

0
1
2
ch closed

bufferなしchannel

bufferなしchannelとbufferつきchannelの挙動の違いについてシンプルにr例示している資料が意外とすぐに見つからなかった

まずbufferなしchannelの例

package main

import (
        "fmt"
        "time"
)

func main() {
        ch := make(chan int)
        go func() {
                defer close(ch)      
                defer fmt.Println("Producer Done")
                for i := 0; i < 5; i++ {
                        ch <- i
                        fmt.Printf("Sending: %d\n", i)
                }
        }()
    
        for i := range ch {
                fmt.Printf("Received %v\n", i)
                time.Sleep(1 * time.Second)
        }
       
}

実行結果

Sending: 0
Received 0
Received 1
Sending: 1
Received 2
Sending: 2
Received 3
Sending: 3
Received 4
Sending: 4
Producer Done
  • 1秒おきにSendingとReceivedが交互に出力されている

channelが満杯のときはchannelへの書き込みは(読み込みされるまで)ブロックされるので、bufferなしchannelは毎回読み込みをした後で追加の書き込みをしている

bufferつきchannel

bufferつきchannelは一度に送信できる件数を指定できる

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int, 4)
    go func() {
        defer close(ch)
        defer fmt.Println("Producer Done")
        for i := 0; i < 5; i++ {
            ch <- i
            fmt.Printf("Sending: %d\n", i)
        }
    }()

    for i := range ch {
        fmt.Printf("Received %v\n", i)
        time.Sleep(1 * time.Second)
    }

}

実行結果

Sending: 0
Sending: 1
Sending: 2
Sending: 3
Sending: 4
Producer Done
Received 0
Received 1
Received 2
Received 3
Received 4
  • buffer4に指定した場合、channelに対して先に4つ書き込みが終了し、あとからReceivedされていることがわかる
  • このように、bufferつきchannelは読み込みを待つことなく何件まで書き込めるかを指定できる

select

selectは複数のcaseに対して、それぞれの条件が等しく選択されるように、疑似乱数を使って配分している。

例えば、以下のようにc1, c2という2つのチャネルから合計1000回読み込むとする。

  • c1からのチャネルから読み込んだらc1Countを増やす
  • c2からのチャネルから読み込んだらc2Countを増やす
package main

import (
        "fmt"
)

func main() {
        c1 := make(chan interface{})
        close(c1)
        c2 := make(chan interface{})
        close(c2)
        var c1Count, c2Count int
        for i := 1000; i >= 0; i-- {
                select {
                case <-c1:
                        c1Count++
                case <-c2:
                        c2Count++
                }
        }

        fmt.Printf("c1Count: %d, c2Count: %d.\n", c1Count, c2Count)
}

このコードを何回も実行した結果を見てみると、c1, c2がほぼ同じ回数ずつカウントされていることがわかる

$go run select2.go
c1Count: 499, c2Count: 502.
$go run select2.go
c1Count: 498, c2Count: 503.
$go run select2.go
c1Count: 520, c2Count: 481.
$go run select2.go
c1Count: 513, c2Count: 488.
$go run select2.go
c1Count: 505, c2Count: 496.
$go run select2.go
c1Count: 502, c2Count: 499.
$go run select2.go
c1Count: 509, c2Count: 492.
$ 

for-selectでdefault処理

参考:Go by Example: Select

以下のように、チャネルを待っている間やりたい処理をdefaultに書くことでgoroutineの結果を待っている間他の処理ができる

for {
    select {
    case <-チャネル:
        <チャネルを受け取ったあとの処理>
    case <-チャネル2:
        <チャネル2を受け取ったあとの処理>
    default:
    }
    <チャネル1とチャネル2を受け取る前に行いたい処理>
}

for で無限ループをしながらselectでdoneが来るのを待ち続ける例

goroutineが5秒後にdoneにcloseを送るので、処理が終わる

それまではWaitを繰り返す

package main

import (
    "fmt"
    "time"
)

func main() {

    done := make(chan interface{})
    go func() {
        defer close(done)
        fmt.Println("Goroutine start")
        time.Sleep(5 * time.Second)
        fmt.Println("Goroutine end. send done")
    }()

    i := 0
    for {
        select {
        case <-done:
            fmt.Println("Receive done")
            return
        default:
        }

        i++
        fmt.Printf("Wait %d\n", i)
        time.Sleep(1 * time.Second)
    }
}

これを実行すると以下のようになる

$go run sample.go
Wait 1
Goroutine start
Wait 2
Wait 3
Wait 4
Wait 5
Goroutine end. send done
Receive done

ちなみに、以下のようにdefautlとselectの終端の}の間に処理を入れても同様の結果になるが、この処理が往々にして長くなることがあってselectの終端がわかりにくくなるので、自分はあまり好んで使う人はいないようだ

 default:
        <チャネル1とチャネル2を受け取る前に行いたい処理>
    }

timeout

Go by Exampleに書いてあるtimeoutの方法

Go by Example: Timeouts に書いてある方法

time.After(タイムアウトする時間) を設定してselect文で受け取ることで、時間がかかるgoroutineのタイムアウト処理ができる

以下の例では、 - 2秒かかるgoroutineの結果c1が1秒のtimeout制限に引っかかってtimeout 1が出力され、 - 同じく2秒かかるgoroutineの結果c2が3秒のtimeout制限以内に終わるので、result 2が出力される

package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c1 <- "result 1"
    }()
    select {
    case res := <-c1:
        fmt.Println(res)
    case <-time.After(1 * time.Second):
        fmt.Println("timeout 1")
    }

    c2 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "result 2"
    }()
    select {
    case res := <-c2:
        fmt.Println(res)
    case <-time.After(3 * time.Second):
        fmt.Println("timeout 2")
    }
}

実行結果

timeout 1
result 2

上のtimeout方法の問題点

上の方法には一つ問題があって、timeout 1と出てもgoroutineに切り出した処理は自動で止まらないということがある

ためしに以下のようにc1で受け取っていた部分を以下のようにtaskというgoroutineにして、task関数の中では0から999999まで数字を出力し続けるようにしてみる - また、最初と最後の時間差をとって、処理時間を計測してみる (結果は長くなるので注意)

package main

import (
    "fmt"
    "log"
    "time"
)

func main() {
    start := time.Now()
    select {
    case res := <-task():
        fmt.Println(res)
    case <-time.After(1 * time.Second):
        fmt.Println("timeout 1")
    }
    log.Printf("Total time: %fs", time.Since(start).Seconds())
}

func task() <-chan string {
    res := make(chan string)
    go func() {
        defer close(res)
        for i := 0; i < 1000000; i++ {
            fmt.Println(i)
            time.Sleep(1 * time.Nanosecond)
        }
        res <- "done!"
    }()
    return res
}

The Go Playground

実行結果

0
1
2
(省略)
475135
475136
475137
timeout 1
2019/09/29 06:11:19 Total time: 12.296678s

おかしなことが起きた! 実行結果はマシンの性能にもよると思うが、「timeout 1」まで出した後でtimeoutが出力されて、「 Total time: 12.296678s」となっている。

しかも恐ろしいことに2回目に実行すると、今度は「timeout 1」すら出ずに12秒かかっている。

489587
489588
489589
2019/09/29 06:13:37 Total time: 12.209647s

3回目もまた結果が変わる

489490
489491
489492
2019/09/29 06:14:39 Total time: 12.120974s

timeoutとなっても、すでに起動してfmt.Println(i) を出力し続けるgoroutineは勝手に暴走して、それが終わるタイミングは全然予測できていない

これをtimeout時間の通り1秒で終了させたい場合は以下のようにcontextやdoneチャネルを使うといい。

timeoutでcontext cancelを行う場合

上のコードをcontextを用いて書き直したものが以下となる。

timeout時刻になるとcontextのcancel処理が飛んで、1秒で処理が終わっていることがわかる

package main

import (
    "context"
    "fmt"
    "log"
    "time"
)

func main() {
    start := time.Now()
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    res := task(ctx)
    select {
    case <-res:
        fmt.Println(res)
    case <-time.After(1 * time.Second):
        log.Println("timeout after 1s")
        cancel()
        log.Println(<-res)
    }
    log.Printf("Total time: %fs", time.Since(start).Seconds())
}

func task(ctx context.Context) <-chan string {
    res := make(chan string)
    go func() {
        defer close(res)
        for i := 0; i < 1000000; i++ {
            select {
            case <-ctx.Done():
                log.Printf("context cancelled. count: %d", i)
                res <- fmt.Sprintf("error: %s", ctx.Err())
                return
            default:
            }
            fmt.Println(i)
            time.Sleep(1 * time.Nanosecond)
        }
        res <- "done!"
    }()
    return res
}

The Go Playground

実行結果

(省略)
38503
38504
38505
38506
38507
2019/09/29 06:30:35 timeout after 1s
2019/09/29 06:30:35 context cancelled. count: 37841
2019/09/29 06:30:35 error: context canceled
2019/09/29 06:30:35 Total time: 1.000472s

contextの使い方は公式は以下。ほかにもネット上にたくさんあるので、以下で簡単な説明だけ記載しておく。 Go Concurrency Patterns: Context - The Go Blog

簡単に説明
  • コードの最初でcancel 機能付きのcontextを宣言する
  • 「defer cancel()」によって、プログラム終了時に確実にすべてのcontextがキャンセルされるようにする
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
  • task関数にこのcontextを渡して(第一引数として渡すのがセオリー)、for 文の1ループごとに、「ctx.Done()」が来ていないかチェックしている
  • このプログラムでは 「ctx.Done()」を受け取ったらlogに「context cancelled. count」とその時のループ番号を出力している
  • さらにresにエラー原因として「 ctx.Err()」を返してreturnでgoroutineを終える
  • このreturnがないとDoneを受け取ってもgoroutineが終わらないので必要
for i := 0; i < 1000000; i++ {
    select {
    case <-ctx.Done():
        log.Printf("context cancelled. count: %d", i)
        res <- fmt.Sprintf("error: %s", ctx.Err())
        return
    default:
    }
    fmt.Println(i)
    time.Sleep(1 * time.Nanosecond)
}
  • 呼び出し側のmain内では、「res := task(ctx)」としてtaskの戻り値(channel)をいったんres変数に定義
  • time.Afterの制限時間内に処理が終われば、「case <-res」に入る
  • time.Afterの制限時間を過ぎたら、「timeout after 1s」をlogに表示させて、contextのcancel関数を呼び出す(重要)
  • 「log.Println(<-res)」には、task関数内で詰めたctx.Err()の内容が出力される「error: context canceled」
    • 「<-res」とすることで、cancelをした後のtask関数が返すエラーをきちんと受け取ってから終わるように待ち合わせをしている
res := task(ctx)
select {
case <-res:
    fmt.Println(res)
case <-time.After(1 * time.Second):
    log.Println("timeout after 1s")
    cancel()
    log.Println(<-res)
}

contextを使うことで、goroutineを安全に終了させることができる

pipeline、fan-in、fan-out

go言語のpipeline、fan-in、fan-out - ludwig125のブログ

複数のgoroutineのエラーハンドリングをする

ludwig125.hatenablog.com

同時並列数を制御する

ludwig125.hatenablog.com

シグナル処理

ludwig125.hatenablog.com

GoogleAppEngineでgoroutineを使う

GoogleAppEngineでgoroutineを使った例 ludwig125.hatenablog.com

vmwareで「パワーオン中にエラーが発生しました」が出た(Windows10)

起きたこと

Windows10 で、 VMware Playerで以下のエラーが発生して起動しない

パワーオン中にエラーが発生しました:Transport(VMDB)error -44 Message. The VMware Authorization Service is not running.

f:id:ludwig125:20190303074339p:plain
vmware

やったこと

Windows10の場合、左下のWindowsボタンから「PC」を検索 f:id:ludwig125:20190303074543p:plain

PCを右クリックして「管理」を開く f:id:ludwig125:20190303074844p:plain

「サービスとアプリケーション」を選択 f:id:ludwig125:20190303074952p:plain

「サービス」を選択 f:id:ludwig125:20190303075054p:plain

VMware Authorization Service」をダブルクリック f:id:ludwig125:20190303080041p:plain

「サービスの状態」の「開始」をクリック f:id:ludwig125:20190303075323p:plain

OKを押して閉じると「VMware Authorization Service」が「実行中」になっていることが確認できる f:id:ludwig125:20190303080227p:plain

go言語で呼び出す関数を動的に変更する

package main

import (
    "fmt"
)

func main() {

    funcList := []func(){function1, function2, function3}

    for i:=0; i < 3; i++{
    funcList[i]()
    }
}

func function1() {
    fmt.Println("print function1")
}

func function2() {
    fmt.Println("print function2")
}

func function3() {
    fmt.Println("print function3")
}

実行結果

print function1
print function2
print function3

実行例 https://play.golang.org/p/MQnTJWIu7P1

参考にしたページ C# - 関数名call1, call2, call3...の連続実行をまとめる方法はありますか?|teratail

応用

上のを応用して自前のretry関数の挙動を確かめたもの

スプレッドシートへの書き込みを3回失敗するまでretryする関数を作りたかった

https://play.golang.org/p/HV0uqiVphap

以下は、3回失敗するまでretryをする関数とその実行例

  • test1は、3回とも失敗するのでinsertedは0
  • test1は、3回目で成功するのでinsertedは10
package main

import (
    "errors"
    "fmt"
    "time"
)

type Resp struct {
    status int
}

func main() {

    target, inserted := retry([]func()(Resp, error){returnStatus1, returnStatus2, returnStatus3})
    fmt.Printf("test1. target %d, inserted: %d", target, inserted)
    
    fmt.Printf("\n----------------------\n")
    
    target, inserted = retry([]func()(Resp, error){returnStatus1, returnStatus2, returnStatus4})
    fmt.Printf("test2. target %d, inserted: %d", target, inserted)
}

func retry(returnStatusList []func()(Resp, error)) (int, int) {
    target_num := 10
    fmt.Printf("insert target num: %v\n", target_num)

    var MaxRetries = 3

    for attempt := 0; attempt < MaxRetries; attempt++ {
        resp, err := returnStatusList[attempt]()
        if err != nil {
            fmt.Printf("Unable to write value. %v. attempt: %d\n", err, attempt+1)
            time.Sleep(3 * time.Second) // 3秒待つ
            continue
        }
        status := resp.status
        if status != 200 {
            fmt.Printf("HTTPstatus error. %v. attempt: %d\n", status, attempt+1)
            time.Sleep(3 * time.Second) // 3秒待つ
            continue
        }

        // 書き込み対象の件数と成功した件数
        fmt.Printf("succeded to write value.\n")
        return target_num, target_num
    }
    fmt.Printf("Failed to write data to sheet. reached to MaxRetries: %d\n", MaxRetries )
    // 書き込み対象の件数と成功した件数
    return target_num, 0
}

func returnStatus1() (Resp, error) {
    resp := Resp{}
    resp.status = 200
    err := errors.New("error! happend")
    return resp, err
}

func returnStatus2() (Resp, error) {
    resp := Resp{}
    resp.status = 404
    return resp, nil
}

func returnStatus3() (Resp, error) {
    resp := Resp{}
    resp.status = 201
    return resp, nil
}

func returnStatus4() (Resp, error) {
    resp := Resp{}
    resp.status = 200
    return resp, nil
}

結果

insert target num: 10
Unable to write value. error! happend. attempt: 1
HTTPstatus error. 404. attempt: 2
HTTPstatus error. 201. attempt: 3
Failed to write data to sheet. reached to MaxRetries: 3
test1. target 10, inserted: 0
----------------------
insert target num: 10
Unable to write value. error! happend. attempt: 1
HTTPstatus error. 404. attempt: 2
succeded to write value.
test2. target 10, inserted: 10

少し書き換え

上のはretryの中で回数を指定しているのが気に食わなかったのでちょっと修正。 getReturnStatusというラップ関数を用意

https://play.golang.org/p/hQK-TunSAWE

package main

import (
    "errors"
    "fmt"
    "time"
)

type Resp struct {
    status int
}

func main() {

    target, inserted := retry()
    fmt.Printf("test1. target %d, inserted: %d", target, inserted)
}

func retry() (int, int) {
    target_num := 10
    fmt.Printf("insert target num: %v\n", target_num)

    var MaxRetries = 3

    for attempt := 0; attempt < MaxRetries; attempt++ {
        resp, err := getReturnStatus()
        if err != nil {
            fmt.Printf("Unable to write value. %v. attempt: %d\n", err, attempt+1)
            time.Sleep(3 * time.Second) // 3秒待つ
            continue
        }
        status := resp.status
        if status != 200 {
            fmt.Printf("HTTPstatus error. %v. attempt: %d\n", status, attempt+1)
            time.Sleep(3 * time.Second) // 3秒待つ
            continue
        }

        // 書き込み対象の件数と成功した件数
        fmt.Printf("succeded to write value.\n")
        return target_num, target_num
    }
    fmt.Printf("Failed to write data to sheet. reached to MaxRetries: %d\n", MaxRetries )
    // 書き込み対象の件数と成功した件数
    return target_num, 0
}

var count = 0

func getReturnStatus() (Resp, error) {
    funcList := []func()(Resp, error){returnStatus1, returnStatus2, returnStatus3}
    count++
    return funcList[count-1]()
}

func returnStatus1() (Resp, error) {
    resp := Resp{}
    resp.status = 200
    err := errors.New("error! happend")
    return resp, err
}

func returnStatus2() (Resp, error) {
    resp := Resp{}
    resp.status = 404
    return resp, nil
}

func returnStatus3() (Resp, error) {
    resp := Resp{}
    resp.status = 201
    return resp, nil
}

func returnStatus4() (Resp, error) {
    resp := Resp{}
    resp.status = 200
    return resp, nil
}

GoogleAppEngine(GAE)メモ

Tips

githubに上げたくない環境変数をどうやって管理するか

githubに上げたくない環境変数をどうやって管理するか

以下を参考に、別のyamlをincludesして、秘密にしたいinclude先のファイルは.gitignoreに入れてgithubには上げないようにする

app.yaml リファレンス  |  Python の App Engine スタンダード環境  |  Google Cloud

こんな感じ app.yamlの中身

env_variables:
  HOGE: "hoge"
  FUGA: "fuga"

includes:                                                                                        
- secret_piyo.yaml

secret_piyo.yamlの中身

env_variables:
  PIYO: "piyo"

secretの方はgitignoreする

$cat .gitignore      
secret_piyo.yaml

トラブル対応

INVALID_ARGUMENT: Your app may not have more than 15 versions.

$gcloud app deploy app.yaml
略
ERROR: (gcloud.app.deploy) INVALID_ARGUMENT: Your app may not have more than 15 versions. Please delete one of the existing versions before trying to create a new version.

こちらが大変参考になった 😅&lt; golang+gaeにて(gcloud.app.deploy) INVALID_ARGUMENT: Your app may not have more than 15 versions. Please delete one of the existing versions before trying to create a new version.が出た場合。 - Qiita

対応方法

$ gcloud app versions list

でバージョンのリストを表示

こんな感じに表示されるので、不要なVERSIONを選んで消す

$ gcloud app versions list
SERVICE  VERSION          TRAFFIC_SPLIT  LAST_DEPLOYED              SERVING_STATUS
default  20180811t000507  0.00           2018-08-11T00:05:29+09:00  SERVING
default  20180812t234454  0.00           2018-08-12T23:47:48+09:00  STOPPED
default  20180814t002149  0.00           2018-08-14T00:22:47+09:00  STOPPED
default  20180814t004044  0.00           2018-08-14T00:41:48+09:00  STOPPED

上の2列目がバージョンID

以下のように消す

$gcloud app versions delete  20190224t071430 20190226t055739 ...以下複数まとめて消せる

deleteには結構時間がかかる

go言語アルゴリズムやTipsなどメモ

内容

go言語で自分がよく使う書き方やTipsのまとめ

並行処理関係は以下

ludwig125.hatenablog.com

BenchmarkTestは以下

ludwig125.hatenablog.com

Tips

入力値

入力値を受け取る

文字列で受け取る

var s string
fmt.Scan(&s)

整数で受け取る

var N int
fmt.Scan(&N)

複数の入力をまとめて受け取ることもできる

var a, b, c int
fmt.Scan(&a, &b, &c)

空白区切りの入力値を受け取ってスライスにする

N文字の空白区切りの入力された数値をスライスにする

  • fmt.Scanは、改行もしくは空白を区切ってしまうので、N回ループする
l := make([]int, N)
for i := range l {
    fmt.Scan(&l[i])
}
20 24 59  ← 入力値
[20 24 59]

空白区切りの入力値を一行受け取ってスライスにする

参考: Goで標準入力から文字列や数値列を取得する - Qiita

package main

import (
    "bufio"
    "fmt"
    "os"                             
)

func main() {
    s := bufio.NewScanner(os.Stdin)
    s.Scan()
    input := s.Text()
    l := strings.Split(input, " ")
    fmt.Println(l)
}
3 42 25  ← 入力
[3 42 25]  ← 全部stringなので注意

全部intのスライスにしたい場合はこんな感じかな

  • この方法よりは上記のfmt.Scanを使ったほうがよっぼど簡単だと思う
package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
    "strings"
)

func main() {
    s := bufio.NewScanner(os.Stdin)
    s.Scan()                                             
    input := s.Text()
    l := strings.Split(input, " ")

    toIntL := func(l []string) []int {
        var intL []int
        for _, v := range l {
            i, _ := strconv.Atoi(v)
            intL = append(intL, i)
        }
        return intL
    }
    fmt.Println(toIntL(l))
}

実行結果

431 12 64 ←入力値
[431 12 64]

整数の数列をソート

package main

import (
    "fmt"
    "sort"
)

func main() {
    a := []int{10, 3, 4, 1}
    
    A := a
    sort.Ints(A)
    fmt.Println(A)
    
    B := a
    fmt.Println(sort.IntSlice(B))  // 上と同じ出力

    C := a
    sort.Sort(sort.IntSlice(C))
    fmt.Println(C)    // 上と同じ出力

    D := a
    sort.Sort(sort.Reverse(sort.IntSlice(D)))
    fmt.Println(D)  // 逆順に出力
}

結果 [1 3 4 10] [1 3 4 10] [1 3 4 10] [10 4 3 1]

文字列を辞書順にソート

いったんスライスにしてソートしてからjoinで戻す方法

参考

package main

import (
    "fmt"
    "sort"
    "strings"
)

func main() {
    var s string
    fmt.Scan(&s)
    sl := strings.Split(s, "")
    sort.Strings(sl)
    sj := strings.Join(sl, "")

    fmt.Println(sj)                                  
}

実行

gasdbfgaa ← 入力
aaabdfggs ← 出力

逆順にソートしたいときは以下を使う

  • sort.Sort(sort.Reverse(sort.StringSlice(s)))

関数化したもの

複数条件で優先順位をつけたソート

安定ソートを数回するといい - 安定ソート(SliceStable)を使うことで、最初の並びをなるべくそのままにしつつ並べ替える

参考 - GoのSliceをSortする(sort.Sliceとsort.SliceStable) - Qiita

package main

import (
    "fmt"
    "sort"
)

type Person struct {
    ID   int
    Name string
}

func main() {
    group := []Person{
        {ID: 2, Name: "A"},
        {ID: 1, Name: "A"},
        {ID: 3, Name: "A"},
        {ID: 2, Name: "C"},
        {ID: 1, Name: "B"},
        {ID: 1, Name: "C"},
        {ID: 3, Name: "C"},
        {ID: 3, Name: "B"},
        {ID: 2, Name: "B"},
    }

    // groupをgroup1, group2に代入してそれぞれの変化を見る

    group1 := group
    // 最初にID順にソート
    sort.SliceStable(group1, func(i, j int) bool { return group1[i].ID < group1[j].ID })
    // 次にName順にソート。この時、Nameが同じであれば上でソートされたID順の並びを崩さない(安定ソート)
    sort.SliceStable(group1, func(i, j int) bool { return group1[i].Name < group1[j].Name })
    fmt.Printf("IDで安定SortしてからNameで安定ソート:%+v\n", group1)

    group2 := group
    // 最初にName順にソート
    sort.SliceStable(group2, func(i, j int) bool { return group2[i].Name < group2[j].Name })
    // 次にID順にソート。この時、IDが同じであれば上でソートされたName順の並びを崩さない(安定ソート)
    sort.SliceStable(group2, func(i, j int) bool { return group2[i].ID < group2[j].ID })
    fmt.Printf("Nameで安定SortしてからIDで安定ソート:%+v\n", group2)
}

The Go Playground

実行結果

IDで安定SortしてからNameで安定ソート:[{ID:1 Name:A} {ID:2 Name:A} {ID:3 Name:A} {ID:1 Name:B} {ID:2 Name:B} {ID:3 Name:B} {ID:1 Name:C} {ID:2 Name:C} {ID:3 Name:C}]
Nameで安定SortしてからIDで安定ソート:[{ID:1 Name:A} {ID:1 Name:B} {ID:1 Name:C} {ID:2 Name:A} {ID:2 Name:B} {ID:2 Name:C} {ID:3 Name:A} {ID:3 Name:B} {ID:3 Name:C}]

数字の各桁の数値をすべて足す

方法1.
sum := 0
for n > 0 {
        sum += n % 10
        n /= 10
}

⇒ n が 12345なら sは15

方法2.
sum := 0
for i := n; i > 0; i /= 10 {
        sum += i % 10
}

ポインタ

変数のポインタ

package main

import (
    "fmt"
)

func main() {   
    t := 10
    fmt.Println(t)
    change := func(t *int) {
        *t = 11
    }
    change(&t)
    fmt.Println(t)
}

実行結果

10
11

グリッド

二次元配列を組み立て

package main

import (
    "fmt"
    "strings"
)

func main() {
    var h, w int
    fmt.Scan(&h, &w) // high, width

    var p [][]string
    for i := 0; i < h; i++ {
        var l string
        fmt.Scan(&l)
        p = append(p, strings.Split(l, ""))
    }                                                          
    fmt.Println(p)
    fmt.Println(p[0][1])

}

実行結果

3 5
12345
67891
23456 
[[1 2 3 4 5] [6 7 8 9 1] [2 3 4 5 6]] ← 出力
2 ← 出力

行列計算

n * m の二次元配列を入力から受け取り

 var n, m int
    fmt.Scan(&n)
    fmt.Scan(&m)

    a := make([][]int, n)
    for i := 0; i < n; i++ {
        a[i] = make([]int, m)
        for j := 0; j < m; j++ {
            fmt.Scan(&a[i][j])
        }
    }

n * mの行列Aと m * l の行列Bの積としてn * lの行列Cを作成

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_7_D

 var n, m, l int
    fmt.Scan(&n)
    fmt.Scan(&m)
    fmt.Scan(&l)

    a := make([][]int, n)
    for i := 0; i < n; i++ {
        a[i] = make([]int, m)
        for j := 0; j < m; j++ {
            fmt.Scan(&a[i][j])
        }
    }
    b := make([][]int, m)
    for i := 0; i < m; i++ {
        b[i] = make([]int, l)
        for j := 0; j < l; j++ {
            fmt.Scan(&b[i][j])
        }
    }
    c := make([][]int, n)
    for i := 0; i < n; i++ {
        c[i] = make([]int, l)
    }

    for k := 0; k < n; k++ {
        for j := 0; j < l; j++ {
            for i := 0; i < m; i++ {
                c[k][j] += a[k][i] * b[i][j]
            }
        }
    }
    fmt.Println(c)

構造体とメソッド

構造体

こんな感じにstructを宣言して初期化できる

package main

import "fmt"

type Person struct {
    name   string
    age    int
    weight float64
}

func main() {
    person1 := Person{name: "Taro", age: 21, weight: 60.12} // フィールド名を指定するとわかりやすい
    fmt.Println(person1)
    person2 := Person{"Akira", 22, 61.12} // 順番通りなら指定しなくてもいい
    fmt.Println(person2)
}

実行結果

{Taro 21 60.12}
{Akira 22 61.12}

メソッド

goにはクラスはないが、構造体にメソッドを追加することでクラスメソッドのように使える

上で定義したPerson構造体の要素をすべてstringにしたスライスを返すメソッドを追加した例

func (p *Person) toString() []string {
    var str []string
    str = append(str, fmt.Sprintf("%s", p.name))
    str = append(str, fmt.Sprintf("%d", p.age))
    str = append(str, fmt.Sprintf("%f", p.weight))
    return str
}

func main() {
    person1 := Person{name: "Taro", age: 21, weight: 60.12}
    fmt.Println(person1.toString())
}

実行結果

[Taro 21 60.120000]

例2

Person構造体のスライスPersonsを定義して、要素をすべてinterfaceの二次元配列にして返す関数を使ってみた例

type Person struct {
    name   string
    age    int
    weight float64
}

type Persons []Person

func (p *Person) toString() []string {
    var str []string
    str = append(str, fmt.Sprintf("%s", p.name))
    str = append(str, fmt.Sprintf("%d", p.age))
    str = append(str, fmt.Sprintf("%f", p.weight))
    return str
}

func (p *Person) toInterfaceSlice() []interface{} {
    var is []interface{}
    is = append(is, p.name)
    is = append(is, p.age)
    is = append(is, p.weight)
    return is
}

func (ps *Persons) toInterfaceSlices() [][]interface{} {
    var iss [][]interface{}
    for _, p := range *ps {
        iss = append(iss, p.toInterfaceSlice())
    }
    return iss
}

func main() {
    person1 := Person{name: "Taro", age: 21, weight: 60.12}
    fmt.Printf("要素が全部stringのスライスに変換 %v\n", person1.toString())
    fmt.Printf("要素が全部interfaceのスライスに変換 %v\n", person1.toInterfaceSlice())

    persons := Persons{}
    persons = append(persons, person1)

    person2 := Person{name: "Akira", age: 22, weight: 61.12}
    persons = append(persons, person2)
    fmt.Printf("要素が全部interfaceの二次元スライスに変換 %v\n", persons.toInterfaceSlices())
}

実行結果

要素が全部stringのスライスに変換 [Taro 21 60.120000]
要素が全部interfaceのスライスに変換 [Taro 21 60.12]
要素が全部interfaceの二次元スライスに変換 [[Taro 21 60.12] [Akira 22 61.12]]

コード https://play.golang.org/p/FeJx-_wXv68

Enum

参考

4 iota enum examples · YourBasic Go

Go言語 - enumを定義する - 覚えたら書く

エラーハンドリング

参考

【go】golangのエラー処理メモ - ②. 例外はないがエラーハンドリングはできるよ(インスタンスや型でハンドリング) - tweeeetyのぶろぐ的めも

エラー・ハンドリングについて(追記あり) — プログラミング言語 Go | text.Baldanders.info

Golangのエラー処理とpkg/errors | SOTA

Go言語のエラーハンドリングについて - Qiita

Errors are values - The Go Blog ← 同じ関数を複数回呼ぶ時のエラーをまとめて扱う

reflect

こちらにまとめた ludwig125.hatenablog.com

アルゴリズム

全N個のものをM個単位で処理する

以下のようなlistを指定の件数ずつ分割して処理したいとき

package main

import (
    "fmt"
)

func main() {
    l := []int{1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10}

    for _, i := range l {
        fmt.Println(i)
    }

}
// 結果
/*
1
2
3
4
5
6
6
7
8
9
10
*/

以下のようにしてみた

package main

import (
    "fmt"
)

func main() {

    l := []int{1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10}

    chunkSize := 3
    for start := 0; start < len(l); start += chunkSize {
        
        end := start + chunkSize
        if end > len(l) {
            end = len(l)
        }
        fmt.Println(l[start:end])
        fmt.Println("---")
    }
}

// 結果
/* 
[1 2 3]
---
[4 5 6]
---
[6 7 8]
---
[9 10]
---
*/