ludwig125のブログ

頑張りすぎずに頑張る父

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]
---
*/

Ubuntuインストール後の設定メモ

前提

Windows7にUbuntuをインストールする手順メモ - ludwig125のブログ

Ubuntuでデスクトップのディレクトリ名を「Desktop」にする

参考: Ubuntuでデスクトップのディレクトリ名を「Desktop」にする - ぬいぐるみライフ?

端末エミュレータを開き,以下のコマンドを実行する.

$ LANG=C xdg-user-dirs-gtk-update
ダイアログが表示されるので,「Don't ask me this again」をチェックし,「Update Names」をクリックすればOK.

すでに「デスクトップ」の中にファイルを作っている場合、上記のコマンドで日本語の「デスクトップ」ディレクトリが残ったまま「Desktop」が作られる

ショートカットキー

UbuntuTips/Desktop/KeyboardShortcutOnUnity - Ubuntu Japanese Wiki

Ubuntu 16.04 LTSで使えるキーボードショートカット一覧 - 旧ID:itiriのブログ

端末

端末のショートカットキー

コピーアンドペースト 端末の起動 Ctrl+Shift+n

コピー Ctrl+Shift+c

ペースト Ctrl+Shift+v

 .bash_profile

参考: ユーザーの環境変数を設定するbashの設定ファイルと、カスタムプロンプトについて | OXY NOTES Linuxユーザのためのチップス: .bash_profileで環境変数を設定する。

[~/Documents ] $ cat ~/.bash_profile 
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

# User specific environment and startup programs

ENV=$HOME/.bashrc
USERNAME="xxxx"
PATH=$PATH:"/$HOME/bin"

export USERNAME ENV PATH

PS1="[\w ] $ "
[~/Documents ] $

Ubuntu14で.bash_profileが認識されない 正しくは .profile

Ubuntu14.04で.bash_profileが読み込まれない(見つからない) - Qiita

bash - I cannot find .bash_profile in ubuntu - Ask Ubuntu

上記に.bash_profileの設定を書いたが、起動時に.pash_profileに書いた設定が読み込まれていないようだったので調査

ubuntu14では .profileが認識されているらしい

Ubuntu起動と同時に端末を開く

参考: Ubuntu起動と同時に自動でターミナルを開く - Qiita

Ubuntuのターミナルでタブを複数起動させて、タブごとに別サーバにログインしたい時 | shinodogg.com

自分の設定(gnome端末を使っていた時)

[~] $cat ~/.config/autostart/gnome-terminal.desktop
[Desktop Entry]
Exec=gnome-terminal --geometry=160x48+0+0
Type=Application
[~] $

↑使う端末をterminatorに変えたのでこれになった - 以下の設定で保存すると、次回からUbuntuを開くときに横幅1200、縦幅960のterminatorのWindowが左上隅に表示される

[~] $cat ~/.config/autostart/terminator-terminal.desktop
[Desktop Entry]
Exec=/usr/bin/terminator --geometry=1200x960+0+0 
Type=Application

git

gitインストール

参考: 第3回 さっそくGitを使ってみよう!:ハックガールズと学ぼう!ゼロから学ぶGit講座|gihyo.jp … 技術評論社

sudo apt-get install git 

[~ ] $ git config --global user.name "ludwig125"
[~ ] $ git config --global user.name 
ludwig125
[~ ] $ git config --global user.email "XXXX@gmail.com"
[~ ] $ mkdir ~/git
[~ ] $ cd git/
[~/git ] $ ls
[~/git ] $ ls
[~/git ] $ git init
Initialized empty Git repository in /home/XXXX/git/.git/
[~/git ] $ git status
On branch master

最初のコミット

nothing to commit (create/copy files and use "git add" to track)
[~/git ] $

 githubの登録

今さら聞けない!GitHubの使い方【超初心者向け】 | TechAcademyマガジン

githubリポジトリ作成

f:id:ludwig125:20190119231748p:plain f:id:ludwig125:20190119231810p:plain

