- 投稿日:2019-03-10T23:47:13+09:00
golangの時刻処理におけるint型の取り扱い方
はじめに
golangで簡単なタイマーをつくろうとしたときに出会いました。
timeパッケージはint型を使用するのにひと工夫が必要です。time.Duration型
以下のようなソースを書いたところ
package main import ( "flag" "fmt" "strconv" "time" ) func main() { fmt.Printf("timer start %v\n", time.Now()) // 第一引数の値をintegerに変換する flag.Parse() second, err := strconv.Atoi(flag.Arg(0)) if err != nil { fmt.Println(err) } // データは読み捨てる <-time.After(second * time.Second) fmt.Printf("timer stop %v\n", time.Now()) }こんなエラーが出ました。
invalid operation: second * time.Second (mismatched types int and time.Duration)time.After()ではtime.Duration型を受け付けますが, int型*Duration型は使えないみたいです。
そのため, 時刻処理にint型を使用する際にはtime.Duration()でラップしてやる必要があります。<-time.After(time.Duration(second) * time.Second)エラーも出なくなりましたので動かしてみましょうか。
go run timer.go 5timer start 2019-03-10 23:26:00.38674904 +0900 JST m=+0.000227757 timer stop 2019-03-10 23:26:05.386941458 +0900 JST m=+5.000420240ばっちり動いてますね!
さいごに
そもそもなぜタイマーをつくっていたのかというと渋川よしきさん著の「Goならわかるシステムプログラミング」という本を読んでいたためでした。
こちらの本、とてもわくわくしながら読み進められています!
golangを身につけたい方、システムプログラミングを知りたい方、是非手にとってみてはどうでしょうか?とてもおすすめです!さて、こちらの記事が私の初めて書いた記事となります。
これからは自分が学習中に気づいたこと、そしてみなさんを共有したいことを書いていこうと思います!
それではまた近いうちにお会いしましょう!参考
- 投稿日:2019-03-10T21:18:36+09:00
Serverless Frameworkを使ってgoを定期実行させる
勉強がてら触っていた際のメモです。
slsのインストール
npm install serverless -ggoのテンプレート作成
sls create -u https://github.com/serverless/serverless-golang/ -p go-hatenago modulesの初期化
go mod init go-hatena必要なパッケージのインストール
go get github.com/aws/aws-lambda-go/lambdaserverless.ymlの編集
以下のような感じで.aws/credentialsを仕事用、プライベート用で分けている。
$ vim ~/.aws/credentials [default] aws_access_key_id = hoge aws_secret_access_key = huga cloudfront=true [private] aws_access_key_id = hogehoge aws_secret_access_key = hugahuga今回、lambdaのデプロイはprivate用の方で行いたい。
なのでserverless.ymlにその設定を書いていく。service: go-hatena provider: name: aws runtime: go1.x stage: ${opt:stage, self:custom.defaultStage} #profileオプション追加 profile: ${opt:profile, self:custom.defaultProfile} #regionオプション追加 region: ${opt:region, self:custom.defaultRegion} custom: defaultStage: dev #何も指定がなければ private profile defaultProfile: private #何も指定がなければ 東京リージョン defaultRegion: ap-northeast-1 package: exclude: - ./** include: - ./bin/** functions: hello: handler: bin/mainまた、awsのアカウントを指定してデプロイを行う別の方法として、--aws-profileオプションを使う方法もあるぽい。参考
serverless deploy --aws-profile privateMakefileの作成
Makefileという名前でファイルを作成し、ビルドとデプロイのコマンドを記載
build: GOOS=linux go build -o bin/main deploy: sls deployビルド、デプロイ
make build make deployこれでawsのlambdaを見に行けば関数がデプロイされている。
実行
Makefileに以下を追加し、
run: sls invoke -f hello実行。レスポンスが返ってくる。
$ make run sls invoke -f hello { "message": "Go Serverless v1.0! Your function executed successfully!" } $作成した関数を定期実行させる
serverless.ymlにscheduleを追記
functions: hello: handler: bin/main events: - schedule: cron(0/1 * * * ? *)定期実行されていることはcloudWatchLogsから確認できる。わかりやすくするために「hello!!」とログ表示させるようにする。
main.gofunc Handler() (Response, error) { fmt.Println("hello!!") // 追記 return Response{ Message: "Go Serverless v1.0! Your function executed successfully!", }, nil }ビルドし、デプロイ
make build make deploy定期実行されていることの確認
hello!!と定期的に表示されている。
cloudWatchLogsの保存期間を設定
デフォルトでslsではCloudWatch Logsの保存期間は無期限らしい。
個人アカウントの場合、課金されても嫌なので、保存期間を設定する。serverless.ymlを修正
provider: logRetentionInDays: 1 #追加また、このlogRetentionInDaysに指定できる日数は決まってる
[1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]上記以外の日数を指定するとデプロイで怒られる
Invalid retention value. Valid values are: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653] (Service: AWSLogs; Status Code: 400; Error Code: InvalidParameterException; Request ID: 8aca73dc-43a1-11e9-a81a-6bf91d5376af).lambdaのバージョン管理をしない
デフォルトでslsはデプロイした全てのバージョンのlambdaを保存するらしい。
今回お遊びで触っているだけなので、バージョン管理をしないように設定。provider: versionFunctions: false #追加定期実行を止める
lambdaの管理コンソール上からCloudWatchEventsの有効/無効が切り替えれる。
無効にし、保存すれば定期実行は止まる参考
https://qiita.com/imura81gt/items/5cf490468f95ce3d100b
https://tikasan.hatenablog.com/entry/2018/06/27/083316
https://dev.classmethod.jp/etc/serverless-framework-lambda-cron-execute/
https://tech.ga-tech.co.jp/entry/2018/12/12/120000
- 投稿日:2019-03-10T17:51:28+09:00
Buildkit の Goのコードを読んで Dockerfile 抽象構文木から LLB を生成するフローを覗いてみよう!!
こんにちはpo3rinです。最近趣味でmoby project の buildkit の実装をぼーっと眺めてます。
前回の記事で Dockerfile の parser を使い、Dockerfie の抽象構文木の構造を確認しました。https://qiita.com/po3rin/items/a3934f47b5e390acfdfd
更に暇なので、Dockerfile の AST がどのように LLB に変換されているかを大雑把に探ったので記事にしました。執筆時 moby/buildkit のバージョンはv0.3.3です。
LLBとは
Docker 18.09から BuildKit が正式に統合され、Dockerfileのビルドをローレベルの中間ビルドフォーマット(LLB:Low-Level intermediate build format)を介して行われるようになりました。LLBはDAG構造(上の画像のような非循環な構造)を取ることにより、ステージごとの依存を解決し、並列実行可能な形で記述可能です。これにより、BuildKitを使ったdocker build は並列実行を可能にし、従来よりもビルドを高速化しています。
参考:https://www.publickey1.jp/blog/18/docker_engine_1809buildkit9.html
Perserがどこで使われているのか探す
まず、ASTの確認で使った
moby/buildkit/frontend/dockerfile/parser/parser.go
のparser.Parse
関数を呼び出している部分を検索してみましょう。そうすると下記がヒットします。buildkit/frontend/dockerfile/parser/dumper/main.go buildkit/frontend/dockerfile/instructions/parse_test.go buildkit/frontend/dockerfile/instructions/parse_test.go buildkit/frontend/dockerfile/dockerfile2llb/convert.goこの中で明らかにbuildkit/frontend/dockerfile/dockerfile2llb/convert.goだけが匂います。コードを見ると下記の関数が呼び出していました。
func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error){ // ... dockerfile, err := parser.Parse(bytes.NewReader(dt)) if err != nil { return nil, nil, err } // ... }Dockerfile から LLB(Low-Level intermediate build format) を生成するドンピシャの関数発見です。引数の dt は parser.Parse の引数に使うところから推察して Dockerfile のバイト列を渡しているのでしょう。opt は ConvertOpt という名前から LLB に Convert するときの Option を渡せるそうです。
DockerfileのASTがLLBになるフローを読む
それでは前回確認した Dockerfile の AST が使われているコードをみてみましょう。dockerfile.AST をさらに Parse している箇所があります。
func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error){ // ... stages, metaArgs, err := instructions.Parse(dockerfile.AST) if err != nil { return nil, nil, err } // ... }何やら AST から stages & metaArgs という物を手に入れています。これはなんでしょうか。ローカルで確認してみます。import で多分ハマるので moby/buildkit をクローンしてきて適当な場所にディレクトリを切るとすんなり行きます。
package main import ( "os" "github.com/kr/pretty" "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/parser" ) func main() { f, _ := os.Open("./Dockerfile") r, _ := parser.Parse(f) stages, metaArgs, _ := instructions.Parse(r.AST) pretty.Print(stages) pretty.Print(metaArgs) }対象は下記のようなマルチステージビルドするDockerfileにしましょう。
FROM golang:1.11.1 as builder WORKDIR /api COPY . . ENV GO111MODULE=on RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /app COPY --from=builder /api . CMD ["./server"]実行するとstagesは下記の構造であることがわかります。
[]instructions.Stage{ { Name: "builder", Commands: { &instructions.WorkdirCommand{ withNameAndCode: instructions.withNameAndCode{ code:"WORKDIR /api", name:"workdir" }, Path: "/api", }, &instructions.CopyCommand{ withNameAndCode: instructions.withNameAndCode{ code:"COPY . .", name:"copy" }, SourcesAndDest: {".", "."}, From: "", Chown: "", }, &instructions.EnvCommand{ withNameAndCode: instructions.withNameAndCode{ code:"ENV GO111MODULE=on", name:"env" }, Env: { {Key:"GO111MODULE", Value:"on"}, }, }, &instructions.RunCommand{ withNameAndCode: instructions.withNameAndCode{ code:"RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo .", name:"run" }, withExternalData: instructions.withExternalData{}, ShellDependantCmdLine: instructions.ShellDependantCmdLine{ CmdLine: {"CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo ."}, PrependShell: true, }, }, }, BaseName: "golang:1.11.1", SourceCode: "FROM golang:1.11.1 as builder", Platform: "", }, { Name: "", Commands: { &instructions.RunCommand{ withNameAndCode: instructions.withNameAndCode{ code:"RUN apk --no-cache add ca-certificates", name:"run" }, withExternalData: instructions.withExternalData{}, ShellDependantCmdLine: instructions.ShellDependantCmdLine{ CmdLine: {"apk --no-cache add ca-certificates"}, PrependShell: true, }, }, &instructions.WorkdirCommand{ withNameAndCode: instructions.withNameAndCode{ code:"WORKDIR /app", name:"workdir" }, Path: "/app", }, &instructions.CopyCommand{ withNameAndCode: instructions.withNameAndCode{ code:"COPY --from=builder /api .", name:"copy" }, SourcesAndDest: {"/api", "."}, From: "builder", Chown: "", }, &instructions.CmdCommand{ withNameAndCode: instructions.withNameAndCode{ code:"CMD [\"./server\"]", name:"cmd" }, ShellDependantCmdLine: instructions.ShellDependantCmdLine{ CmdLine: {"./server"}, PrependShell: false, }, }, }, BaseName: "alpine:latest", SourceCode: "FROM alpine:latest", Platform: "", }, }何やら
instructions.Stage
構造体が出てきた。中身を見るとASTよりもさらに進んで、Dockerfileをビルド可能なステージの集まりに解析している。今回はマルチステージビルドを行うDockerfileなのでlen(instructions.Stage)==2
であることがわかる。そしてinstructions.Stage
の中ではコマンド種類ごとに解析されている。では更に深ぼるために
instructions.Perser
のコードを読んでみよう。// Parse a Dockerfile into a collection of buildable stages. // metaArgs is a collection of ARG instructions that occur before the first FROM. func Parse(ast *parser.Node) (stages []Stage, metaArgs []ArgCommand, err error) { for _, n := range ast.Children { cmd, err := ParseInstruction(n) if err != nil { return nil, nil, &parseError{inner: err, node: n} } if len(stages) == 0 { // meta arg case if a, isArg := cmd.(*ArgCommand); isArg { metaArgs = append(metaArgs, *a) continue } } switch c := cmd.(type) { case *Stage: stages = append(stages, *c) case Command: stage, err := CurrentStage(stages) if err != nil { return nil, nil, err } stage.AddCommand(c) default: return nil, nil, errors.Errorf("%T is not a command type", cmd) } } return stages, metaArgs, nil }Children Nodeの数だけforで回している。そして、switch文でそのNodeがstageに関するものなのかcommandに関するものなのかを分けている。stageなら
[]instructions.Stage
に追加し、commandなら対象のinstructions.Stage
に追加している。commandはそれぞれ意味ごとに
instructions.CmdCommand
やinstructions.CopyCommand
などに大別されている。Goを書いたことがある人ならば何やらinterfaceの香りがしてきますね。buildkit/frontend/dockerfile/instructions/commands.go
を見るとやはりありました。// Command is implemented by every command present in a dockerfile type Command interface { Name() string }これがCommandを表現するinterfaceですね。予想通りcommandの識別子を返します。識別子は
buildkit/frontend/dockerfile/command/command.go
にあります。つまりこれがDockerfileで使えるコマンドの全てです。// Define constants for the command strings const ( Add = "add" Arg = "arg" Cmd = "cmd" Copy = "copy" Entrypoint = "entrypoint" Env = "env" Expose = "expose" From = "from" Healthcheck = "healthcheck" Label = "label" Maintainer = "maintainer" Onbuild = "onbuild" Run = "run" Shell = "shell" StopSignal = "stopsignal" User = "user" Volume = "volume" Workdir = "workdir" )switch文を見ると、defaultでエラーを返しています。つまり上のcommandに当てはまらないものに対してエラーを吐いています。ASTを作る時ではなくここで検知するんですね。
LLBの中身を見る
dockerfile2llb.Dockerfile2LLB
のここから先は[]instructions.Stage
からLLBを構築していく部分になります。dockerfile2llb.Dockerfile2LLB関数で最終的に出てくるLLBの出力をサラッと確認しましょう。package main import ( "io/ioutil" "github.com/kr/pretty" "github.com/moby/buildkit/client/llb/imagemetaresolver" "github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb" "github.com/moby/buildkit/util/appcontext" ) func main() { df, _ := ioutil.ReadFile("./Dockerfile") st, img, _ := dockerfile2llb.Dockerfile2LLB(appcontext.Context(), df, dockerfile2llb.ConvertOpt{}) pretty.Print(st) pretty.Print(img) }出力が長いので、各自確認してみてください。st(llb.State型)はまさに欲していた LLB の定義を表し、img (dockerfile2llb.Image型) コンテナイメージの基本情報が入ります。
まとめ
dockerfile2llb.Dockerfile2LLB
関数が行なっていることを図にすると下記のようなフローになります。(ちょっとBuildable Stages から LLB までの詰めが甘いですが。。)Goは読みやすくていいですね。実は README.md の moby/buildkit の buildctl コマンドを使った example でもLLBの構造を簡単に確認できます。暇ならそちらの紹介も記事にします。
- 投稿日:2019-03-10T11:17:46+09:00
GolangでQuoineのAPIを使ってみた
はじめに
最近UdemyでGolangを勉強しているのですが、応用編のビットコイン自動売買の講義でbitflyerのアカウントがなく実際に売買できないってなったので代わりにみつけたQuoine APIを使って講義を受けています。
実際に動くものを書きながら講義を受けたほうが理解が深まりやすいと思ったので、初めてですが記事を書いてみることにしました。
こちらのかたの講座です。
https://www.udemy.com/share/100BhMBEsZeF9QQn4=/Liquid by Quoineについて
bitflyerの持っていない方はアカウントを作れない現状(2019年3月)これ以外特に選択肢がないように思えます。
現物取引が手数料無料なので、bitflyerをお持ちの方にもliquidがおすすめです。アカウントの準備
こちらでアカウントを作成してAPIのIDとキーを取得してください。
こちらはググれば出てくるので割愛します。APIをGoで書いてみる
APIのリファレンスです。
https://developers.quoine.com/LiquidAPIの認証
quoine.gotype APIClient struct { key string secret string httpClient *http.Client } // header リクエストに追加するヘッダー情報 func (api APIClient) header(method, endpoint string, body []byte) map[string]string { token := jwt.New(jwt.GetSigningMethod("HS256")) token.Claims = jwt.MapClaims{ "path": endpoint, "nonce": strconv.FormatInt(time.Now().Unix(), 10), "token_id": "APIキーをここに", } signature, _ := token.SignedString([]byte("APIシークレットをここに")) return map[string]string{ "X-Quoine-Auth": signature, "X-Quoine-API-Version": "2", "Content-Type": "application/json", } }すみません。JWTについてはよく分からなかったのでgithub.com/dgrijalva/jwt-goを使いました。
残高の取得
quoine.gotype Balance struct { Currency string `json:"currency"` Balance string `json:"balance"` } // GetBalances 現在の総合資産を取得する func (api *APIClient) GetBalances() []Balance { url := "/accounts/balance" var balances []Balance resp, err := api.doRequest("GET", url, nil, nil) if err != nil { log.Printf("action=GETBalances err=%s", err.Error()) return balances } err = json.Unmarshal(resp, &balances) if err != nil { log.Printf("action=GETBalances(unmarshal) err=%s", err.Error()) return balances } return balances }注文
quoine.go// Order 注文 type Order struct { OrderType string `json:"order_type"` ProductID int `json:"product_id"` Side string `json:"side"` Quantity string `json:"quantity"` Price string `json:"price"` } // ResponseSendChildOrder 注文のレスポンス情報 type ResponseSendChildOrder struct { ID int `json:"id"` OrderType string `json:"order_type"` Quantity string `json:"quantity"` DiscQuantity string `json:"disc_quantity"` IcebergTotalQuantity string `json:"iceberg_total_quantity"` Side string `json:"side"` FilledQuantity string `json:"filled_quantity"` Price float64 `json:"price"` CreatedAt int `json:"created_at"` UpdatedAt int `json:"updated_at"` Status string `json:"status"` LeverageLevel int `json:"leverage_level"` SourceExchange string `json:"source_exchange"` ProductID int `json:"product_id"` ProductCode string `json:"product_code"` FundingCurrency string `json:"funding_currency"` CryptoAccountID interface{} `json:"crypto_account_id"` CurrencyPairCode string `json:"currency_pair_code"` AveragePrice float64 `json:"average_price"` Target string `json:"target"` OrderFee float64 `json:"order_fee"` SourceAction string `json:"source_action"` UnwoundTradeID interface{} `json:"unwound_trade_id"` TradeID interface{} `json:"trade_id"` } // SendOrder 注文を送る func (api *APIClient) SendOrder(order *SendOrder) (*ResponseSendChildOrder, error) { data, _ := json.Marshal(order) url := "/orders/" resp, err := api.doRequest("POST", url, map[string]string{}, data) if err != nil { log.Printf("Order Request fail, err=%s", err.Error()) return nil, err } var response ResponseSendChildOrder err = json.Unmarshal(resp, &response) if err != nil { log.Printf("Order Request Unmarshal fail, err=%s", err.Error()) return nil, err } return &response, nil } // GetOrder 注文IDの情報を取得する func (api *APIClient) GetOrder(orderID int) (*GetOrder, error) { var getOrder *GetOrder spath := fmt.Sprintf("/orders/%d", orderID) resp, err := api.doRequest("GET", spath, nil, nil) if err != nil { log.Printf("Get Order Request Error, err = %s", err.Error()) return nil, err } err = json.Unmarshal(resp, &getOrder) if err != nil { log.Printf("Get Order Request Unmarshal Error, err = %s", err.Error()) return nil, err } return getOrder, nil }さいごに
まだ勉強中なのでこれを機にたくさんアウトプットをしてみようと思います。
ありがとうございました。
- 投稿日:2019-03-10T00:17:11+09:00
オフラインリアルタイムどう書く E31 の実装例( Go, C++ )
問題の名前 : ぐるぐる数
問題 : http://nabetani.sakura.ne.jp/hena/orde31rotnum/
実装リンク集 : https://qiita.com/Nabetani/items/d139d5ef70aa23c2d038次回のイベントは 4/6
see https://yhpg.doorkeeper.jp/events/88379で。
ナイーブな実装を Go と C++ で。
Go
gopackage main import ( "encoding/json" "fmt" "io/ioutil" "os" "strconv" "strings" "time" ) func isGuru(b, i int32) bool { prev := int32(-1) for { num := i % b if 0 <= prev && prev != num && prev != (num+1)%b { return false } i = (i - num) / b if i == 0 { return true } prev = num } } func impl(b, x, y int32) int32 { c := int32(0) for i := x; i <= y; i++ { if isGuru(b, i) { c++ } } return c } func solveSlow(src string) string { vals := strings.Split(src, ",") b, e0 := strconv.Atoi(vals[0]) x, e1 := strconv.ParseInt(vals[1], b, 32) y, e2 := strconv.ParseInt(vals[2], b, 32) if e0 != nil || e1 != nil || e2 != nil { panic(fmt.Sprint(e0, e1, e2)) } count := impl(int32(b), int32(x), int32(y)) return fmt.Sprint(count) } func bytesFromFile(fn string) []byte { f, err := os.Open(fn) if err != nil { panic(err) } defer f.Close() b, err := ioutil.ReadAll(f) if err != nil { panic(err) } return b } type testDataType struct { Number int `json:"number"` Src string `json:"src"` Expected string `json:"expected"` } type dataType struct { EventID string `json:"event_id"` EventURL string `json:"event_url"` TestData []testDataType `json:"test_data"` } func runTest(list []testDataType) { for _, t := range list { start := time.Now() actual := solveSlow(t.Src) tick := time.Now().Sub(start).Seconds() result := func() string { if actual == t.Expected { return "ok" } return "***NG***" }() fmt.Printf("%2d:%s solve(%q)=%q, want %q ( %.3f sec )\n", t.Number, result, t.Src, actual, t.Expected, tick) } } func main() { data := dataType{} json.Unmarshal(bytesFromFile(os.Args[1]), &data) start := time.Now() runTest(data.TestData) tick := time.Now().Sub(start).Seconds() fmt.Printf("total : %.2f sec\n", tick) }実行は
go run slow.go data.json
みたいな感じで。随所で
int32
と書いているのは、int
よりもたぶん速いから。手元の環境ではint
は 64bit なので、落ちるコードに差が出てくる。この実装で、手元の環境で 75秒ぐらい。
goルーチンを活用すれば半分ぐらいにはなるはずだけど、何もしていない。C++
こちらは、cielavenir さんの記事を参考にして、OpenMP を入れてみた。
c++17// clang++ -O2 -std=c++17 -Wall -Xpreprocessor -fopenmp -lomp 3.cpp #include <iomanip> #include <iostream> #include <numeric> #include <regex> #include <sstream> #include <string> int is_guru(int b, int i) { auto prev{-1}; for (;;) { auto num = i % b; if (0 <= prev && prev != num && prev != (num + 1) % b) { return 0; } i = (i - num) / b; if (i == 0) { return 1; } prev = num; } } std::string solve(std::string const &src) { auto regex = std::regex(R"(^([^\,]+)\,([^\,]+)\,([^\,]+))"); std::smatch m; std::regex_match(src, m, regex); auto b = std::stoi(m[1]); auto x = std::strtol(m[2].str().c_str(), nullptr, b); auto y = std::strtol(m[3].str().c_str(), nullptr, b); int count = 0; #pragma omp parallel for reduction(+ : count) for (int i = x; i <= y; ++i) { count += is_guru(b, i); } return std::to_string(count); } void test(std::string const &src, std::string const &expected) { auto actual{solve(src)}; auto okay{actual == expected}; std::cout << (okay ? "ok" : "**NG**") << " " << src << " " << actual << " " << expected << std::endl; } int main() { /*0*/ test("4,1313,3012", "12"); /*1*/ test("10,100,110", "0"); /*2*/ test("36,zoo,zyz", "0"); /*3*/ test("4,1300000,2222221", "0"); // 中略 /*68*/ test("2,101001011010110000110101,110101110001110110110101", "3240321"); }Go の実装とロジックは同じ。
というか、Go の実装を移植しただけ。
入力のパースで正規表現を使ってみた。std::split
とかあると嬉しいんだけどなぁ。
「中略」のところを略さないで実行して、手元のマシンで、10秒弱。OpenMP 様々。