- 投稿日:2020-09-14T23:55:26+09:00
A Tour of Go メモ 【4】二日目
A Tour of Go
Range
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } for i, a := range hiragana { fmt.Printf("「%s」は、%d番目\n", a, i+1) } } > 2**0 = 1 > 2**1 = 2 > 2**2 = 4 > 2**3 = 8 > 2**4 = 16 > 2**5 = 32 > 2**6 = 64 > 2**7 = 128 > 「あ」は、1番目 > 「い」は、2番目 > 「う」は、3番目 > 「え」は、4番目 > 「お」は、5番目rangeと"_(アンダーバー)"
func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1 << uint(i) // == 2**i } for _, value := range pow { fmt.Printf("%d\n", value) } s := "hello" for i, _ := range s { fmt.Printf("%d回目の%s\n", i+1, s) } } > 1 > 2 > 4 > 8 > 16 > 32 > 64 > 128 > 256 > 512 > 1回目のhello > 2回目のhello > 3回目のhello > 4回目のhello > 5回目のhelloMaps
make(map["keyになる値の型"]"valueになる値の型")
例えば、
・ keyも値もint型
=> make(map[int]int)
.keyも値もstring型
=> make(map[string]string)type Vertex struct { Lat, Long float64 } var m map[string]Vertex //変数を定義するときにキーと値を設定できる var n = map[string]Vertex{ "key1" : Vertex { 10.0, 10.000, }, "key2" : Vertex { 20.0, 20.000, }, } var z = map[string]Vertex{ "key1" : {333.33, 44.444}, "key2" : {88.888, 999.999}, } func main() { fmt.Println(m) //エラー。 初期化していないので、追加できない。 m["insert"] = Vertex{ 11.111, 22.222, } fmt.Println(n) // 初期化 m = make(map[string]Vertex) fmt.Println(m) //キーの追加 m["Bell Labs"] = Vertex{ 40.68433, -74.39967, } m["test"] = Vertex{ 22222, 2020202, } fmt.Println(m) fmt.Println(m["Bell Labs"]) } > map[] > map[key1:{10 10} key2:{20 20}] > map[] > map[Bell Labs:{40.68433 -74.39967} test:{22222 2.020202e+06}] > {40.68433 -74.39967}Mapの操作
func main() { m := make(map[string]int) m["Answer"] = 42 fmt.Println(m) //keyを指定すると対応した要素(elem)を取得でkりう fmt.Println("The value:", m["Answer"]) m["Answer"] = 48 fmt.Println("The value:", m["Answer"]) //要素の削除 delete(m, "Answer") fmt.Println("The value:", m["Answer"]) //keyに対応する値があるか //もしもkeyがあれば、対応する値がvに入る、keyがなければ、map作成時に指定した型のゼロ値が入る //okには、keyがあれば、true, なければfalseが入る v, ok := m["Answer"] fmt.Println("The value:", v, "Present?", ok) } > map[Answer:42] > The value: 42 > The value: 48 > The value: 0 > The value: 0 Present? false関数
関数の引数に関数を渡せる
import ( "fmt" "math" ) //引数に関数を渡すときに渡す関数の引数と戻り値の型を書かなくてはいけない //引数に渡された関数の引数に3,4をいれる関数 func compute(fn func(float64, float64) float64) float64 { return fn(3, 4) } func main() { hypot := func(x, y float64) float64 { return math.Sqrt(x*x + y*y) } fmt.Println(hypot(5, 12)) // hypot関数の引数に3,4をいれる fmt.Println(compute(hypot)) //3の4乗を返す fmt.Println(compute(math.Pow)) } > 13 > 5 > 81クロージャー
・複数の関数がセットとなり、その関数内で変数を共有できる
・オブジェクトの変数やメソッドを容易に変更できないようにカプセル化できるfunc adder() func(int) int { //sumの値を維持できる sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } }
- 投稿日:2020-09-14T22:50:28+09:00
GolangでDynamoDBとJSONのマッピングを同時に使って躓いた話
Golang x Lambdaで小規模なAPIを作った際、一つの構造体にDynamoDBとJSONのマッピングを同時に使って躓いたのでメモを残しておく。
うまく行かない方法
DynamoDBから取得したデータに対して以下のような構造体でマッピングをして、それをそのままJSON形式で返そうとした。
雰囲気としては以下の感じ(エラーハンドリングなどは省略)main.gotype UserInfo struct { UserName string `dynamodbav:"userName", json:"userName"` ContactNumber int `dynamodbav:"contactNumber", json:"contactNumber"` } func SuccessResponse(body string) events.APIGatewayProxyResponse { return events.APIGatewayProxyResponse{ StatusCode: 200, IsBase64Encoded: false, Body: body, Headers: map[string]string{ "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", }, } } func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { // いろいろ... var dbResult DbResult // dynamoDBからQueryでデータ取得 result, _ := svc.Query(fetchQueryParams(userId)) // dynamoDBのデータを構造体にマッピング dynamodbattribute.UnmarshalMap(result.Items[0], &dbResult) responseBody, _ := json.Marshal(&dbResult) return SuccessResponse(string(responseBody)), nil }すると、レスポンスは以下のようになった。
JSONのレスポンス{ "UserName": "やまだたろう", "ContactNumber": 1234567890 }なぜかkeyの先頭が大文字になった。どうも構造体に定義した
json:
での指定がうまく効いていないようだ。うまくいく方法
エラーが出るわけでもなく完全に原因不明で小一時間ほど詰まった挙げ句にたどり着いたのがこちら。
DynamoDB Marshal: "dynamodbav" struct tag is not respected when json tag is present unless it is first #748You need a space instead of a comma between multiple tags I think. Comma didn't work for me.
とのことで、
dynamodbav
とjson
の区切りは,
ではなく(スペース)にしないとイケないらしい。
コンマにしていたせいでjsonのkey名が構造体のフィールド名と同じ形式になっていたようだ(指定がない場合には構造体のフィールド名がJSONのkey名に入る仕様らしい)。以下のように修正することで、意図通りのレスポンス形式となった。
修正版の構造体type UserInfo struct { UserName string `dynamodbav:"userName" json:"userName"` // スペースで区切る ContactNumber int `dynamodbav:"contactNumber" json:"contactNumber"`// スペースで区切る }JSONのレスポンス(修正版){ "userName": "やまだたろう", "contactNumber": 1234567890 }所感
せめてエラーか何か出してほしかった....。
- 投稿日:2020-09-14T22:41:02+09:00
Golang チュートリアル その2
概要
「処理が速い」「並行処理」が得意ということから、
今後大規模webサービスなどで重宝されると思いましたので、
学習していきたいと思います。今回は、「For」や「if」といったところをみていきます。
参考サイト
以下を実施してみました。
環境
GoogleChrome
※ブラウザ操作だけで学習できるため、 エディタを用意する必要がありません。
※私は、ローカルにCentOS8環境を用意して、チュートリアルを行います。
※Goインストール手順です。よろしければ、ご活用ください。
CentOS8へのGo言語インストール手順For文
- 書き方
forループはセミコロン ; で3つの部分に分かれている。
・初期化ステートメント: 最初のイテレーション(繰り返し)の前に初期化が実行される。
・条件式: イテレーション毎に評価される。
・後処理ステートメント: イテレーション毎の最後に実行される。
・初期化ステートメントは、短い変数宣言によく利用される。
※その変数は for ステートメントのスコープ内でのみ有効。※他の言語、C言語やJava、JavaScriptのfor ループとは異なり、
for ステートメントの3つの部分を括る括弧 ( ) はない。
しかし、中括弧 { } は常に必要。for.gopackage main import "fmt" func main(){ sum := 0 for i := 0; i < 10; i++{ sum += i } fmt.Println(sum) } //実行 go run for.go //実行結果 45
- For 続き
初期化と後処理ステートメントの記述は任意である。
※for.goファイルの中身を以下に書き換えるfor.gopackage main import "fmt" func main(){ sum := 1 for ; sum < 10; { sum += sum } fmt.Println(sum) } //実行 go run for.go //実行結果 16
- For 続き whileの代わりにForを使う
セミコロン(;)も省略可能。
C言語などにある while は、Goではforだけを使う。for.gopackage main import "fmt" func main(){ sum := 1 //10の後ろの;を省略している for sum < 10 { sum += sum } fmt.Println(sum) } //実行 go run for.go //実行結果 16
- 無限ループ
forのループ条件を省略すれば無限ループが可能となる。
for.gopackage main import "fmt" func main(){ i := 1 for{ if i < 10 { i += i }else{ break } fmt.Println(i) } } //実行 go run for.go //実行結果 2 4 8 16If文
ifステートメントは、forループと同様に、
括弧 ( ) は不要で、中括弧 { } は必要。if.gopackage main import ( "fmt" "math" ) func sqrt(x float64) string{ if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) } func main(){ fmt.Println(sqrt(2), sqrt(-4)) } //実行 go run if.go //実行結果 1.4142135623730951 2i
- If内で宣言した変数
上記は、elseブロック内でも使うことができる
Switch
switchステートメントはif-elseステートメントのシーケンスを短く書く方法。
Goでは選択されたcaseだけを実行してそれに続く全てのcaseは実行されない。
これらの言語の各caseの最後に必要なbreakステートメントがGoでは自動的に提供されるため。
もう一つの重要な違いはGoのswitchのcaseは定数である必要はなく、
関係する値は整数である必要はないということ。switch.gopackage main import ( "fmt" "runtime" ) func main(){ fmt.Print("Go runs on ") switch os := runtime.GOOS; os{ case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: fmt.Printf("%s.\n", os) } } //実行 switch.go
- 条件なしのSwitch
条件のないswitchは、switch true と書くことと同じに意味合いです。
このswitchの構造は、長くなりがちな"if-then-else"のつながりをシンプルに表現できます。switch-with-no-condition.gopackage main import ( "fmt" "time" ) func main(){ t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Print("Good afternoon") default: fmt.Println("Good evening.") } } //実行 go run switch-with-no-condition.goDefer
deferステートメントは、 defer へ渡した関数の実行を、
呼び出し元の関数の終わり(returnする)まで遅延させるもの。deferへ渡した関数の引数は、すぐに評価されますが、
その関数自体は呼び出し元の関数がreturnするまで実行されません。defer.gopackage main import "fmt" func main(){ defer fmt.Println("World") fmt.Println("Hello") } //実行 go run defer.go //結果 Hello World
- deferを複数使用した場合
defer へ渡した関数が複数ある場合、
その呼び出しはスタック( stack )される。
呼び出し元の関数がreturnするとき、
defer へ渡した関数は LIFO(last-in-first-out) の順番で実行される。defer-multi.gopackage main import "fmt" func main(){ fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") } //実行 go run defer-multi.go //結果 counting done 9 8 7 6 5 4 3 2 1 0まとめ
今回は、「For」「If」などみていきました。
次回は、ポインタを見ていきたいと思います。
- 投稿日:2020-09-14T12:48:38+09:00
GoでQiitaのトレンドを取得するAPI①作ってみた
はじめに
Qiitaのトレンド記事を取得したいと思ったのですが、公式でAPIが提供されていないので自分で作ってみました。
Qiitaトップページのdivタグにトレンド記事のJSONが入った属性があるので、これをスクレイピングして取得します。
スクレイピングには、goquery、
フレームワークは Gin を使用しています。リポジトリはこちら。
コード
まず、トレンド記事一覧のJSONを取得します。
func qiitaTrend() (string, error) { doc, err := goquery.NewDocument("https://qiita.com/") if err != nil { fmt.Fprintln(os.Stderr, err) return "", err } trend := doc.Find("div[data-hyperapp-app='Trend']") val, exists := trend.Attr("data-hyperapp-props") if !exists { return "", errors.New("Internal Server Error") } return val, nil }順番に見ていきます。
まずはgoquery.NewDocument(url)でドキュメントを取得します
doc, err := goquery.NewDocument("https://qiita.com/")次にdoc.Find(セレクタ)でセレクションを取得します。
セレクタでフィルタリングして一致した要素を取得します。今回取得しようとしている要素は、以下のようになっているのでdata-hyperapp-app="Trend"を指定しています。
<div data-hyperapp-app="Trend" data-hyperapp-props="トレンド記事のJSON" data-hyperapp-mounted="true"> ... </div>trend := doc.Find("div[data-hyperapp-app='Trend']")最後は欲しいデータを抜き取ります。
trend.Attr(属性)でその属性の値を取得します。
existsはその値が存在するかboolで返します。記事一覧のJSONはdata-hyperapp-propsに入っているのでそれを指定します。
val, exists := trend.Attr("data-hyperapp-props")以上でトレンド記事のJSONを取得することができました。
最終的なコードは以下のようになります。package main import ( "errors" "fmt" "net/http" "os" "github.com/PuerkitoBio/goquery" "github.com/gin-gonic/gin" ) func main() { r := gin.New() r.Use(gin.Logger()) r.Use(gin.Recovery()) r.GET("/trend", func(c *gin.Context) { trend, err := qiitaTrend() if err != nil { c.JSON(http.StatusInternalServerError, err) return } c.String(http.StatusOK, trend) return }) r.Run(":8080") } func qiitaTrend() (string, error) { doc, err := goquery.NewDocument("https://qiita.com/") if err != nil { fmt.Fprintln(os.Stderr, err) return "", err } trend := doc.Find("div[data-hyperapp-app='Trend']") val, exists := trend.Attr("data-hyperapp-props") if !exists { return "", errors.New("Internal Server Error") } return val, nil }Ginでのhttpサーバーについては今回は省略します。
実行してcurlで取得すると、
$ curl http://localhost:8080/trend {"trend":{"edges":[{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T07:53:22Z","likesCount":338,"title":"オブジェクト指向は単なる【整理術】だよ","uuid":"2aa9859d4da41991bbb7","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163629/profile-images/1567829792","urlName":"br_branch"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T03:08:08Z","likesCount":104,"title":"間違えて違うブランチで作業を始めてしまったときの対処法【Git】","uuid":"dc21de47c617b0d0eebf","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/profile-images/1598685340","urlName":"paleo_engineer"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T17:26:06Z","likesCount":82,"title":"Pythonでコンソールに綺麗なグラフを描画できる「AsciichartPy」使ってみた。","uuid":"aff5c301aacb8513a11d","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/535270/3e59522dfca791fd0ce7f98d863a9a796ca95a16/large.png?1574159780","urlName":"kkent030315"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T13:59:19Z","likesCount":90,"title":"CPUのキーワード(ブランド、命令セット、マイクロアーキテクチャ、拡張命令セット)の説明","uuid":"656bac4c3871031a4448","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/79744/profile-images/1501125720","urlName":"kaityo256"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T10:57:35Z","likesCount":79,"title":"スプラトゥーン2のプレイ動画から、やられたシーンだけをディープラーニングで自動抽出する","uuid":"acbc0906046bb7b2b1db","author":{"profileImageUrl":"https://avatars3.githubusercontent.com/u/16898831?v=4","urlName":"tfandkusu"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-11T09:35:58Z","likesCount":63,"title":"様々な日本語文章を、目線移動無く早く読めるオープンソースツール(Riot Shield)を公開しました","uuid":"c7e48f9f9b3e937c5dad","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/85111/profile-images/1499606550","urlName":"am_nimitz3"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T13:59:38Z","likesCount":55,"title":"Kubernetesの負荷試験で絶対に担保したい13のチェックリスト","uuid":"56a5442c1fc4d714c941","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45875/profile-images/1595854294","urlName":"taisho6339"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-11T13:14:11Z","likesCount":42,"title":"《コロナに負けず!!》29歳で営業職からジョブチェンジ。未経験で受託開発企業から内定を頂けました。","uuid":"3eafbcc52c04241788a5","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/635167/4df257c74aac0acdbb03f0e7f63e276aa42830a0/x_large.png?1591368347","urlName":"ki-ku"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-13T11:10:37Z","likesCount":93,"title":"kubernetesでもぷよぷよがしたいので同じ色のPodが4個くっついたらdeleteされるcustom controller「くべくべ」を作った","uuid":"c8ff9c824b65ca371cd9","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/38448/profile-images/1473687769","urlName":"yoitomakenouta"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T10:57:47Z","likesCount":36,"title":"データ構造 Fenwick tree (binary indexed tree, BIT) にどっぷりと入門し、その美しき構造に心を洗われたい方のための紹介記事です!","uuid":"7d50ff180a4e5c294cb7","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352395/profile-images/1557632578","urlName":"ngtkana"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T08:22:19Z","likesCount":46,"title":"TypeScriptリポジトリの歩き方","uuid":"a63882c4c42c6ccf8c66","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54872/profile-images/1573246604","urlName":"sisisin"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-12T00:49:28Z","likesCount":32,"title":"【翻訳】wordpressのプラグインで毎月5万収益を得ている話","uuid":"ab1b2b6d5676dc55144b","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705937/profile-images/1599573315","urlName":"kirishima_app"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T09:04:26Z","likesCount":24,"title":"Ractor超入門","uuid":"f9d1917fda770bcdbe2a","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/191521/profile-images/1531325180","urlName":"S_H_"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-13T01:57:50Z","likesCount":23,"title":"音声分析におけるフーリエ変換とスペクトログラムを理解する","uuid":"6af2cc4c4be0c57bef06","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651198/profile-images/1591512068","urlName":"shirowanisan"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T11:29:02Z","likesCount":18,"title":"production-ready なGo製2Dゲームライブラリ Ebiten の紹介 \u0026 リンク集","uuid":"1fd6077327f99245b807","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348791/profile-images/1565355993","urlName":"eihigh"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T08:11:54Z","likesCount":23,"title":"自分のQiita記事をインクリメンタル検索するAlfred Workflowを作ってみた","uuid":"6909b534a59d3550093e","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/profile-images/1596162182","urlName":"ryo2132"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-11T06:43:05Z","likesCount":33,"title":"SQLアンチパターン ファントムファイル","uuid":"cc811929485471897c89","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257405/profile-images/1585098105","urlName":"fktnkit"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T14:56:56Z","likesCount":20,"title":"たった3行でVSCode(codeserver)をGoogle Colabで使う方法","uuid":"ebcc8d51476f6f7c5b72","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/profile-images/1574818910","urlName":"kojikanao"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T13:07:56Z","likesCount":15,"title":"Raspberry PiにVS Code Insider版をインストールする方法","uuid":"fb50b6ac5c175b9b1045","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/81919/profile-images/1549284868","urlName":"karaage0703"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-13T05:42:09Z","likesCount":24,"title":"論文と公共データベースを使って無料で始めるAI創薬","uuid":"a12f7bf594a56a2b22ba","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/329816/ba6cbc8b71c92411f763dc9334212db0a2d0f08f/x_large.png?1589641830","urlName":"kimisyo"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T23:36:29Z","likesCount":18,"title":"Docker × Laravel 8 Jetstream でログイン、ユーザー登録、2要素認証を実装する","uuid":"7824d1293fef4698c212","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/52879/profile-images/1540380605","urlName":"ucan-lab"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T13:32:05Z","likesCount":19,"title":"SwiftUIとCombineの練習がてらmacOS向けのピアノアプリ作ってみた","uuid":"0230a14ec6c3a8174568","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/67153/e88c7b2790238820700627c60482ade9228609f7/x_large.png?1575435001","urlName":"Kyome"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-10T22:39:44Z","likesCount":29,"title":" scikit-learnで機械学習パイプラインをインタラクティブに描画、HTML保存する方法","uuid":"55c7f567ca79b81c36ab","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/191401/185398f529f8156fed141d7b2e1b0a78fc114172/large.png?1599774000","urlName":"sugulu_Ogawa_ISID"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-11T14:02:08Z","likesCount":19,"title":"【技術書典9】ゲーム関連技術頒布物(新刊)まとめ (2020/9/11 22:00 時点)","uuid":"5f535164ee46f7e0c52d","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258608/profile-images/1566747392","urlName":"takehara-ryo"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-13T04:57:19Z","likesCount":26,"title":"FlutterにFirebase(Firestore)を導入したら、iOSのビルドが遅くなった","uuid":"290d61a1731e50201a61","author":{"profileImageUrl":"https://avatars1.githubusercontent.com/u/38914807?v=4","urlName":"sakaihiroaki"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T01:56:00Z","likesCount":14,"title":"PythonでDICOM画像を扱う","uuid":"f145675055796c69e228","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/680528/4c5642d036920f85daf6640335027d9a0387cecc/x_large.png?1595678226","urlName":"asparagasu"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T08:48:31Z","likesCount":13,"title":"Prism の Prism Full App (.NET Core) テンプレートを見てみよう","uuid":"cfbf5c9eaea6c5aed4e1","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/70288/edf16d4d4fdd1abcb3e61b06730b0609cdc77adb/x_large.png?1585128349","urlName":"okazuki"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T23:07:03Z","likesCount":15,"title":"C++上でPythonのコードを実行する(Boost.Pythonの利用)","uuid":"1e2c24e80212a4c4c584","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192805/profile-images/1556565825","urlName":"m4saka"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-10T23:04:14Z","likesCount":9,"title":"sprintf()でSQL文を生成する際、%は%でエスケープできる","uuid":"8df42e2b070bd387f1ee","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/588983/270ed08b6d978f0cf81a04772f20a27246151dda/x_large.png?1589982712","urlName":"Shigeyoshi_Koyama"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":false,"node":{"createdAt":"2020-09-13T09:55:28Z","likesCount":15,"title":"micro:bitプログラミング入門講座テキスト","uuid":"df4b502b43fde7eba35b","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/26536/profile-images/1473684616","urlName":"noanoa07"}}}]},"scope":"daily"}すごく見にくいので整形すると、
{ "trend": { "edges": [ { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T07:53:22Z", "likesCount": 338, "title": "オブジェクト指向は単なる【整理術】だよ", "uuid": "2aa9859d4da41991bbb7", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163629/profile-images/1567829792", "urlName": "br_branch" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T03:08:08Z", "likesCount": 104, "title": "間違えて違うブランチで作業を始めてしまったときの対処法【Git】", "uuid": "dc21de47c617b0d0eebf", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/profile-images/1598685340", "urlName": "paleo_engineer" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T17:26:06Z", "likesCount": 82, "title": "Pythonでコンソールに綺麗なグラフを描画できる「AsciichartPy」使ってみた。", "uuid": "aff5c301aacb8513a11d", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/535270/3e59522dfca791fd0ce7f98d863a9a796ca95a16/large.png?1574159780", "urlName": "kkent030315" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T13:59:19Z", "likesCount": 90, "title": "CPUのキーワード(ブランド、命令セット、マイクロアーキテクチャ、拡張命令セット)の説明", "uuid": "656bac4c3871031a4448", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/79744/profile-images/1501125720", "urlName": "kaityo256" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T10:57:35Z", "likesCount": 79, "title": "スプラトゥーン2のプレイ動画から、やられたシーンだけをディープラーニングで自動抽出する", "uuid": "acbc0906046bb7b2b1db", "author": { "profileImageUrl": "https://avatars3.githubusercontent.com/u/16898831?v=4", "urlName": "tfandkusu" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-11T09:35:58Z", "likesCount": 63, "title": "様々な日本語文章を、目線移動無く早く読めるオープンソースツール(Riot Shield)を公開しました", "uuid": "c7e48f9f9b3e937c5dad", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/85111/profile-images/1499606550", "urlName": "am_nimitz3" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T13:59:38Z", "likesCount": 55, "title": "Kubernetesの負荷試験で絶対に担保したい13のチェックリスト", "uuid": "56a5442c1fc4d714c941", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45875/profile-images/1595854294", "urlName": "taisho6339" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-11T13:14:11Z", "likesCount": 42, "title": "《コロナに負けず!!》29歳で営業職からジョブチェンジ。未経験で受託開発企業から内定を頂けました。", "uuid": "3eafbcc52c04241788a5", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/635167/4df257c74aac0acdbb03f0e7f63e276aa42830a0/x_large.png?1591368347", "urlName": "ki-ku" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-13T11:10:37Z", "likesCount": 93, "title": "kubernetesでもぷよぷよがしたいので同じ色のPodが4個くっついたらdeleteされるcustom controller「くべくべ」を作った", "uuid": "c8ff9c824b65ca371cd9", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/38448/profile-images/1473687769", "urlName": "yoitomakenouta" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T10:57:47Z", "likesCount": 36, "title": "データ構造 Fenwick tree (binary indexed tree, BIT) にどっぷりと入門し、その美しき構造に心を洗われたい方のための紹介記事です!", "uuid": "7d50ff180a4e5c294cb7", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352395/profile-images/1557632578", "urlName": "ngtkana" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T08:22:19Z", "likesCount": 46, "title": "TypeScriptリポジトリの歩き方", "uuid": "a63882c4c42c6ccf8c66", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54872/profile-images/1573246604", "urlName": "sisisin" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-12T00:49:28Z", "likesCount": 32, "title": "【翻訳】wordpressのプラグインで毎月5万収益を得ている話", "uuid": "ab1b2b6d5676dc55144b", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705937/profile-images/1599573315", "urlName": "kirishima_app" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T09:04:26Z", "likesCount": 24, "title": "Ractor超入門", "uuid": "f9d1917fda770bcdbe2a", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/191521/profile-images/1531325180", "urlName": "S_H_" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-13T01:57:50Z", "likesCount": 23, "title": "音声分析におけるフーリエ変換とスペクトログラムを理解する", "uuid": "6af2cc4c4be0c57bef06", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651198/profile-images/1591512068", "urlName": "shirowanisan" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T11:29:02Z", "likesCount": 18, "title": "production-ready なGo製2Dゲームライブラリ Ebiten の紹介 & リンク集", "uuid": "1fd6077327f99245b807", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348791/profile-images/1565355993", "urlName": "eihigh" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T08:11:54Z", "likesCount": 23, "title": "自分のQiita記事をインクリメンタル検索するAlfred Workflowを作ってみた", "uuid": "6909b534a59d3550093e", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/profile-images/1596162182", "urlName": "ryo2132" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-11T06:43:05Z", "likesCount": 33, "title": "SQLアンチパターン ファントムファイル", "uuid": "cc811929485471897c89", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257405/profile-images/1585098105", "urlName": "fktnkit" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T14:56:56Z", "likesCount": 20, "title": "たった3行でVSCode(codeserver)をGoogle Colabで使う方法", "uuid": "ebcc8d51476f6f7c5b72", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/profile-images/1574818910", "urlName": "kojikanao" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T13:07:56Z", "likesCount": 15, "title": "Raspberry PiにVS Code Insider版をインストールする方法", "uuid": "fb50b6ac5c175b9b1045", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/81919/profile-images/1549284868", "urlName": "karaage0703" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-13T05:42:09Z", "likesCount": 24, "title": "論文と公共データベースを使って無料で始めるAI創薬", "uuid": "a12f7bf594a56a2b22ba", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/329816/ba6cbc8b71c92411f763dc9334212db0a2d0f08f/x_large.png?1589641830", "urlName": "kimisyo" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T23:36:29Z", "likesCount": 18, "title": "Docker × Laravel 8 Jetstream でログイン、ユーザー登録、2要素認証を実装する", "uuid": "7824d1293fef4698c212", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/52879/profile-images/1540380605", "urlName": "ucan-lab" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T13:32:05Z", "likesCount": 19, "title": "SwiftUIとCombineの練習がてらmacOS向けのピアノアプリ作ってみた", "uuid": "0230a14ec6c3a8174568", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/67153/e88c7b2790238820700627c60482ade9228609f7/x_large.png?1575435001", "urlName": "Kyome" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-10T22:39:44Z", "likesCount": 29, "title": " scikit-learnで機械学習パイプラインをインタラクティブに描画、HTML保存する方法", "uuid": "55c7f567ca79b81c36ab", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/191401/185398f529f8156fed141d7b2e1b0a78fc114172/large.png?1599774000", "urlName": "sugulu_Ogawa_ISID" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-11T14:02:08Z", "likesCount": 19, "title": "【技術書典9】ゲーム関連技術頒布物(新刊)まとめ (2020/9/11 22:00 時点)", "uuid": "5f535164ee46f7e0c52d", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258608/profile-images/1566747392", "urlName": "takehara-ryo" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-13T04:57:19Z", "likesCount": 26, "title": "FlutterにFirebase(Firestore)を導入したら、iOSのビルドが遅くなった", "uuid": "290d61a1731e50201a61", "author": { "profileImageUrl": "https://avatars1.githubusercontent.com/u/38914807?v=4", "urlName": "sakaihiroaki" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T01:56:00Z", "likesCount": 14, "title": "PythonでDICOM画像を扱う", "uuid": "f145675055796c69e228", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/680528/4c5642d036920f85daf6640335027d9a0387cecc/x_large.png?1595678226", "urlName": "asparagasu" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T08:48:31Z", "likesCount": 13, "title": "Prism の Prism Full App (.NET Core) テンプレートを見てみよう", "uuid": "cfbf5c9eaea6c5aed4e1", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/70288/edf16d4d4fdd1abcb3e61b06730b0609cdc77adb/x_large.png?1585128349", "urlName": "okazuki" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T23:07:03Z", "likesCount": 15, "title": "C++上でPythonのコードを実行する(Boost.Pythonの利用)", "uuid": "1e2c24e80212a4c4c584", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192805/profile-images/1556565825", "urlName": "m4saka" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-10T23:04:14Z", "likesCount": 9, "title": "sprintf()でSQL文を生成する際、%は%でエスケープできる", "uuid": "8df42e2b070bd387f1ee", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/588983/270ed08b6d978f0cf81a04772f20a27246151dda/x_large.png?1589982712", "urlName": "Shigeyoshi_Koyama" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": false, "node": { "createdAt": "2020-09-13T09:55:28Z", "likesCount": 15, "title": "micro:bitプログラミング入門講座テキスト", "uuid": "df4b502b43fde7eba35b", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/26536/profile-images/1473684616", "urlName": "noanoa07" } } } ] }, "scope": "daily" }こんな感じでちゃんと取得できています。
さいごに
goqueryには他にも様々な取得の仕方があるので気になったからは見てみてください。(ドキュメント)
qiitaのトレンド記事を取得したいと思った方は是非使ってみてください!
- 投稿日:2020-09-14T12:48:38+09:00
GoでQiitaのトレンドを取得するAPI
はじめに
Qiitaのトレンド記事を取得したいと思ったのですが、公式でAPIが提供されていないので自分で作ってみました。
Qiitaトップページのdivタグにトレンド記事のJSONが入った属性があるので、これをスクレイピングして取得します。
スクレイピングには、goquery、
フレームワークは Gin を使用しています。(https://github.com/PuerkitoBio/goquery)
リポジトリはこちら。コード
まず、トレンド記事一覧のJSONを取得します。
func qiitaTrend() (string, error) { doc, err := goquery.NewDocument("https://qiita.com/") if err != nil { fmt.Fprintln(os.Stderr, err) return "", err } trend := doc.Find("div[data-hyperapp-app='Trend']") val, exists := trend.Attr("data-hyperapp-props") if !exists { return "", errors.New("Internal Server Error") } return val, nil }順番に見ていきます。
まずはgoquery.NewDocument(url)でドキュメントを取得します
doc, err := goquery.NewDocument("https://qiita.com/")次にdoc.Find(セレクタ)でセレクションを取得します。
セレクタでフィルタリングして一致した要素を取得します。今回取得しようとしている要素は、以下のようになっているのでdata-hyperapp-app="Trend"を指定しています。
<div data-hyperapp-app="Trend" data-hyperapp-props="トレンド記事のJSON" data-hyperapp-mounted="true"> ... </div>trend := doc.Find("div[data-hyperapp-app='Trend']")最後は欲しいデータを抜き取ります。
trend.Attr(属性)でその属性の値を取得します。
existsはその値が存在するかboolで返します。記事一覧のJSONはdata-hyperapp-propsに入っているのでそれを指定します。
val, exists := trend.Attr("data-hyperapp-props")以上でトレンド記事のJSONを取得することができました。
最終的なコードは以下のようになります。package main import ( "errors" "fmt" "net/http" "os" "github.com/PuerkitoBio/goquery" "github.com/gin-gonic/gin" ) func main() { r := gin.New() r.Use(gin.Logger()) r.Use(gin.Recovery()) r.GET("/trend", func(c *gin.Context) { trend, err := qiitaTrend() if err != nil { c.JSON(http.StatusInternalServerError, err) return } c.String(http.StatusOK, trend) return }) r.Run(":8080") } func qiitaTrend() (string, error) { doc, err := goquery.NewDocument("https://qiita.com/") if err != nil { fmt.Fprintln(os.Stderr, err) return "", err } trend := doc.Find("div[data-hyperapp-app='Trend']") val, exists := trend.Attr("data-hyperapp-props") if !exists { return "", errors.New("Internal Server Error") } return val, nil }Ginでのhttpサーバーについては今回は省略します。
実行してcurlで取得すると、
$ curl http://localhost:8080/trend {"trend":{"edges":[{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T07:53:22Z","likesCount":338,"title":"オブジェクト指向は単なる【整理術】だよ","uuid":"2aa9859d4da41991bbb7","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163629/profile-images/1567829792","urlName":"br_branch"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T03:08:08Z","likesCount":104,"title":"間違えて違うブランチで作業を始めてしまったときの対処法【Git】","uuid":"dc21de47c617b0d0eebf","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/profile-images/1598685340","urlName":"paleo_engineer"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T17:26:06Z","likesCount":82,"title":"Pythonでコンソールに綺麗なグラフを描画できる「AsciichartPy」使ってみた。","uuid":"aff5c301aacb8513a11d","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/535270/3e59522dfca791fd0ce7f98d863a9a796ca95a16/large.png?1574159780","urlName":"kkent030315"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T13:59:19Z","likesCount":90,"title":"CPUのキーワード(ブランド、命令セット、マイクロアーキテクチャ、拡張命令セット)の説明","uuid":"656bac4c3871031a4448","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/79744/profile-images/1501125720","urlName":"kaityo256"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T10:57:35Z","likesCount":79,"title":"スプラトゥーン2のプレイ動画から、やられたシーンだけをディープラーニングで自動抽出する","uuid":"acbc0906046bb7b2b1db","author":{"profileImageUrl":"https://avatars3.githubusercontent.com/u/16898831?v=4","urlName":"tfandkusu"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-11T09:35:58Z","likesCount":63,"title":"様々な日本語文章を、目線移動無く早く読めるオープンソースツール(Riot Shield)を公開しました","uuid":"c7e48f9f9b3e937c5dad","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/85111/profile-images/1499606550","urlName":"am_nimitz3"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T13:59:38Z","likesCount":55,"title":"Kubernetesの負荷試験で絶対に担保したい13のチェックリスト","uuid":"56a5442c1fc4d714c941","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45875/profile-images/1595854294","urlName":"taisho6339"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-11T13:14:11Z","likesCount":42,"title":"《コロナに負けず!!》29歳で営業職からジョブチェンジ。未経験で受託開発企業から内定を頂けました。","uuid":"3eafbcc52c04241788a5","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/635167/4df257c74aac0acdbb03f0e7f63e276aa42830a0/x_large.png?1591368347","urlName":"ki-ku"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-13T11:10:37Z","likesCount":93,"title":"kubernetesでもぷよぷよがしたいので同じ色のPodが4個くっついたらdeleteされるcustom controller「くべくべ」を作った","uuid":"c8ff9c824b65ca371cd9","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/38448/profile-images/1473687769","urlName":"yoitomakenouta"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T10:57:47Z","likesCount":36,"title":"データ構造 Fenwick tree (binary indexed tree, BIT) にどっぷりと入門し、その美しき構造に心を洗われたい方のための紹介記事です!","uuid":"7d50ff180a4e5c294cb7","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352395/profile-images/1557632578","urlName":"ngtkana"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T08:22:19Z","likesCount":46,"title":"TypeScriptリポジトリの歩き方","uuid":"a63882c4c42c6ccf8c66","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54872/profile-images/1573246604","urlName":"sisisin"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-12T00:49:28Z","likesCount":32,"title":"【翻訳】wordpressのプラグインで毎月5万収益を得ている話","uuid":"ab1b2b6d5676dc55144b","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705937/profile-images/1599573315","urlName":"kirishima_app"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T09:04:26Z","likesCount":24,"title":"Ractor超入門","uuid":"f9d1917fda770bcdbe2a","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/191521/profile-images/1531325180","urlName":"S_H_"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-13T01:57:50Z","likesCount":23,"title":"音声分析におけるフーリエ変換とスペクトログラムを理解する","uuid":"6af2cc4c4be0c57bef06","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651198/profile-images/1591512068","urlName":"shirowanisan"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T11:29:02Z","likesCount":18,"title":"production-ready なGo製2Dゲームライブラリ Ebiten の紹介 \u0026 リンク集","uuid":"1fd6077327f99245b807","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348791/profile-images/1565355993","urlName":"eihigh"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T08:11:54Z","likesCount":23,"title":"自分のQiita記事をインクリメンタル検索するAlfred Workflowを作ってみた","uuid":"6909b534a59d3550093e","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/profile-images/1596162182","urlName":"ryo2132"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-11T06:43:05Z","likesCount":33,"title":"SQLアンチパターン ファントムファイル","uuid":"cc811929485471897c89","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257405/profile-images/1585098105","urlName":"fktnkit"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T14:56:56Z","likesCount":20,"title":"たった3行でVSCode(codeserver)をGoogle Colabで使う方法","uuid":"ebcc8d51476f6f7c5b72","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/profile-images/1574818910","urlName":"kojikanao"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T13:07:56Z","likesCount":15,"title":"Raspberry PiにVS Code Insider版をインストールする方法","uuid":"fb50b6ac5c175b9b1045","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/81919/profile-images/1549284868","urlName":"karaage0703"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-13T05:42:09Z","likesCount":24,"title":"論文と公共データベースを使って無料で始めるAI創薬","uuid":"a12f7bf594a56a2b22ba","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/329816/ba6cbc8b71c92411f763dc9334212db0a2d0f08f/x_large.png?1589641830","urlName":"kimisyo"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-11T23:36:29Z","likesCount":18,"title":"Docker × Laravel 8 Jetstream でログイン、ユーザー登録、2要素認証を実装する","uuid":"7824d1293fef4698c212","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/52879/profile-images/1540380605","urlName":"ucan-lab"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T13:32:05Z","likesCount":19,"title":"SwiftUIとCombineの練習がてらmacOS向けのピアノアプリ作ってみた","uuid":"0230a14ec6c3a8174568","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/67153/e88c7b2790238820700627c60482ade9228609f7/x_large.png?1575435001","urlName":"Kyome"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-10T22:39:44Z","likesCount":29,"title":" scikit-learnで機械学習パイプラインをインタラクティブに描画、HTML保存する方法","uuid":"55c7f567ca79b81c36ab","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/191401/185398f529f8156fed141d7b2e1b0a78fc114172/large.png?1599774000","urlName":"sugulu_Ogawa_ISID"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":false,"node":{"createdAt":"2020-09-11T14:02:08Z","likesCount":19,"title":"【技術書典9】ゲーム関連技術頒布物(新刊)まとめ (2020/9/11 22:00 時点)","uuid":"5f535164ee46f7e0c52d","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258608/profile-images/1566747392","urlName":"takehara-ryo"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-13T04:57:19Z","likesCount":26,"title":"FlutterにFirebase(Firestore)を導入したら、iOSのビルドが遅くなった","uuid":"290d61a1731e50201a61","author":{"profileImageUrl":"https://avatars1.githubusercontent.com/u/38914807?v=4","urlName":"sakaihiroaki"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T01:56:00Z","likesCount":14,"title":"PythonでDICOM画像を扱う","uuid":"f145675055796c69e228","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/680528/4c5642d036920f85daf6640335027d9a0387cecc/x_large.png?1595678226","urlName":"asparagasu"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":false,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T08:48:31Z","likesCount":13,"title":"Prism の Prism Full App (.NET Core) テンプレートを見てみよう","uuid":"cfbf5c9eaea6c5aed4e1","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/70288/edf16d4d4fdd1abcb3e61b06730b0609cdc77adb/x_large.png?1585128349","urlName":"okazuki"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-12T23:07:03Z","likesCount":15,"title":"C++上でPythonのコードを実行する(Boost.Pythonの利用)","uuid":"1e2c24e80212a4c4c584","author":{"profileImageUrl":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192805/profile-images/1556565825","urlName":"m4saka"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":true,"node":{"createdAt":"2020-09-10T23:04:14Z","likesCount":9,"title":"sprintf()でSQL文を生成する際、%は%でエスケープできる","uuid":"8df42e2b070bd387f1ee","author":{"profileImageUrl":"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/588983/270ed08b6d978f0cf81a04772f20a27246151dda/x_large.png?1589982712","urlName":"Shigeyoshi_Koyama"}}},{"followingLikers":[],"isLikedByViewer":false,"isNewArrival":true,"hasCodeBlock":false,"node":{"createdAt":"2020-09-13T09:55:28Z","likesCount":15,"title":"micro:bitプログラミング入門講座テキスト","uuid":"df4b502b43fde7eba35b","author":{"profileImageUrl":"https://qiita-image-store.s3.amazonaws.com/0/26536/profile-images/1473684616","urlName":"noanoa07"}}}]},"scope":"daily"}すごく見にくいので整形すると、
{ "trend": { "edges": [ { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T07:53:22Z", "likesCount": 338, "title": "オブジェクト指向は単なる【整理術】だよ", "uuid": "2aa9859d4da41991bbb7", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163629/profile-images/1567829792", "urlName": "br_branch" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T03:08:08Z", "likesCount": 104, "title": "間違えて違うブランチで作業を始めてしまったときの対処法【Git】", "uuid": "dc21de47c617b0d0eebf", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/profile-images/1598685340", "urlName": "paleo_engineer" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T17:26:06Z", "likesCount": 82, "title": "Pythonでコンソールに綺麗なグラフを描画できる「AsciichartPy」使ってみた。", "uuid": "aff5c301aacb8513a11d", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/535270/3e59522dfca791fd0ce7f98d863a9a796ca95a16/large.png?1574159780", "urlName": "kkent030315" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T13:59:19Z", "likesCount": 90, "title": "CPUのキーワード(ブランド、命令セット、マイクロアーキテクチャ、拡張命令セット)の説明", "uuid": "656bac4c3871031a4448", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/79744/profile-images/1501125720", "urlName": "kaityo256" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T10:57:35Z", "likesCount": 79, "title": "スプラトゥーン2のプレイ動画から、やられたシーンだけをディープラーニングで自動抽出する", "uuid": "acbc0906046bb7b2b1db", "author": { "profileImageUrl": "https://avatars3.githubusercontent.com/u/16898831?v=4", "urlName": "tfandkusu" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-11T09:35:58Z", "likesCount": 63, "title": "様々な日本語文章を、目線移動無く早く読めるオープンソースツール(Riot Shield)を公開しました", "uuid": "c7e48f9f9b3e937c5dad", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/85111/profile-images/1499606550", "urlName": "am_nimitz3" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T13:59:38Z", "likesCount": 55, "title": "Kubernetesの負荷試験で絶対に担保したい13のチェックリスト", "uuid": "56a5442c1fc4d714c941", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45875/profile-images/1595854294", "urlName": "taisho6339" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-11T13:14:11Z", "likesCount": 42, "title": "《コロナに負けず!!》29歳で営業職からジョブチェンジ。未経験で受託開発企業から内定を頂けました。", "uuid": "3eafbcc52c04241788a5", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/635167/4df257c74aac0acdbb03f0e7f63e276aa42830a0/x_large.png?1591368347", "urlName": "ki-ku" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-13T11:10:37Z", "likesCount": 93, "title": "kubernetesでもぷよぷよがしたいので同じ色のPodが4個くっついたらdeleteされるcustom controller「くべくべ」を作った", "uuid": "c8ff9c824b65ca371cd9", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/38448/profile-images/1473687769", "urlName": "yoitomakenouta" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T10:57:47Z", "likesCount": 36, "title": "データ構造 Fenwick tree (binary indexed tree, BIT) にどっぷりと入門し、その美しき構造に心を洗われたい方のための紹介記事です!", "uuid": "7d50ff180a4e5c294cb7", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352395/profile-images/1557632578", "urlName": "ngtkana" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T08:22:19Z", "likesCount": 46, "title": "TypeScriptリポジトリの歩き方", "uuid": "a63882c4c42c6ccf8c66", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54872/profile-images/1573246604", "urlName": "sisisin" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-12T00:49:28Z", "likesCount": 32, "title": "【翻訳】wordpressのプラグインで毎月5万収益を得ている話", "uuid": "ab1b2b6d5676dc55144b", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705937/profile-images/1599573315", "urlName": "kirishima_app" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T09:04:26Z", "likesCount": 24, "title": "Ractor超入門", "uuid": "f9d1917fda770bcdbe2a", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/191521/profile-images/1531325180", "urlName": "S_H_" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-13T01:57:50Z", "likesCount": 23, "title": "音声分析におけるフーリエ変換とスペクトログラムを理解する", "uuid": "6af2cc4c4be0c57bef06", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651198/profile-images/1591512068", "urlName": "shirowanisan" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T11:29:02Z", "likesCount": 18, "title": "production-ready なGo製2Dゲームライブラリ Ebiten の紹介 & リンク集", "uuid": "1fd6077327f99245b807", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348791/profile-images/1565355993", "urlName": "eihigh" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T08:11:54Z", "likesCount": 23, "title": "自分のQiita記事をインクリメンタル検索するAlfred Workflowを作ってみた", "uuid": "6909b534a59d3550093e", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/profile-images/1596162182", "urlName": "ryo2132" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-11T06:43:05Z", "likesCount": 33, "title": "SQLアンチパターン ファントムファイル", "uuid": "cc811929485471897c89", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257405/profile-images/1585098105", "urlName": "fktnkit" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T14:56:56Z", "likesCount": 20, "title": "たった3行でVSCode(codeserver)をGoogle Colabで使う方法", "uuid": "ebcc8d51476f6f7c5b72", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/profile-images/1574818910", "urlName": "kojikanao" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T13:07:56Z", "likesCount": 15, "title": "Raspberry PiにVS Code Insider版をインストールする方法", "uuid": "fb50b6ac5c175b9b1045", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/81919/profile-images/1549284868", "urlName": "karaage0703" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-13T05:42:09Z", "likesCount": 24, "title": "論文と公共データベースを使って無料で始めるAI創薬", "uuid": "a12f7bf594a56a2b22ba", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/329816/ba6cbc8b71c92411f763dc9334212db0a2d0f08f/x_large.png?1589641830", "urlName": "kimisyo" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-11T23:36:29Z", "likesCount": 18, "title": "Docker × Laravel 8 Jetstream でログイン、ユーザー登録、2要素認証を実装する", "uuid": "7824d1293fef4698c212", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/52879/profile-images/1540380605", "urlName": "ucan-lab" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T13:32:05Z", "likesCount": 19, "title": "SwiftUIとCombineの練習がてらmacOS向けのピアノアプリ作ってみた", "uuid": "0230a14ec6c3a8174568", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/67153/e88c7b2790238820700627c60482ade9228609f7/x_large.png?1575435001", "urlName": "Kyome" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-10T22:39:44Z", "likesCount": 29, "title": " scikit-learnで機械学習パイプラインをインタラクティブに描画、HTML保存する方法", "uuid": "55c7f567ca79b81c36ab", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/191401/185398f529f8156fed141d7b2e1b0a78fc114172/large.png?1599774000", "urlName": "sugulu_Ogawa_ISID" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": false, "node": { "createdAt": "2020-09-11T14:02:08Z", "likesCount": 19, "title": "【技術書典9】ゲーム関連技術頒布物(新刊)まとめ (2020/9/11 22:00 時点)", "uuid": "5f535164ee46f7e0c52d", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258608/profile-images/1566747392", "urlName": "takehara-ryo" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-13T04:57:19Z", "likesCount": 26, "title": "FlutterにFirebase(Firestore)を導入したら、iOSのビルドが遅くなった", "uuid": "290d61a1731e50201a61", "author": { "profileImageUrl": "https://avatars1.githubusercontent.com/u/38914807?v=4", "urlName": "sakaihiroaki" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T01:56:00Z", "likesCount": 14, "title": "PythonでDICOM画像を扱う", "uuid": "f145675055796c69e228", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/680528/4c5642d036920f85daf6640335027d9a0387cecc/x_large.png?1595678226", "urlName": "asparagasu" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": false, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T08:48:31Z", "likesCount": 13, "title": "Prism の Prism Full App (.NET Core) テンプレートを見てみよう", "uuid": "cfbf5c9eaea6c5aed4e1", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/70288/edf16d4d4fdd1abcb3e61b06730b0609cdc77adb/x_large.png?1585128349", "urlName": "okazuki" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-12T23:07:03Z", "likesCount": 15, "title": "C++上でPythonのコードを実行する(Boost.Pythonの利用)", "uuid": "1e2c24e80212a4c4c584", "author": { "profileImageUrl": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192805/profile-images/1556565825", "urlName": "m4saka" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": true, "node": { "createdAt": "2020-09-10T23:04:14Z", "likesCount": 9, "title": "sprintf()でSQL文を生成する際、%は%でエスケープできる", "uuid": "8df42e2b070bd387f1ee", "author": { "profileImageUrl": "https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/588983/270ed08b6d978f0cf81a04772f20a27246151dda/x_large.png?1589982712", "urlName": "Shigeyoshi_Koyama" } } }, { "followingLikers": [], "isLikedByViewer": false, "isNewArrival": true, "hasCodeBlock": false, "node": { "createdAt": "2020-09-13T09:55:28Z", "likesCount": 15, "title": "micro:bitプログラミング入門講座テキスト", "uuid": "df4b502b43fde7eba35b", "author": { "profileImageUrl": "https://qiita-image-store.s3.amazonaws.com/0/26536/profile-images/1473684616", "urlName": "noanoa07" } } } ] }, "scope": "daily" }こんな感じでちゃんと取得できています。
さいごに
goqueryには他にも様々な取得の仕方があるので気になったからは見てみてください。(ドキュメント)
qiitaのトレンド記事を取得したいと思った方は是非使ってみてください!
- 投稿日:2020-09-14T12:44:13+09:00
Firebase AuthenticationとIDトークンと更新トークンとセキュリティの話
初めに
Firebase Authenticationを使用すると簡単にアプリにユーザー認証の仕組みを実装することができます。
自前のWebサーバーでFirebase Authenticationのユーザー認証を検証したい場合、IDトークンをクライアントから送ることで実現できます。
このIDトークンとセキュリティについて記載します。以下の記事も参考にしてください。
JWTでセッション管理してはいけない登場する単語
- IDトークン
ユーザーの情報が含まれたJWT。
有効期限が1時間しかない。
- 更新トークン
IDトークンを取得するために使用するトークン。
Webサーバーでのユーザー認証の流れ
- クライアントはFirebase Backendから更新トークンを使ってIDトークンを取得する。
- クライアントはWebサーバーにIDトークンを送る。
- WebサーバーはIDトークンを検証し、ユーザーを認証する。
IDトークンが漏洩した場合
漏洩したトークンが1時間以上前のものなら期限が切れているのでユーザー認証には使用できません。
1時間以内のものならユーザー認証に使用できてしまうので無効にする必要があります。
更新トークンを無効にすることで間接的にIDトークンも無効になります。注意が必要なのはIDトークンをWebサーバー側で
VerifyIDToken
を使用して検証している場合です。
この場合はIDトークンが無効であっても検証に成功してしまいます。
無効かどうかも含めて判別するためにはVerifyIDTokenAndCheckRevoked
を使用する必要があります。
両者の違いはIDトークンの無効状態をチェックするかどうかですが、無効状態のチェックにはコストがかかります。
(この関数はgoのものですがほかの言語ではチェックするかどうかをフラグで制御します。)// This function uses
VerifyIDToken()
internally to verify the ID token JWT. However, unlike
//VerifyIDToken()
this function must make an RPC call to perform the revocation check.
// Developers are advised to take this additional overhead into consideration when including this
// function in an authorization flow that gets executed often.-- firebase-admin-go のauth.go VerifyIDTokenAndCheckRevokedのコメントより引用
更新トークンが漏洩した場合
更新トークンは有効期限がほぼないのですぐに無効にする必要があります。
RevokeRefreshTokens
を使用することでユーザー単位で更新トークンを無効にすることができます。
更新トークンを無効にした場合でも前述の通りVerifyIDToken
でIDトークンを検証していると成功してしまう点には注意してください。まとめ
IDトークンは期限が1時間ととても短い。
更新トークンを無効にすることでIDトークンも無効にできる。
無効にした場合でもVerifyIDToken
で検証している場合は成功してしまう。
無効状態もチェックする場合はVerifyIDTokenAndCheckRevoked
を使用する。
- 投稿日:2020-09-14T09:14:21+09:00
ISUCON10 予選でやったこと、失敗したこと
初めてのISUCON参加して680点あたりで終わった話です。
eqval
という3人チームでGo
言語で挑みまして、僕はアプリ側を担当しました。? 予選前の準備
自分はインフラ周りはあんまり詳しくなかったので, チームメンバーのnoraさんが全部準備しました。
秘伝のタレとも呼ばれているもの
環境変数, エイリアス, MySQL, nginxの設定ファイルなど
Makefileに各種コマンドのまとめ
- 各種設定ファイルの変更を適
- アプリのbuild, deploy
- slow query log, access logをSlackに送る
- pt-query-digest, alpでlogを集計した結果を送ってます
??? 予選でやったこと
Google Cloud Profiler
pprof
を使うとベンチマークが回るタイミングに合わせて, プロファイリングかけることや
結果ファイルの変換処理などにも手間がかかると判断して, Google Cloud Profilerを使いました。
ISUCON9予選問題で模擬テストをしたときに, かなりわかりやすかったので今回も迷わず入れました。ISUCON10のちょっと改善した結果もちょっと混じっているけどこんな感じ
なぞって検索と物件, 椅子検索処理が累計時間が多いことはわかりますし, SQLを2回以上実行していることがわかります。
(キャプチャでは分かりづらいですが, 詳細を見るとわかります)
ただ, 実行時間はアクセスログでも把握できましたので, Profilerはソースコードのどこを先に見るべきかの目安として使いました。Goのバージョン上げ
既存の
1.14.7
から1.15.1
に上げましたが, 特にベンチマーク点数は変動しませんでした。DBのインスタンス分け
- Server 1 : Webサーバー, アプリ
- Server 2 : DB
- Server 3 : 予備
アプリ修正
なぞって検索の修正 (ベンチマーク結果680)
- GIS関連メソッド呼び出しを1回のみに変更
- 検索結果の上限
変更前for _, estate := range estatesInBoundingBox { ... // 改善 : coordinates.coordinatesToText()は同じ結果なのでforのそとで処理し変数で渡す query := fmt.Sprintf(`SELECT * FROM estate WHERE id = ? AND ST_Contains(ST_PolygonFromText(%s), ST_GeomFromText(%s))`, coordinates.coordinatesToText(), point) err = db.Get(&validatedEstate, query, estate.ID) if err != nil { ... } else { estatesInPolygon = append(estatesInPolygon, validatedEstate) } } var re EstateSearchResponse re.Estates = []Estate{} // 改善 : NazotteLimitが50固定なので, forで50件上限を達したらbreakするように変更 if len(estatesInPolygon) > NazotteLimit { re.Estates = estatesInPolygon[:NazotteLimit] } else { re.Estates = estatesInPolygon }JSONエンコーダーを
francoispqt/gojay
に変更したが, やめたProfilerを見た感じ, DBからのデータ取得よりJSON Encodeに時間がかかっていたので
searchEstates
とsearchChairs
にGoJayでEncodeするように変更しましたが, ベンチマークとProfilerの変動がなかったので取り消しました。もっとパフォーマンスが良いJSONエンコーダーを知っておくべきでした。
DB connection数上限削除
connection数が10になっていたので, 削除して無制限にしましたが, 効果が得られませんでした。
db.SetMaxOpenConns(10)アプリのインスタンス分け
- Server 1 : Webサーバー, アプリ
- Server 2 : DB
- Server 3 : アプリ
一部処理は3番サーバーでするように、アプリの負荷を分散しましたが、ベンチマークの結果は変わりませんでした。
? 失敗したこと
DB replication
DBのCPU使用率が非常に高く、
Server 2
とServer 3
でレプリカし3番はSelect専用にしました。
ベンチマークをまわした結果、
post処理のCSVデータinsert、在庫を一つ減らすupdate処理のあとにデータ同期が終わる前に
get処理でのSlave DBからのselectが走り、不正データになりました。INSERT時はレスポンス前に0.5秒sleepさせるなど色々と試してみましたが, 結果は同じでした。
安い椅子一覧のキャッシュ
getLowPricedChair()
もアクセス数が多かったので、レスポンスを早くするために
安い椅子一覧の結果をメモリ上にキャッシュしました。
insert, update処理があるときにスライスに再格納するようにしました。var StockChairs []Chair func loadChairs() { StockChairs = make([]Chair, 0) query := `SELECT * FROM chair WHERE stock > 0 ORDER BY price ASC, id ASC LIMIT ?` db.Select(&StockChairs, query, Limit) }アプリが動いてWeb上では結果が確認できましたが、ベンチマークでは不正データになり、0点の結果でした。
COUNT(*) SQLの改善
物件, 椅子の検索結果で該当ページデータと, 全ページ数計算用のために
count(*)
を返していたのを一つのSQLにしました。変更前SELECT ... FROM estate WHERE {同じ条件} LIMIT ? OFFSET ?; SELECT COUNT(*) FROM estate WHERE {同じ条件};変更後SELECT ..., estate_cnt.cnt FROM estate JOIN (SELECT COUNT(*) cnt FROM estate WHERE {同じ条件}) estate_cnt WHERE {同じ条件} LIMIT ? OFFSET ?;このときも、アプリは動いてたので、これでよし!と思いましたが
ベンチマークでは不正データと判断されて0点になってしまいました。ついでに、SQLは既存に戻し、COUNT(*)だけ goroutineで並行処理してみてもアプリが動きましたが、ベンチマークは0点になりました。
SQLのOR条件を 5つから1つに減らす (対応できなかった)
自分が
count(*)
問題に時間を使いすぎてしまい、
メンバーから分析して改善余地がある部分だと共有はもらった、
椅子にあうおすすめ物件のOR
を一つに減らすところに対応できませんでした。もっと早い段階で、自分が手こずっていたところを諦めて、先にこの対応をすれば良かったなと後悔します・・
変更前var estates []Estate w := chair.Width h := chair.Height d := chair.Depth query = `SELECT * FROM estate WHERE (door_width >= ? AND door_height >= ?) OR (door_width >= ? AND door_height >= ?) OR (door_width >= ? AND door_height >= ?) OR (door_width >= ? AND door_height >= ?) OR (door_width >= ? AND door_height >= ?) OR (door_width >= ? AND door_height >= ?) ORDER BY popularity DESC, id ASC LIMIT ?` err = db.Select(&estates, query, w, h, w, d, h, w, h, d, d, w, d, h, Limit)? 感想
初めての参加で、自分の知識と力不足をものすごく感じて悔しかったですが、それ以上にすごくドキドキして楽しかった一日でした!
テーブルも2つですごくシンプルで、0.1秒基準のSlow queryもなかったので、ボトルネック探すのが大変でした。
また、本格的にアプリ側で手を動かし始めたのが中盤が超えてからだったので、慌てたり頭が回らなかったりもしました。
競技がおわったあとに気づきましたが、アプリ側でやれることは多かったと思います。
まだまだ、自分がgoになれてなかったのも原因かと思います。インフラの知識をつけることと、もっとgoをすらすら書けるようにして、来年のISUCON11にも参加します!
- 投稿日:2020-09-14T01:37:37+09:00
GoでAtCoder Library (ACL)
GoでAtCoder Library (ACL)のスニペットを作りました。
コピペするだけで使えると思います。
各コードの使い方はAtCoder Library Practice Contestの提出を見てください。注意
動作保証はできないので、使用は自己責任でお願いします。
modintはありません。他にもところどころ実装していないものがあります。
AtCoder Library Practice Contestの全ての問題でACしましたが、ここで使用していない機能は動作確認していないものがあります。実装に問題があったらご指摘いただけると幸いです。
目次
・DSU (Union-Find)
・Fenwick Tree (BIT)
・math (floor sumなど)
・MaxFlow
・MinCostFlow
・Convolution
・SCC
・Two SAT
・strings
・Segtree
・LazySegtree
・bitsDisjoint Set Union (DSU, Union-Find)
UnionFind.gotype UnionFind struct { n int par []int } func newUnionFind(n int) *UnionFind { uf := new(UnionFind) uf.n = n uf.par = make([]int, n) for i, _ := range uf.par { uf.par[i] = -1 } return uf } func (uf UnionFind) root(x int) int { if uf.par[x] < 0 { return x } uf.par[x] = uf.root(uf.par[x]) return uf.par[x] } func (uf UnionFind) unite(x, y int) { rx, ry := uf.root(x), uf.root(y) if rx != ry { if uf.size(rx) > uf.size(ry) { rx, ry = ry, rx } uf.par[ry] += uf.par[rx] uf.par[rx] = ry } } func (uf UnionFind) same(x, y int) bool { return uf.root(x) == uf.root(y) } func (uf UnionFind) size(x int) int { return -uf.par[uf.root(x)] } func (uf UnionFind) groups() [][]int { rootBuf, groupSize := make([]int, uf.n), make([]int, uf.n) for i := 0; i < uf.n; i++ { rootBuf[i] = uf.root(i) groupSize[rootBuf[i]]++ } res := make([][]int, uf.n) for i := 0; i < uf.n; i++ { res[i] = make([]int, 0, groupSize[i]) } for i := 0; i < uf.n; i++ { res[rootBuf[i]] = append(res[rootBuf[i]], i) } result := make([][]int, 0, uf.n) for i := 0; i < uf.n; i++ { if len(res[i]) != 0 { r := make([]int, len(res[i])) copy(r, res[i]) result = append(res, r) } } return result }groups()の動作確認はしていません。
AtCoder Library Practice Contestでの提出・参考
https://qiita.com/ofutonfuton/items/c17dfd33fc542c222396
https://qiita.com/haru1843/items/2295d0ec1f5002bd5c33Fenwick Tree (BIT)
BIT.gotype BIT struct { v []int } func newBIT(n int) *BIT { b := new(BIT) b.v = make([]int, n) return b } func (b BIT) sum(a int) int { ret := 0 for i := a + 1; i > 0; i -= i & -i { ret += b.v[i-1] } return ret } func (b BIT) rangeSum(x, y int) int { if y == 0 { return 0 } y-- if x == 0 { return b.sum(y) } else { return b.sum(y) - b.sum(x-1) } } func (b BIT) add(a, w int) { n := len(b.v) for i := a + 1; i <= n; i += i & -i { b.v[i-1] += w } }ACLのコードをあまり見ていないので、挙動が異なる部分があるかもしれません。
AtCoder Library Practice Contestでの提出・参考
http://hos.ac/slides/20140319_bit.pdfmath
math.gofunc floorSum(n, m, a, b int) int { ret := 0 if a >= m { ret += (n - 1) * n * (a / m) / 2 a %= m } if b >= m { ret += n * (b / m) b %= m } ymx := (a*n + b) / m xmx := ymx*m - b if ymx == 0 { return ret } ret += (n - (xmx+a-1)/a) * ymx ret += floorSum(ymx, a, m, (a-xmx%a)%a) return ret } func crt(r, m []int) [2]int { n := len(r) r0, m0 := 0, 1 for i := 0; i < n; i++ { r1 := safeMod(r[i], m[i]) m1 := m[i] if m0 < m1 { r0, r1 = r1, r0 m0, m1 = m1, m0 } if m0%m1 == 0 { if r0%m1 != r1 { return [2]int{0, 0} } continue } tmp := invGcd(m0, m1) g, im := tmp[0], tmp[1] u1 := m1 / g if (r1-r0)%g != 0 { return [2]int{0, 0} } x := (r1 - r0) / g % u1 * im % u1 r0 += x * m0 m0 *= u1 if r0 < 0 { r0 += m0 } } return [2]int{r0, m0} } func powMod(x, n, m int) int { if m == 1 { return 0 } r := 1 y := x % m if y < 0 { y += m } for n != 0 { if (n & 1) == 1 { r = (r * y) % m } y = (y * y) % m n >>= 1 } return r } func safeMod(x, d int) int { x %= d if x < 0 { x += d } return x } func invMod(x, m int) int { z := invGcd(x, m) return z[1] } func invGcd(a, b int) [2]int { a = a % b if a < 0 { a += b } s, t := b, a m0, m1 := 0, 1 for t != 0 { u := s / t s -= t * u m0 -= m1 * u tmp := s s = t t = tmp tmp = m0 m0 = m1 m1 = tmp } if m0 < 0 { m0 += b / s } return [2]int{s, m0} } func primitiveRoot(m int) int { if m == 2 { return 1 } else if m == 167772161 { return 3 } else if m == 469762049 { return 3 } else if m == 754974721 { return 11 } else if m == 998244353 { return 3 } divs := make([]int, 20) divs[0] = 2 cnt := 1 x := (m - 1) / 2 for (x % 2) == 0 { x /= 2 } for i := 3; i*i <= x; i += 2 { if x%i == 0 { divs[cnt] = i cnt++ for x%i == 0 { x /= i } } } if x > 1 { divs[cnt] = x cnt++ } for g := 2; ; g++ { ok := true for i := 0; i < cnt; i++ { if powMod(g, (m-1)/divs[i], m) == 1 { ok = false break } } if ok { return g } } }AtCoder Library Practice Contestでの提出
MaxFlow
MaxFlow.gotype Edge struct { from int to int capa int flow int } type _Edge struct { to int rev int capa int } type MaxFlow struct { n int pos [][2]int g [][]_Edge } func newMaxFlow(n int) *MaxFlow { return &MaxFlow{ n: n, g: make([][]_Edge, n), } } func (mf *MaxFlow) smaller(a, b int) int { if a < b { return a } return b } func (mf *MaxFlow) AddEdge(from, to, capa int) int { m := len(mf.pos) mf.pos = append(mf.pos, [2]int{from, len(mf.g[from])}) mf.g[from] = append(mf.g[from], _Edge{to, len(mf.g[to]), capa}) mf.g[to] = append(mf.g[to], _Edge{from, len(mf.g[from]) - 1, 0}) return m } func (mf *MaxFlow) GetEdge(i int) Edge { _e := mf.g[mf.pos[i][0]][mf.pos[i][1]] _re := mf.g[_e.to][_e.rev] return Edge{mf.pos[i][0], _e.to, _e.capa + _re.capa, _re.capa} } func (mf *MaxFlow) EdgesList() []Edge { m := len(mf.pos) result := make([]Edge, 0, m) for i := 0; i < m; i++ { result = append(result, mf.GetEdge(i)) } return result } func (mf *MaxFlow) ChangeEdge(i, newCapa, newFlow int) { _e := &mf.g[mf.pos[i][0]][mf.pos[i][1]] _re := &mf.g[_e.to][_e.rev] _e.capa = newCapa - newFlow _re.capa = newFlow } func (mf *MaxFlow) Flow(s, t int) int { return mf.FlowL(s, t, int(1e+18)) } func (mf *MaxFlow) FlowL(s, t, flowLim int) int { level := make([]int, mf.n) iter := make([]int, mf.n) bfs := func() { for i, _ := range level { level[i] = -1 } level[s] = 0 q := make([]int, 0, mf.n) q = append(q, s) for len(q) != 0 { v := q[0] q = q[1:] for _, e := range mf.g[v] { if e.capa == 0 || level[e.to] >= 0 { continue } level[e.to] = level[v] + 1 if e.to == t { return } q = append(q, e.to) } } } var dfs func(v, up int) int dfs = func(v, up int) int { if v == s { return up } res := 0 lv := level[v] for ; iter[v] < len(mf.g[v]); iter[v]++ { e := &mf.g[v][iter[v]] if lv <= level[e.to] || mf.g[e.to][e.rev].capa == 0 { continue } d := dfs(e.to, mf.smaller(up-res, mf.g[e.to][e.rev].capa)) if d <= 0 { continue } mf.g[v][iter[v]].capa += d mf.g[e.to][e.rev].capa -= d res += d if res == up { break } } return res } flow := 0 for flow < flowLim { bfs() if level[t] == -1 { break } for i, _ := range iter { iter[i] = 0 } for flow < flowLim { f := dfs(t, flowLim-flow) if f == 0 { break } flow += f } } return flow } func (mf *MaxFlow) MinCut(s int) []bool { visited := make([]bool, mf.n) q := make([]int, 0, mf.n) q = append(q, s) for len(q) != 0 { p := q[0] q = q[1:] visited[p] = true for _, e := range mf.g[p] { if e.capa > 0 && !visited[e.to] { visited[e.to] = true q = append(q, e.to) } } } return visited }AtCoder Library Practice Contestでの提出
MinCostFlow
MinCostFlow.goimport "container/heap" type MinCostFlow struct { n int pos [][2]int g [][]_Edge } type _Edge struct { to int rev int capa int cost int } type Edge struct { from int to int capa int flow int cost int } func newMinCostFlow(n int) *MinCostFlow { return &MinCostFlow{n: n, g: make([][]_Edge, n)} } func (mcf *MinCostFlow) AddEdge(from, to, capa, cost int) int { m := len(mcf.pos) mcf.pos = append(mcf.pos, [2]int{from, len(mcf.g[from])}) mcf.g[from] = append(mcf.g[from], _Edge{to, len(mcf.g[to]), capa, cost}) mcf.g[to] = append(mcf.g[to], _Edge{from, len(mcf.g[from]) - 1, 0, -cost}) return m } func (mcf *MinCostFlow) GetEdge(i int) Edge { e := mcf.g[mcf.pos[i][0]][mcf.pos[i][1]] re := mcf.g[e.to][e.rev] return Edge{mcf.pos[i][0], e.to, e.capa + re.capa, re.capa, e.cost} } func (mcf *MinCostFlow) Edges() []Edge { m := len(mcf.pos) res := make([]Edge, m) for i := 0; i < m; i++ { res[i] = mcf.GetEdge(i) } return res } func (mcf *MinCostFlow) Flow(s, t int) [2]int { res := mcf.Slope(s, t) return res[len(res)-1] } func (mcf *MinCostFlow) FlowL(s, t, flowLim int) [2]int { res := mcf.SlopeL(s, t, flowLim) return res[len(res)-1] } func (mcf *MinCostFlow) Slope(s, t int) [][2]int { return mcf.SlopeL(s, t, int(1e+18)) } func (mcf *MinCostFlow) SlopeL(s, t, flowLim int) [][2]int { dual, dist := make([]int, mcf.n), make([]int, mcf.n) pv, pe := make([]int, mcf.n), make([]int, mcf.n) vis := make([]bool, mcf.n) dualRef := func() bool { for i := 0; i < mcf.n; i++ { dist[i], pv[i], pe[i] = int(1e+18), -1, -1 vis[i] = false } pq := make(PriorityQueue, 0) heap.Init(&pq) item := &Item{value: s, priority: 0} dist[s] = 0 heap.Push(&pq, item) for pq.Len() != 0 { v := heap.Pop(&pq).(*Item).value if vis[v] { continue } vis[v] = true if v == t { break } for i := 0; i < len(mcf.g[v]); i++ { e := mcf.g[v][i] if vis[e.to] || e.capa == 0 { continue } cost := e.cost - dual[e.to] + dual[v] if dist[e.to]-dist[v] > cost { dist[e.to] = dist[v] + cost pv[e.to] = v pe[e.to] = i item := &Item{value: e.to, priority: dist[e.to]} heap.Push(&pq, item) } } } if !vis[t] { return false } for v := 0; v < mcf.n; v++ { if !vis[v] { continue } dual[v] -= dist[t] - dist[v] } return true } flow, cost, prevCost := 0, 0, -1 res := make([][2]int, 0, mcf.n) res = append(res, [2]int{flow, cost}) for flow < flowLim { if !dualRef() { break } c := flowLim - flow for v := t; v != s; v = pv[v] { c = mcf.Min(c, mcf.g[pv[v]][pe[v]].capa) } for v := t; v != s; v = pv[v] { mcf.g[pv[v]][pe[v]].capa -= c mcf.g[v][mcf.g[pv[v]][pe[v]].rev].capa += c } d := -dual[s] flow += c cost += c * d if prevCost == d { res = res[:len(res)-1] } res = append(res, [2]int{flow, cost}) prevCost = cost } return res } func (mcf *MinCostFlow) Min(a, b int) int { if a < b { return a } return b } type Item struct { value int priority int index int } type PriorityQueue []*Item func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { return pq[i].priority < pq[j].priority } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] pq[i].index = i pq[j].index = j } func (pq *PriorityQueue) Push(x interface{}) { n := len(*pq) item := x.(*Item) item.index = n *pq = append(*pq, item) } func (pq *PriorityQueue) Pop() interface{} { old := *pq n := len(old) item := old[n-1] old[n-1] = nil item.index = -1 *pq = old[0 : n-1] return item } func (pq *PriorityQueue) update(item *Item, value int, priority int) { item.value = value item.priority = priority heap.Fix(pq, item.index) }AtCoder Library Practice Contestでの提出
Convolution
Convolution.goimport "math/bits" func ceilPow2(n int) int { x := 0 for (1 << uint(x)) < n { x++ } return x } func bsf(n uint) int { return bits.TrailingZeros(n) } func primitiveRoot(m int) int { if m == 2 { return 1 } else if m == 167772161 { return 3 } else if m == 469762049 { return 3 } else if m == 754974721 { return 11 } else if m == 998244353 { return 3 } divs := make([]int, 20) divs[0] = 2 cnt := 1 x := (m - 1) / 2 for (x % 2) == 0 { x /= 2 } for i := 3; i*i <= x; i += 2 { if x%i == 0 { divs[cnt] = i cnt++ for x%i == 0 { x /= i } } } if x > 1 { divs[cnt] = x cnt++ } for g := 2; ; g++ { ok := true for i := 0; i < cnt; i++ { if powMod(g, (m-1)/divs[i], m) == 1 { ok = false break } } if ok { return g } } } func powMod(x, n, m int) int { if m == 1 { return 0 } r := 1 y := x % m if y < 0 { y += m } for n != 0 { if (n & 1) == 1 { r = (r * y) % m } y = (y * y) % m n >>= 1 } return r } func butterfly(a []int, prm int) { g := primitiveRoot(prm) n := len(a) h := ceilPow2(n) first := true se := make([]int, 30) if first { first = false es, ies := make([]int, 30), make([]int, 30) cnt2 := bsf(uint(prm - 1)) e := powMod(g, (prm-1)>>uint(cnt2), prm) ie := invGcd(e, prm)[1] for i := cnt2; i >= 2; i-- { es[i-2] = e ies[i-2] = ie e *= e e %= prm ie *= ie ie %= prm } now := 1 for i := 0; i <= cnt2-2; i++ { se[i] = es[i] * now se[i] %= prm now *= ies[i] now %= prm } } for ph := 1; ph <= h; ph++ { w := 1 << uint(ph-1) p := 1 << uint(h-ph) now := 1 for s := 0; s < w; s++ { offset := s << uint(h-ph+1) for i := 0; i < p; i++ { l := a[i+offset] r := a[i+offset+p] * now % prm a[i+offset] = l + r a[i+offset+p] = l - r a[i+offset] %= prm a[i+offset+p] %= prm if a[i+offset+p] < 0 { a[i+offset+p] += prm } } now *= se[bsf(^(uint(s)))] now %= prm } } } func butterflyInv(a []int, prm int) { g := primitiveRoot(prm) n := len(a) h := ceilPow2(n) first := true sie := make([]int, 30) if first { first = false es, ies := make([]int, 30), make([]int, 30) cnt2 := bsf(uint(prm - 1)) e := powMod(g, (prm-1)>>uint(cnt2), prm) ie := invGcd(e, prm)[1] for i := cnt2; i >= 2; i-- { es[i-2] = e ies[i-2] = ie e *= e e %= prm ie *= ie ie %= prm } now := 1 for i := 0; i <= cnt2-2; i++ { sie[i] = ies[i] * now sie[i] %= prm now *= es[i] now %= prm } } for ph := h; ph >= 1; ph-- { w := 1 << uint(ph-1) p := 1 << uint(h-ph) inow := 1 for s := 0; s < w; s++ { offset := s << uint(h-ph+1) for i := 0; i < p; i++ { l := a[i+offset] r := a[i+offset+p] a[i+offset] = l + r a[i+offset+p] = (prm + l - r) * inow a[i+offset] %= prm a[i+offset+p] %= prm } inow *= sie[bsf(^uint(s))] inow %= prm } } } func convMin(a, b int) int { if a < b { return a } return b } func convolution(p, q []int, prm int) []int { n, m := len(p), len(q) if n == 0 || m == 0 { return []int{} } if convMin(n, m) <= 60 { var a, b []int if n < m { n, m = m, n a = make([]int, n) b = make([]int, m) copy(a, q) copy(b, p) } else { a = make([]int, n) b = make([]int, m) copy(a, p) copy(b, q) } ans := make([]int, n+m-1) for i := 0; i < n; i++ { for j := 0; j < m; j++ { ans[i+j] += a[i] * b[j] % prm ans[i+j] %= prm } } return ans } z := 1 << uint(ceilPow2(n+m-1)) a, b := make([]int, z), make([]int, z) for i := 0; i < n; i++ { a[i] = p[i] } for i := 0; i < m; i++ { b[i] = q[i] } butterfly(a, prm) butterfly(b, prm) for i := 0; i < z; i++ { a[i] *= b[i] a[i] %= prm } butterflyInv(a, prm) a = a[:n+m-1] iz := invGcd(z, prm)[1] for i := 0; i < n+m-1; i++ { a[i] *= iz a[i] %= prm } return a } func convolutionLL(a, b []int, prm int) []int { n, m := len(a), len(b) for n != 0 || m != 0 { return []int{} } MOD1 := 754974721 MOD2 := 167772161 MOD3 := 469762049 M2M3 := MOD2 * MOD3 M1M3 := MOD1 * MOD3 M1M2 := MOD1 * MOD2 M1M2M3 := MOD1 * MOD2 * MOD3 i1 := invGcd(MOD2*MOD3, MOD1)[1] i2 := invGcd(MOD1*MOD3, MOD2)[1] i3 := invGcd(MOD1*MOD2, MOD3)[1] c1 := convolution(a, b, MOD1) c2 := convolution(a, b, MOD2) c3 := convolution(a, b, MOD3) c := make([]int, n+m-1) for i := 0; i < n+m-1; i++ { x := 0 x += (c1[i] * i1) % MOD1 * M2M3 x += (c2[i] * i2) % MOD2 * M1M3 x += (c3[i] * i3) % MOD3 * M1M2 t := x % MOD1 if t < 0 { t += MOD1 } diff := c1[i] - t if diff < 0 { diff += MOD1 } offset := []int{0, 0, M1M2M3, 2 * M1M2M3, 3 * M1M2M3} x -= offset[diff%5] c[i] = x } return c } func invGcd(a, b int) [2]int { a = a % b if a < 0 { a += b } s, t := b, a m0, m1 := 0, 1 for t != 0 { u := s / t s -= t * u m0 -= m1 * u tmp := s s = t t = tmp tmp = m0 m0 = m1 m1 = tmp } if m0 < 0 { m0 += b / s } return [2]int{s, m0} }AtCoder Library Practice Contestでの提出
SCC
SCC.gotype SccGraph struct { n int edges [][2]int } type Csr struct { start []int elist []int } func newSccGraph(n int) *SccGraph { scc := new(SccGraph) scc.n = n return scc } func (scc *SccGraph) NumVertices() int { return scc.n } func (scc *SccGraph) AddEdge(from int, to int) { scc.edges = append(scc.edges, [2]int{from, to}) } func (c *Csr) csr(n int, edges [][2]int) { c.start = make([]int, n+1) c.elist = make([]int, len(edges)) for _, e := range edges { c.start[e[0]+1]++ } for i := 1; i <= n; i++ { c.start[i] += c.start[i-1] } counter := make([]int, n+1) copy(counter, c.start) for _, e := range edges { c.elist[counter[e[0]]] = e[1] counter[e[0]]++ } } func (scc *SccGraph) SccIds() (int, []int) { g := new(Csr) g.csr(scc.n, scc.edges) nowOrd, groupNum := 0, 0 visited, low := make([]int, 0, scc.n), make([]int, scc.n) ord, ids := make([]int, scc.n), make([]int, scc.n) for i := 0; i < scc.n; i++ { ord[i] = -1 } var dfs func(v int) dfs = func(v int) { low[v], ord[v] = nowOrd, nowOrd nowOrd++ visited = append(visited, v) for i := g.start[v]; i < g.start[v+1]; i++ { to := g.elist[i] if ord[to] == -1 { dfs(to) low[v] = scc.min(low[v], low[to]) } else { low[v] = scc.min(low[v], ord[to]) } } if low[v] == ord[v] { for { u := visited[len(visited)-1] visited = visited[:len(visited)-1] ord[u] = scc.n ids[u] = groupNum if u == v { break } } groupNum++ } } for i := 0; i < scc.n; i++ { if ord[i] == -1 { dfs(i) } } for i := 0; i < len(ids); i++ { ids[i] = groupNum - 1 - ids[i] } return groupNum, ids } func (scc *SccGraph) min(x, y int) int { if x < y { return x } else { return y } } func (scc *SccGraph) Scc() [][]int { groupNum, ids := scc.SccIds() counts := make([]int, groupNum) for _, x := range ids { counts[x]++ } groups := make([][]int, groupNum) for i := 0; i < groupNum; i++ { groups[i] = make([]int, 0, counts[i]) } for i := 0; i < scc.n; i++ { groups[ids[i]] = append(groups[ids[i]], i) } return groups }AtCoder Library Practice Contestでの提出
Two SAT
TwoSAT.gotype SccGraph struct { n int edges [][2]int } type Csr struct { start []int elist []int } type TwoSat struct { n int answer []bool sccGraph *SccGraph } func newSccGraph(n int) *SccGraph { scc := new(SccGraph) scc.n = n return scc } func (scc *SccGraph) NumVertices() int { return scc.n } func (scc *SccGraph) AddEdge(from int, to int) { scc.edges = append(scc.edges, [2]int{from, to}) } func (c *Csr) csr(n int, edges [][2]int) { c.start = make([]int, n+1) c.elist = make([]int, len(edges)) for _, e := range edges { c.start[e[0]+1]++ } for i := 1; i <= n; i++ { c.start[i] += c.start[i-1] } counter := make([]int, n+1) copy(counter, c.start) for _, e := range edges { c.elist[counter[e[0]]] = e[1] counter[e[0]]++ } } func (scc *SccGraph) SccIds() (int, []int) { g := new(Csr) g.csr(scc.n, scc.edges) nowOrd, groupNum := 0, 0 visited, low := make([]int, 0, scc.n), make([]int, scc.n) ord, ids := make([]int, scc.n), make([]int, scc.n) for i := 0; i < scc.n; i++ { ord[i] = -1 } var dfs func(v int) dfs = func(v int) { low[v], ord[v] = nowOrd, nowOrd nowOrd++ visited = append(visited, v) for i := g.start[v]; i < g.start[v+1]; i++ { to := g.elist[i] if ord[to] == -1 { dfs(to) low[v] = scc.min(low[v], low[to]) } else { low[v] = scc.min(low[v], ord[to]) } } if low[v] == ord[v] { for { u := visited[len(visited)-1] visited = visited[:len(visited)-1] ord[u] = scc.n ids[u] = groupNum if u == v { break } } groupNum++ } } for i := 0; i < scc.n; i++ { if ord[i] == -1 { dfs(i) } } for i := 0; i < len(ids); i++ { ids[i] = groupNum - 1 - ids[i] } return groupNum, ids } func (scc *SccGraph) min(x, y int) int { if x < y { return x } else { return y } } func (scc *SccGraph) Scc() [][]int { groupNum, ids := scc.SccIds() counts := make([]int, groupNum) for _, x := range ids { counts[x]++ } groups := make([][]int, groupNum) for i := 0; i < groupNum; i++ { groups[i] = make([]int, 0, counts[i]) } for i := 0; i < scc.n; i++ { groups[ids[i]] = append(groups[ids[i]], i) } return groups } func newTwoSat(n int) *TwoSat { ts := new(TwoSat) ts.n = n ts.answer = make([]bool, n) ts.sccGraph = newSccGraph(n * 2) return ts } func (ts *TwoSat) AddClause(i int, f bool, j int, g bool) { ts.sccGraph.AddEdge(2*i+ts.judge(f, 0, 1), 2*j+ts.judge(g, 1, 0)) ts.sccGraph.AddEdge(2*j+ts.judge(g, 0, 1), 2*i+ts.judge(f, 1, 0)) } func (ts *TwoSat) Satisfiable() bool { _, id := ts.sccGraph.SccIds() for i := 0; i < ts.n; i++ { if id[i*2] == id[2*i+1] { return false } ts.answer[i] = id[2*i] < id[2*i+1] } return true } func (ts *TwoSat) judge(f bool, a int, b int) int { if f { return a } else { return b } } func (ts *TwoSat) Answer() []bool { return ts.answer }AtCoder Library Practice Contestでの提出
strings
strings.goimport "sort" func saIs(s []int, upper int) []int { n := len(s) if n == 0 { return []int{} } if n == 1 { return []int{0} } if n == 2 { if s[0] < s[1] { return []int{0, 1} } else { return []int{1, 0} } } sa := make([]int, n) ls := make([]bool, n) for i := n - 2; i >= 0; i-- { if s[i] == s[i+1] { ls[i] = ls[i+1] } else { ls[i] = s[i] < s[i+1] } } sumL, sumS := make([]int, upper+1), make([]int, upper+1) for i := 0; i < n; i++ { if !ls[i] { sumS[s[i]]++ } else { sumL[s[i]+1]++ } } for i := 0; i <= upper; i++ { sumS[i] += sumL[i] if i < upper { sumL[i+1] += sumS[i] } } induce := func(lms []int) { for i := 0; i < n; i++ { sa[i] = -1 } buf := make([]int, upper+1) copy(buf, sumS) for _, d := range lms { if d == n { continue } sa[buf[s[d]]] = d buf[s[d]]++ } copy(buf, sumL) sa[buf[s[n-1]]] = n - 1 buf[s[n-1]]++ for i := 0; i < n; i++ { v := sa[i] if v >= 1 && !ls[v-1] { sa[buf[s[v-1]]] = v - 1 buf[s[v-1]]++ } } copy(buf, sumL) for i := n - 1; i >= 0; i-- { v := sa[i] if v >= 1 && ls[v-1] { buf[s[v-1]+1]-- sa[buf[s[v-1]+1]] = v - 1 } } } lmsMap := make([]int, n+1) for i, _ := range lmsMap { lmsMap[i] = -1 } m := 0 for i := 1; i < n; i++ { if !ls[i-1] && ls[i] { lmsMap[i] = m m++ } } lms := make([]int, 0, m) for i := 1; i < n; i++ { if !ls[i-1] && ls[i] { lms = append(lms, i) } } induce(lms) if m != 0 { sortedLms := make([]int, 0, m) for _, v := range sa { if lmsMap[v] != -1 { sortedLms = append(sortedLms, v) } } recS := make([]int, m) recUpper := 0 recS[lmsMap[sortedLms[0]]] = 0 for i := 1; i < m; i++ { l := sortedLms[i-1] r := sortedLms[i] endL, endR := n, n if lmsMap[l]+1 < m { endL = lms[lmsMap[l]+1] } if lmsMap[r]+1 < m { endR = lms[lmsMap[r]+1] } same := true if endL-l != endR-r { same = false } else { for l < endL { if s[l] != s[r] { break } l++ r++ } if l == n || s[l] != s[r] { same = false } } if !same { recUpper++ } recS[lmsMap[sortedLms[i]]] = recUpper } recSa := saIs(recS, recUpper) for i := 0; i < m; i++ { sortedLms[i] = lms[recSa[i]] } induce(sortedLms) } return sa } func suffixArray(s []int, upper int) []int { sa := saIs(s, upper) return sa } func suffixArrayT(s []int) []int { n := len(s) idx := make([]int, n) for i := 0; i < n; i++ { idx[i] = i } sort.Slice(idx, func(l, r int) bool { return s[l] < s[r] }) s2 := make([]int, n) now := 0 for i := 0; i < n; i++ { if i != 0 && s[idx[i-1]] != s[idx[i]] { now++ } s2[idx[i]] = now } return saIs(s2, now) } func suffixArrayS(s string) []int { n := len(s) s2 := make([]int, n) for i := 0; i < n; i++ { s2[i] = int(s[i]) } return saIs(s2, 255) } func lcpArray(s, sa []int) []int { n := len(s) rnk := make([]int, n) for i := 0; i < n; i++ { rnk[sa[i]] = i } lcp := make([]int, n-1) h := 0 for i := 0; i < n; i++ { if h > 0 { h-- } if rnk[i] == 0 { continue } j := sa[rnk[i]-1] for ; j+h < n && i+h < n; h++ { if s[j+h] != s[i+h] { break } } lcp[rnk[i]-1] = h } return lcp } func lcpArrayS(s string, sa []int) []int { n := len(s) s2 := make([]int, n) for i := 0; i < n; i++ { s2[i] = int(s[i]) } return lcpArray(s2, sa) } func zAlgorithm(s []int) []int { n := len(s) if n == 0 { return []int{} } z := make([]int, n) z[0] = 0 for i, j := 1, 0; i < n; i++ { if j+z[j] <= i { z[i] = 0 } else { z[i] = zmin(j+z[j]-i, z[i-j]) } for i+z[i] < n && s[z[i]] == s[i+z[i]] { z[i]++ } if j+z[j] < i+z[i] { j = i } } z[0] = n return z } func zmin(a, b int) int { if a < b { return a } else { return b } } func zAlgorithmS(s string) []int { n := len(s) s2 := make([]int, n) for i := 0; i < n; i++ { s2[i] = int(s[i]) } return zAlgorithm(s2) }ACLでは入力のサイズが小さい場合にはナイーブな実装になっていますが、うまく動かなかったため今回は入力サイズによる場合分けはしていません。
Z-Algorithmは動作確認していません。
AtCoder Library Practice Contestでの提出Segtree
Segtree.gotype E func() int type Merger func(a, b int) int type Compare func(v int) bool type Segtree struct { n int size int log int d []int e E merger Merger } func newSegtree(v []int, e E, m Merger) *Segtree { seg := new(Segtree) seg.n = len(v) seg.log = seg.ceilPow2(seg.n) seg.size = 1 << uint(seg.log) seg.d = make([]int, 2*seg.size) seg.e = e seg.merger = m for i, _ := range seg.d { seg.d[i] = seg.e() } for i := 0; i < seg.n; i++ { seg.d[seg.size+i] = v[i] } for i := seg.size - 1; i >= 1; i-- { seg.Update(i) } return seg } func (seg *Segtree) Update(k int) { seg.d[k] = seg.merger(seg.d[2*k], seg.d[2*k+1]) } func (seg *Segtree) Set(p, x int) { p += seg.size seg.d[p] = x for i := 1; i <= seg.log; i++ { seg.Update(p >> uint(i)) } } func (seg *Segtree) Get(p int) int { return seg.d[p+seg.size] } func (seg *Segtree) Prod(l, r int) int { sml, smr := seg.e(), seg.e() l += seg.size r += seg.size for l < r { if (l & 1) == 1 { sml = seg.merger(sml, seg.d[l]) l++ } if (r & 1) == 1 { r-- smr = seg.merger(seg.d[r], smr) } l >>= 1 r >>= 1 } return seg.merger(sml, smr) } func (seg *Segtree) AllProd() int { return seg.d[1] } func (seg *Segtree) MaxRight(l int, cmp Compare) int { if l == seg.n { return seg.n } l += seg.size sm := seg.e() for { for l%2 == 0 { l >>= 1 } if !cmp(seg.merger(sm, seg.d[l])) { for l < seg.size { l = 2 * l if cmp(seg.merger(sm, seg.d[l])) { sm = seg.merger(sm, seg.d[l]) l++ } } return l - seg.size } sm = seg.merger(sm, seg.d[l]) l++ if l&-l == l { break } } return seg.n } func (seg *Segtree) MinLeft(r int, cmp Compare) int { if r == 0 { return 0 } r += seg.size sm := seg.e() for { r-- for r > 1 && r%2 != 0 { r >>= 1 } if !cmp(seg.merger(seg.d[r], sm)) { for r < seg.size { r = 2*r + 1 if cmp(seg.merger(seg.d[r], sm)) { sm = seg.merger(seg.d[r], sm) r-- } } return r + 1 - seg.size } sm = seg.merger(seg.d[r], sm) if r&-r == r { break } } return 0 } func (seg *Segtree) ceilPow2(n int) int { x := 0 for (1 << uint(x)) < n { x++ } return x }int型にしか対応していません。
MinLeftは動作確認していません。
AtCoder Library Practice Contestでの提出LazySegtree
LazySegtree.gotype S struct { a int size int } type F struct { a int b int } type E func() S type Merger func(a, b S) S type Mapper func(f F, x S) S type Comp func(f, g F) F type Id func() F type Compare func(v S) bool type LazySegtree struct { n int size int log int d []S lz []F e E merger Merger mapper Mapper comp Comp id Id } func newLazySegtree(v []S, e E, merger Merger, mapper Mapper, comp Comp, id Id) *LazySegtree { lseg := new(LazySegtree) lseg.n = len(v) lseg.log = lseg.ceilPow2(lseg.n) lseg.size = 1 << uint(lseg.log) lseg.d = make([]S, 2*lseg.size) lseg.e = e lseg.lz = make([]F, lseg.size) lseg.merger = merger lseg.mapper = mapper lseg.comp = comp lseg.id = id for i, _ := range lseg.d { lseg.d[i] = lseg.e() } for i, _ := range lseg.lz { lseg.lz[i] = lseg.id() } for i := 0; i < lseg.n; i++ { lseg.d[lseg.size+i] = v[i] } for i := lseg.size - 1; i >= 1; i-- { lseg.Update(i) } return lseg } func (lseg *LazySegtree) Update(k int) { lseg.d[k] = lseg.merger(lseg.d[2*k], lseg.d[2*k+1]) } func (lseg *LazySegtree) AllApply(k int, f F) { lseg.d[k] = lseg.mapper(f, lseg.d[k]) if k < lseg.size { lseg.lz[k] = lseg.comp(f, lseg.lz[k]) } } func (lseg *LazySegtree) Push(k int) { lseg.AllApply(2*k, lseg.lz[k]) lseg.AllApply(2*k+1, lseg.lz[k]) lseg.lz[k] = lseg.id() } func (lseg *LazySegtree) Set(p int, x S) { p += lseg.size for i := lseg.log; i <= 1; i-- { lseg.Push(p >> uint(i)) } lseg.d[p] = x for i := 1; i <= lseg.log; i++ { lseg.Update(p >> uint(i)) } } func (lseg *LazySegtree) Get(p int) S { p += lseg.size for i := lseg.log; i >= 1; i-- { lseg.Push(p >> uint(i)) } return lseg.d[p] } func (lseg *LazySegtree) Prod(l, r int) S { if l == r { return lseg.e() } l += lseg.size r += lseg.size for i := lseg.log; i >= 1; i-- { if (l>>uint(i))<<uint(i) != l { lseg.Push(l >> uint(i)) } if (r>>uint(i))<<uint(i) != r { lseg.Push(r >> uint(i)) } } sml, smr := lseg.e(), lseg.e() for l < r { if (l & 1) == 1 { sml = lseg.merger(sml, lseg.d[l]) l++ } if (r & 1) == 1 { r-- smr = lseg.merger(lseg.d[r], smr) } l >>= 1 r >>= 1 } return lseg.merger(sml, smr) } func (lseg *LazySegtree) AllProd() S { return lseg.d[1] } func (lseg *LazySegtree) Apply(p int, f F) { p += lseg.size for i := lseg.log; i >= 1; i-- { lseg.Push(p >> uint(i)) } lseg.d[p] = lseg.mapper(f, lseg.d[p]) for i := 1; i <= lseg.log; i++ { lseg.Update(p >> uint(i)) } } func (lseg *LazySegtree) RangeApply(l int, r int, f F) { if l == r { return } l += lseg.size r += lseg.size for i := lseg.log; i >= 1; i-- { if (l>>uint(i))<<uint(i) != l { lseg.Push(l >> uint(i)) } if (r>>uint(i))<<uint(i) != r { lseg.Push((r - 1) >> uint(i)) } } l2, r2 := l, r for l < r { if l&1 == 1 { lseg.AllApply(l, f) l++ } if r&1 == 1 { r-- lseg.AllApply(r, f) } l >>= 1 r >>= 1 } l, r = l2, r2 for i := 1; i <= lseg.log; i++ { if (l>>uint(i))<<uint(i) != l { lseg.Update(l >> uint(i)) } if (r>>uint(i))<<uint(i) != r { lseg.Update((r - 1) >> uint(i)) } } } func (lseg *LazySegtree) MaxRight(l int, cmp Compare) int { if l == lseg.n { return lseg.n } l += lseg.size for i := lseg.log; i >= 1; i-- { lseg.Push(l >> uint(i)) } sm := lseg.e() for { for l%2 == 0 { l >>= 1 } if !cmp(lseg.merger(sm, lseg.d[l])) { for l < lseg.size { lseg.Push(l) l = 2 * l if cmp(lseg.merger(sm, lseg.d[l])) { sm = lseg.merger(sm, lseg.d[l]) l++ } } return l - lseg.size } sm = lseg.merger(sm, lseg.d[l]) l++ if l&-l == l { break } } return lseg.n } func (lseg *LazySegtree) MinLeft(r int, cmp Compare) int { if r == 0 { return 0 } r += lseg.size for i := lseg.log; i >= 1; i-- { lseg.Push(r - 1>>uint(i)) } sm := lseg.e() for { r-- for r > 1 && r%2 != 0 { r >>= 1 } if !cmp(lseg.merger(lseg.d[r], sm)) { for r < lseg.size { lseg.Push(r) r = 2*r + 1 if cmp(lseg.merger(lseg.d[r], sm)) { sm = lseg.merger(lseg.d[r], sm) r-- } } return r + 1 - lseg.size } sm = lseg.merger(lseg.d[r], sm) if r&-r == r { break } } return 0 } func (lseg *LazySegtree) ceilPow2(n int) int { x := 0 for (1 << uint(x)) < n { x++ } return x }MinLeftとMaxRightは動作確認していません。
AtCoder Library Practice Contestでの提出
AtCoder Library Practice Contestでの提出bits
bits.goimport "math/bits" func ceilPow2(n int) int { x := 0 for (1 << uint(x)) < n { x++ } return x } func bsf(n uint) int { return bits.TrailingZeros(n) }以上になります。
各関数の使い方はACLのドキュメントと照らし合わせるとだいたい分かると思いますが、後日追記するかもしれません。
- 投稿日:2020-09-14T01:02:49+09:00
Golang基礎 + MacにGoをインストール
はじめに
Golang又はGo言語 とは
・コンパイル言語の中でも特に処理が高速
・2009年にGoogle開発された
・シンプルな構文
・コードの可読性が高い
・初心者はpythonやphpやRubyからのが良い勉強中なので間違っていたら教えてください。
開発環境
Visual Studio Code
MacGoのインストール
ターミナルで実行してください
1. homebrewを最新にします$ brew update2. Goのインストール
$ brew install go3. Goがインストールされたか確認バージョンが返ってくればOK
$ go versionプログラムの基本構造
順次進行
上から順に処理していく
条件分岐
特定の条件の時にAかBなどに分岐する
繰り返し
決まった回数や条件に当てはまる間は繰り返す
Goの記述方法
例)test.gopackage main ←Goのプログラムは1つはmainに属している必要がある import ("fmt") ←fmtパッケージをインストールしてる func main(){ ←関数を記述 mainからスタートする fmt.Println("hello") ←これはfmtパッケージを用いてhelloを表示する } 波かっこ内はブロックというGoのプログラムはなんらかのパッケージに属している必要があります
実行するにはターミナルに
$ go run ファイル名.go 上記の例だと $ go run test.go と記述これでhelloが表示されます。
変数の宣言仕方
例)test.gopackage main func main(){ var num int ←(var 変数名 型) num = 1 ←1を代入 fmt.Println(num) }実行すると1が表示される
宣言の省略すると
例)test.gopackage main func main(){ num := 1 ←宣言の省略書き方 ↑は var num int ←(var 変数名 型) num = 1 この2行と一緒 fmt.Println(num) }実行すると1が表示される
Goのデータ型
int 整数
float64 小数
string 文字列
bool turnかfalseが入る配列の宣言
変数 := [要素数]データ型{データ1、データ2、...}
例)test.gopackage main func main(){ a := [3]string{"sasa","sumika","takako"} ←配列の宣言 fmt.Println(a[0]) fmt.Println(a[1]) fmt.Println(a[2]) }実行すると
こうなりますsasa sumika takako配列の要素数の省略、変更
例)test.gopackage main func main(){ a := [...]string{"sasa","sumika","takako"} ←配列の宣言[...]で要素数を省略できる a[0] = "aki" fmt.Println(a[0]) fmt.Println(a[1]) fmt.Println(a[2]) }実行するとsasaがakiに変更されています
aki sumika takako条件分岐
例)test.gopackage main func main(){ if a := 0 ; a >= 20{ fmt.Println("20以上") } else if a == 0{ fmt.Println("0です") } else { fmt.Println("条件以外") }実行すると以下が表示されます。
0です繰り返しfor
for 変数 := 初期値; 条件; 増減式{}
例)test.gopackage main func main(){ for i := 0; i<=4; i++ { fmt.Println(i) }実行すると以下が表示されます。
0 1 2 3 4関数定義
func 関数名(引数の定義なくても良い){
retuen ←戻り値がいるなら
}例)test.gopackage main func say(){ ←引数なし fmt.Println("hello") } func say1(say string){ ←引数あり (変数 データ型) fmt.Println(say) } func say2(x,y int) int { ←引数戻り値1つあり (変数 データ型) 戻り値のデータ型{ return (x+y) } func say3(x,y int) (int, int) { ←引数戻り値2つあり (変数 データ型) 戻り値のデータ型{ return (x+y), (x-y) } func main(){ say() ←引数なし say1("hi") ←引数あり result := say2(5,3) ←引数戻り値1つあり fmt.Println(result) result2, result3 := say3(5,2) ←引数戻り値2つあり fmt.Println(result2, result3) }実行すると以下が表示されます。
hello hi 8 7 3構造体を定義
type 構造体名 struct {
フィールド
}例)test.gopackage main type Dog struct { ←構造体 name string age int } func main(){ var s Dog ←構造体の初期化 (var 変数名 構造体名) s.name = "poko" s.age = 5 省略バージョン上記と同じ意味 s := Dog{"poko",5} 一部部分に代入 s := Dog{name: "poko} 又は s := Dog{age: 5} fmt.Println(s) }実行すると以下が表示されます。
{ poko 5 }メソッド
例)test.gopackage main type Dog struct { ←構造体 name string age int } func (s Dog) aa() { ←メソッドと構造体を関連付け (変数 構造体名) 変数() { fmt.Println(s.name,s.age) } こうする事で、構造体にアクセスできるようになる。 s.nameは構造体Dogのnameにアクセスして情報を取得できる func (s Dog) aa2(name string, age int) { ←引数あり fmt.Println(name,age) } func (s Dog) aa3(age int) int { ←引数あり戻り値あり return age*2 func main(){ a1 := Dog{"poko",5} a1.aa() ←引数なし a1.aa2("domu",6) ←引数あり result := a1.aa3(2) ←引数あり戻り値あり fmt.Println(result) }実行すると以下が表示されます。
poko 5 domu 6 4