git pushに失敗

[~/git/work ] $ git push origin master
Username for 'https://github.com': ludwig125
Password for 'https://ludwig125@github.com': 
remote: Invalid username or password.
fatal: Authentication failed for 'https://github.com/lugwig125/work.git/'
[~/git/work ] $

 git へpushする方法

1.アクセストークンを使う

[Git][GitHub]GitHubにPushする際に認証失敗する | DevAchieve hubを2-factor authentication(2要素認証)有効でも使いたい - Qiita

以下のように「Personal access tokens」でアクセストークンを発行してコピーしておく

f:id:ludwig125:20190119232834p:plain

パスワード入力にアクセストークンを使ったらpushできるようになった

[~/git/work ] $ git push origin master
Username for 'https://github.com': ludwig125
Password for 'https://ludwig125@github.com': 
Counting objects: 6, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 438 bytes | 0 bytes/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To https://github.com/lugwig125/work.git
3847557..36d73b5 master -> master
[~/git/work ] $ 

2.SSH認証をする

アクセストークンは面倒なので、こちらのほうが便利

参考:

GitHubでssh接続する手順~公開鍵・秘密鍵の生成から~ - Qiita SSHの公開鍵を作成しGithubに登録する手順 - mon_sat at Co-Edo(半年前の自分への教科書 / 別院) MacでGitHubを使う手順 – SSH Keysの登録 / リポジトリの作成 / 基本コマンド | maesblog

手順

  • ssh-keygen -t rsa -C "メルアド"

=> Enter passphrase :ubuntuのパスワードを入力

=> この公開鍵をコピー

[~/git/work ] $ ssh-keygen -t rsa -C "gitに登録したメルアド"
Generating public/private rsa key pair.
Enter file in which to save the key (/home/XXX/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/XXX/.ssh/id_rsa.
Your public key has been saved in /home/XXX/.ssh/id_rsa.pub.
The key fingerprint is:
☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓☓
The key's randomart image is:
+--[ RSA 2048]----+
| .               +==.      |
|                    *OE+. |
|                   .=+*= . |
|                     . +o.= |
|                         S... |
|                                |
|                                |
|                                |
|                                |
+-----------------+
[~/git/work ] $ 
[~/git/work ] $ cat ~/.ssh/id_rsa.pub
ssh-rsa ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○
[~/git/work ] $ ssh -T git@github.com
Warning: Permanently added the RSA host key for IP address '192.30.252.129' to the list of known hosts.
Hi lugwig125! You've successfully authenticated, but GitHub does not provide shell access.
[~/git/work ] $

 SSHKey登録結果

f:id:ludwig125:20190119232205p:plain

c++を使えるようにする

g++のインストール

[~/git/work/src/bin ] $ sudo apt-get install g++
[~/git/work/src/bin ] $ g++ -v

省略
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.1) 
[~/git/work/src/bin ] $

c++コンパイル

参考: 第6回 LinuxでC/C++言語のコンパイルを試す(3ページ目) | 日経クロステック(xTECH)

caps lockをctrlにする手順

下記を参考にしたら、capslockが無効になったが、ctrlにはならなかった。

またいつか試してみる

$ sudo vi /etc/default/keyboard
XKBOPTIONS="ctrl:nocaps"

追記 キーボードの CapsLock と Control を入れ替える – talkwithdevices.com

sudo dpkg-reconfigure keyboard-configuration

をして設定を更新必要があるらしいが、以下のような画面が出た。

自分のキーボードの種類がわからない。。あとで

f:id:ludwig125:20190119232357p:plain

参考:

Ubuntu日本語フォーラム / Ubuntu14.04 LTSでのCtrlとCapslockの入れ替えについて

[LXDE] Caps LockキーをCtrlキーにする(LXDE) - Life with IT

keyboard - How to map Caps Lock key to something useful? - Raspberry Pi Stack Exchange

CapsLockをCtrlにするまとめ - Λlisue's blog

キーボードの CapsLock と Control を入れ替える – talkwithdevices.com

Zsh

zshを使う - ludwig125のブログ

その他設定

https://ludwig125.hatenablog.com/entry/2020/03/19/043511

vmware playerとホストのWindowsの間のフォーカスを切り替える - ludwig125のブログ

4kディスプレイで仮想マシンのUbuntuの文字サイズを大きくする - ludwig125のブログ

トラブル対応

Ubuntu14.0.4でGoogleChromeが起動しない - ludwig125のブログ

VMwareplayerのトラブル - ludwig125のブログ

vmwareで「パワーオン中にエラーが発生しました」が出た(Windows10) - ludwig125のブログ

scalaでjavaクラスを呼び出す

概要

仕事でScalaから別javaを呼び出すときに妙に悩んだのでメモ

以下の3パターンを列挙する

自分の状況

  • scalaよくわかってない
  • java全然しらない

javaからjavaを使う

javaの場合はclass名とファイル名が同じでないといけないらしい

以下のファイルを用意する

[ludwig125 JavaJava]$ cat SampleClass.java
public class SampleClass {
        public static String val = "hoge";

    public static int add(int x, int y) {
        return x + y;
    }
    public static int sub(int x, int y) {
        return x - y;
    }
}
[ludwig125 JavaJava]$
[ludwig125 JavaJava]$ cat useSample.java
public class useSample {
    public static void main(String[] args) {

        System.out.println(SampleClass.val);
        System.out.println(SampleClass.add(5, 3));
        System.out.println(SampleClass.sub(5, 3));
    }
}
[ludwig125 JavaJava]$

コンパイルして実行

[ludwig125 JavaJava]$ javac SampleClass.java useSample.java
[ludwig125 JavaJava]$ java useSample
hoge
8
2
[ludwig125 JavaJava]$

scalaからscalaを使う

以下のファイルを用意する

[ludwig125 ScalaScala]$ cat SampleClass.scala
class S {
  val v = "hoge"
  def add(a: Int, b: Int): Int = a + b
  def sub(a: Int, b: Int): Int = a - b
}
[ludwig125 ScalaScala]$
[ludwig125 ScalaScala]$ cat useSample.scala
object useS {
  def main(args: Array[String]): Unit = {
    val s = new S
    println(s.v)
    println(s.add(5, 3))
    println(s.sub(5, 3))
  }
}
[ludwig125 ScalaScala]$

コンパイルして実行

[ludwig125 ScalaScala]$ scalac SampleClass.scala useSample.scala
[ludwig125 ScalaScala]$ scala useS
hoge
8
2
[ludwig125 ScalaScala]$

特記事項

scalaの場合javaと違ってファイル名とクラス名を一致させる必要はない 「val」がscalaの予約後なのでvに変えたが、valのまま使う方法もあったはず

scalaの場合、scalacでファイル名を指定してコンパイルしたあと、scalaコマンドの実行対象はクラス名(この場合useS)であることに注意

scalaからjavaを使う

以下のファイルを用意する

[ludwig125 ScalaJava]$ cat SampleClass.java
public class SampleClass {
        public static String v = "hoge";

    public static int add(int x, int y) {
        return x + y;
    }
    public static int sub(int x, int y) {
        return x - y;
    }
}
[ludwig125 ScalaJava]$
[ludwig125 ScalaJava]$ cat useSample.scala
object useS {
  def main(args: Array[String]): Unit = {
    println(SampleClass.v)
    println(SampleClass.add(5, 3))
    println(SampleClass.sub(5, 3))
  }
}
[ludwig125 ScalaJava]$

コンパイルして実行

javascalaを個別にコンパイルしたけど、まとめてやる方法があるのかどうかは確認してない

[ludwig125 ScalaJava]$ javac SampleClass.java
[ludwig125 ScalaJava]$ scalac useSample.scala
[ludwig125 ScalaJava]$ scala useS
hoge
8
2
[ludwig125 ScalaJava]$