- 投稿日:2019-03-30T22:11:56+09:00
Go で複数桁の数字を slice に入れたい時、そして reverse させたい時
Go で int と slice をいい感じに扱いたい
こんなん考えたらわかるやろって感じの内容なのかもしれませんが、ググってもいい感じのが出なかったのでメモとして残しておきます。
競技プログラミングとかで使えるかも!
Go で int を1桁ずつ slice に入れる
見てみればなんだこんなもんかって感じです。除算と余りをいい感じに使う再帰関数になってます。
func digit(i int, list []int) []int { if i > 0 { return digit(i/10, append(list, i%10)) } return list }Go で int の slice を逆転したい
作った int の slice を reverse したいときもありますよね?
そういうときはこれです。ループで頑張って入れ替えます。func reverse(numbers []int) []int { for i := 0; i < len(numbers)/2; i++ { j := len(numbers) - i - 1 numbers[i], numbers[j] = numbers[j], numbers[i] } return numbers }int を 1 桁ずつ slice にして逆転する
上記の2つの関数を合わせてこんな感じで使います。
地道にやればなんでもいけますね!package main import ( "fmt" ) func main() { result := 708 var list []int fmt.Println(digit(result, list)) } func digit(i int, list []int) []int { if i > 0 { return digit(i/10, append(list, i%10)) } return reverse(list) } func reverse(numbers []int) []int { for i := 0; i < len(numbers)/2; i++ { j := len(numbers) - i - 1 numbers[i], numbers[j] = numbers[j], numbers[i] } return numbers }
- 投稿日:2019-03-30T19:19:42+09:00
プログラミング言語Go 第6章 メモ
「メソッド」
書籍:プログラミング言語Go
第6章 「メソッド」 の要点と思われる箇所を自分のメモ用にまとめました。6.1 メソッド宣言
- この本では、オブジェクトはメソッドを持つ単なる値あるいは変数で、メソッドは特定の方に関連付けられた関数。
- メソッドは普通の関数宣言の変形で宣言され、そこでは関数名の前に追加のパラメータが書かれる。そのパラメータが当のパラメータの型へ関数を結びつける。
type Point struct { X, Y float64 } func Distance(p, q Point) float64 { // 昔ながらの関数 return math.Hypot(q.X-p.X, q.Y-p.Y) } func (p Point) Distance(q Point) float64 // Point型のメソッド return math.Hypot(q.X-p.X, q.Y-p.Y) } p := Point{1, 2} q := Point{4, 6} fmt.Println(p.Distance(q)) // "5", メソッド呼び出し。
- 上記メソッドの p はレシーバと呼ばれる。Goではレシーバに対し、this や self などの特別な名前は使わない。
- レシーバ名は頻繁に使われるので短くメソッド間で一貫するのが望ましく、Pointに対する p など、型名の頭文字などよく利用される。
- メソッドとフィールドは同じ名前空間に存在するため、メソッド名とフィールド名が同一のものを定義すると曖昧となりコンパイルエラーとなる。
- スライス型などもtype宣言で名前を付けることができ、メソッドを関連付けることができる。この点は他言語と異なる点。
type Path []Point func (path Path) Distance() float64 { // pathに沿って進んだ距離を返す。 sum := 0.0 for i := range path { if i > 0 { sum += path[i-1].Distance(path[i]) } } return sum }
- マップや関数も名前付き型にすればメソッド定義できる。基底型がポインタかインタフェースでない限り同一パッケージ内の名前付き方にはメソッド宣言できる。
- 関数でなくメソッドを使う利点の一つは型の名前があるのでメソッド名を短くできる点。パッケージ外からの呼び出しでこの利点は増大する。
6.2 ポインタレシーバを持つメソッド
- Pointなどのポインタ型にもメソッドを結び付けられる。以下のメソッドの名前は(Point).ScaleBy(()をつけないと*(Point.ScaleBy)と解釈されるので注意)。
func (p *Point) ScaleBy(factor float64) { p.X *= factor p.Y *= factor }
- 慣習として、Pointのどれかのメソッドがポインタレシーバを持つ場合、Pointの すべて のメソッドはポインタレシーバを持つべき。
- 名前付き型 (Point) とその型へのポインタ (Point) は、レシーバ宣言に書くことができる。曖昧さを避けるため、それ自身がポインタの名前付き型はメソッド宣言できない。*
type P *int func (P) f() { ... } // コンパイルエラー: 不正なレシーバ型
- ポインタのレシーバの利用方法は以下。最後の呼び出しもOKで、コンパイラが暗黙で&pを行う。Pointのメソッドを*Pointで呼び出すことも可能。
r := &Point{1, 2} r.ScaleBy(2) p := Point{1, 2} pptr := &p pptr.ScaleBy(2) (&p).ScaleBy(2) p.ScaleBy(2) // コンパイラが暗黙で&pを利用してくれる。変数 (アドレス化可能) の場合のみ暗黙の変換をしてくれる。
- アドレス化可能でない Point レシーバに対して *Point を呼び出すことはできない、
Point{1, 2}.ScaleBy(2) // コンパイルエラー: Pointリテラルのアドレスは得られない。
- 以下が成立すればメソッド呼び出しが可能。
- レシーバ引数がレシーバパラメータと同じ型。
- レシーバ引数が型 T の変数で、レシーバパラメータが型 *T である。(コンパイラが暗黙でその変数のアドレスを得る。)
- レシーバ引数の型が *T で、レシーバパラメータは型 T である。(コンパイラは暗黙的にレシーバの指す値を参照する。)
- 名前付き型 T のすべてのメソッドが T (*Tでなく)のレシーバを持つならその型のインスタンスをコピーすることは安全。メソッド呼び出しではコピーが行われるから。
- どれかのメソッドがポインタレシーバをもっていると、その名前付き型のインスタンスコピーは避けるべき。内部の不変式を破る可能性があるから。(コピーしたインスタンスがコピー元のインスタンス操作で変更されうるので。)
6.2.1 nil は正当なレシーバ値
- 特にマップやスライスのように nil がその型のゼロ値を意味する場合などあるため、nil をレシーバとして利用することは許されている。
func (list *IntList) Sum() int { if list == nil { return 0 } ...
- レシーバ値として nil を許すメソッドを持つ型を定義する場合、ドキュメンテーションコメントで明示すべき。
■6.3 構造体埋め込みによる型の合成
- 構造体を埋め込んだ(無名フィールドを利用)構造体は、埋め込まれた構造体のフィールドに直接アクセスできる。埋め込まれた構造体のメソッドは埋め込んだ構造体に 格上げ される。type Point struct{ X, Y float64 } func (p *Point) ScaleBy(factor float64) { p.X *= factor p.Y *= factor } type ColoredPoint struct { Point // 無名フィールドとして Point を利用。 Color color.RGBA } var cp ColoredPoint cp.X = 1 // OK fmt.Println(cp.Point.X) // "1" cp.ScaleBy(2) // OK
- ColoredPoint と Point の関係は "is a"(継承関係) ではなく "has a"。Point を引数にする関数やメソッドに ColoredPoint は使えない。
- 無名フィールドの型は名前付き型へのポインタでもよい。その場合もフィールドとメソッドは格上げされる。
- 埋め込みのおかげで名前なし構造体型がメソッドを持つことができる。以下の例は機能的に同じ実装だが、後者のほうが可読性が高くなる。
var ( mu sync.Mutex mapping = make(map[string]string) ) func Lookup(key string) string { mu.Lock() v := mapping[key] mu.Unlock() return v } var cache = struct { // 名前なし構造体 sync.Mutex // sync.Mutex を埋め込み。 mapping map[string]string } { mapping: make(map[string]string) } func Lookup(key string) string { cache.Lock() v := cache.mapping[key] cache.Unlock() return v }6.4 メソッド値とメソッド式
- p.ScaleByなどは特定のレシーバ p に結びついたメソッド値と呼ぶ。メソッド値を利用するとレシーバ値なしで関数呼び出しできる。
distanceFromP = p.Distance fmt.Println(distanceFromP(q)) // p.Distance(q) と同じ呼び出し。
- 名前付き構造体 T があり、fというメソッドを持つ場合、T.f または (*T).f をメソッド式と呼ぶ。レシーバの代わりをする普通の第一パラメータを持つ関数値を生成する。
distance := Point.Distance fmt.Println(distance(p, q)) // p.Distance(q) と同じ呼び出し。6.5 例:ビットベクタ型
- ビットベクタは符号なし整数値のスライス「複数ワード」を使う。個々のビットはセット内の可能な要素を表す。そのセットは i 番目のビットが設定されていれば i を含む。
- 演習問題 6.5 「64で割る代わりにunitの」は uint の間違い??
6.6 カプセル化
- Goは名前の可視性を制御する仕組みは一つしか持たない。大文字始まりだとパッケージ外へ公開され、大文字で始まっていない場合は公開されない。
- カプセル化は三つの利点を提供する。
- クライアントがオブジェクトの変数を直接修正できないためオブジェクトの変数が取り得る値を理解するために調べるコードが少なくなる。
- 詳細な実装の隠蔽によりクライアントは変更されるかもしれない事柄に依存しなくなり、APIの互換性を破ることなく実装を発展させる大きな自由を設計者へ与える。
- クライアントがオブジェクトの変数を勝手に設定するのを防ぐ。関数経由で変数を設定させるので、オブジェクトの内部的な不変式を維持することが保証できる。
- フィールドが一旦公開されたらAPIに対する互換性を失うことなく非公開にすることはできない。そのため最初の選択は慎重に検討されるべき。
練習問題解答例
https://github.com/tsuyuzaki/go/tree/master/GoProgrammingLanguage/chapter06
- 投稿日:2019-03-30T18:39:11+09:00
はてなブログの歴代一位をまとめて見れるサイトを作った
はじめに
はてなブログのテクノロジーカテゴリをよく見ています。
今までの歴代一位がどのようなものだったか気になって一覧で見れるサイトを作りました。https://hatena-hotentry.netlify.com/
使った技術
- serverlessFramework
- go
- goquery
- RealtimeDatabase
- lambda
- vue
バッチ
はてなブログのデータを取得するスクリプトをバッチで動かしています。
はてなブログをスクレイピングするコード(go)を書き、ServerlessFrameworkを使ってlambdaにアップロードしています。
ServerlessFrameworkはserverless.ymlにcronを定義しておけば、cloudWatchを使ったlambdaのスケジュール実行を設定してくれるのでお手軽にバッチ処理が作れます。参考:https://dev.classmethod.jp/etc/serverless-framework-lambda-cron-execute/
スクレイピングしたデータを保存する場所としてはfirebaseのRealtimeDatabaseを使いました。
初めはCloudFirestoreの方に保存していたのですが、保存しているドキュメント数が5000近くになり、無料プランでの一日のドキュメント読み取り数が50000だったため、10回全件取得すると、その日はデータ取得ができなくなる状態になりました。
そのため、RealtimeDatabaseに乗り換えましたが、NoSQLにデータを保存する際の設計がわかっていれば、CloudFirestoreでもうまくできたような気がします。フロント
vueを使いました。vue-cliで雛形を作り、それをいじって作っています。
デプロイにはnetlifyを使っています。最後に
goとvueを勉強するために作りましたが、こういう小さいアプリケーションを色々作っていきたい気持ちです。
- 投稿日:2019-03-30T18:04:51+09:00
阿部寛をWebDriverでいじくる
なにこれ
GoでWebDriverを使いたくて、試しに阿部寛のホームページでも回遊するかー。と軽いノリで手をつけたらちょいハマったのでメモ。
最終的に?みたいなプログラムができた。この記事読んで得られるモノ
- 阿部寛のホームページでメンズノンノ時代の阿部寛のスクショを取れるようになる
framesetおよびframeが利用されているwebページを操作できるようになる- Go言語でブラウザ操作を行えるようになる
必要なもの
macなら以下でChromeDriverとAgoutiをインストールできます。簡単ね。
brew install chromedriver go get github.com/sclevine/agouti回遊する方法
以下コードで回遊できます。
package main import ( "log" "github.com/sclevine/agouti" ) func main() { driver := agouti.ChromeDriver() // headlessにする場合はこっちを使う // driver := agouti.ChromeDriver( // agouti.ChromeOptions("args", []string{"--headless", "--disable-gpu", "--no-sandbox"}), // ) if err := driver.Start(); err != nil { log.Fatalf("driverの起動に失敗しました : %v", err) } defer driver.Stop() page, err := driver.NewPage(agouti.Browser("chrome")) if err != nil { log.Fatalf("セッション作成に失敗しました : %v", err) } // 阿部寛のウェブページに遷移する if err := page.Navigate("http://abehiroshi.la.coocan.jp/"); err != nil { log.Fatalf("阿部寛になにかあったかもしれません : %v", err) } // 写真集のリンクを検索する // framesetの中の要素を検索するには一旦該当のフレームにフォーカスしなければならない // 左側のフレームにフォーカスする if err := page.FindByXPath("/html/frameset/frame[1]").SwitchToFrame(); err != nil { log.Fatalf("阿部寛の左側frameにフォーカスできませんでした : %v", err) } // 「写真集」をクリック if err := page.FindByXPath("html/body/table/tbody/tr[10]/td[3]/p/a").Click(); err != nil { log.Fatalf("阿部寛の写真集が見つかりませんでした : %v", err) } // フレームのフォーカス外すためrootにもどる if err := page.SwitchToRootFrame(); err != nil { log.Fatalf("しょうも無いエラーが発生しました : %v", err) } // 右側のフレームにフォーカスする if err := page.FindByXPath("/html/frameset/frame[2]").SwitchToFrame(); err != nil { log.Fatalf("阿部寛の右側frameにフォーカスできませんでした : %v", err) } // 「メンズノンノ阿部寛」をクリック if err := page.FindByXPath("/html/body/table/tbody/tr[8]/td[2]/strong/a").Click(); err != nil { log.Fatalf("阿部寛のメンズノンノがみつかりませんでした : %v", err) } // スクショとる if err := page.Screenshot("./abe_hiroshi.jpg"); err != nil { log.Fatalf("スクショ取れまへん : %v", err) } }ハマったこと
frame内部の要素にアクセスできないよぉぉぉ?
コードで言うと
// 写真集のリンクを検索するのコメント以下でハマってた。。。トップページから写真集のリンクをクリックしようとGoogle ChromeのDeveloper Toolsで
XPathをコピーしてFindByXPathに貼り付けたのですが、要素が見つからないエラーが発生した。failed to select elements from selection 'XPath: html/body/table/tbody/tr[10]/td[3]/p/a [single]': element not found
なんで。。。
ちゃんとHTMLを読んで見ると
framesetおよびframeの存在を見つけました。
あれ?Developer ToolsでコピーしたXPathは
html/body/table/tbody/tr[10]/td[3]/p/aですね。これじゃ見つからないですね。
html/frameset/frame/.......って書けば良いのかな?
ん?#documentってなにこれ?っていうかframeってなに?わからぬ。。。...
どうやら阿部寛のホームページは以下のように2つのframeで構成されているということがわかった。
解決方法
AgoutiのドキュメントでFrameで検索してみると
SwitchToFrame()ってメソッドを見つけた。func (s *Selection) SwitchToFrame() error
SwitchToFrame focuses on the frame specified by the selection. All new and existing selections will refer to the new frame. All further Page methods will apply to this frame as well.https://godoc.org/github.com/sclevine/agouti#Selection.SwitchToFrame
どうやらframeにフォーカスを当てる必要があるらしい。
そのためframeを指定してから要素を検索すればOK。
またframeのフォーカスを外すSwitchToRootFrame()ってメソッドも用意されていた。なので、以下方針でリンクをクリックすることにする
- トップページにアクセス
- 左側のフレームにフォーカスする
写真集リンクをクリックする- フレームのフォーカスをRootに戻す
- 右側のフレームにフォーカスする
メンズノンノ阿部寛リンクをクリックする// 写真集のリンクを検索する // frameの中の要素を検索するには一旦該当のフレームにフォーカスしなければならない // 左側のフレームにフォーカスする page.FindByXPath("/html/frameset/frame[1]").SwitchToFrame() // 「写真集」をクリック page.FindByXPath("html/body/table/tbody/tr[10]/td[3]/p/a").Click() // 左側フレームのフォーカス外すためrootにもどる page.SwitchToRootFrame() // 右側のフレームにフォーカスする page.FindByXPath("/html/frameset/frame[2]").SwitchToFrame() // 「メンズノンノ阿部寛」をクリック page.FindByXPath("/html/body/table/tbody/tr[8]/td[2]/strong/a").Click()これでframeを利用しているWebページに回遊できるようになりました?
- 投稿日:2019-03-30T13:05:12+09:00
TCPのLISTEN見て、ヘルスチェック応答させる
背景
AWSのALB(Application Load Balancer)を使いたかったんですけど、アプリがヘルスチェックにうまく反応しない。。
(ALBは正常のHTTPステータスコードとして、200 ~ 499の範囲で指定可能ですが、アプリが500を正常で応答する仕様 orz)そしてアプリ側の改修して頂ける雰囲気はないので、ヘルスチェックを書きました。
サマリ
最終的に出来たコード
・ net/httpでヘルスチェック用webを起動
・ health check requestを受けたら、net.Listen でサービスが稼働しているポート20000番のポートをListenを試みる
・ Listen出来た場合(サービス停止) → 404を応答
or
・ Listen出来なかった場合(サービス稼働) → 200を応答handler作成
ヘルスチェックのリクエスト受けた時に、特定ポート(今回は20000port)の Listen を試みる handler です。
listen出来たら、404 NotFoundで、Listen出来なかったら、200OKを応答する。var ( svcProtocol = "tcp4" svcPort = ":20000" okMsg = "healthy" ngMsg = "unhealthy..." ) func checker(writer http.ResponseWriter, request *http.Request) { // Try to listen on svcProtocol and svcPort. listenchecker, err := net.Listen(svcProtocol, svcPort) if err == nil { // able to listen it... Which means, the service has stopped...ng writer.WriteHeader(http.StatusNotFound) fmt.Fprint(writer, ngMsg) defer listenchecker.Close() return } // not able to listen it. Which means, the service is working...ok!!! fmt.Fprint(writer, okMsg) return }handler登録してヘルスチェック応答web起動
ヘルスチェックのパスとポートを決めて、 handler を登録して web を起動
health.govar ( healthPath = "/health" healthPort = ":80" ) func main() { http.HandleFunc(healthPath, checker) http.ListenAndServe(healthPort, nil) }windows 用にコンパイル
-ldflags="-H windowsgui"はプロンプト起動させない様にするとか。GOOS=windows GOARCH=386 go build -ldflags="-H windowsgui" health.goAWS側
リスナー設定
ヘルスチェック設定
次にヘルスチェックの指定
ポイントはヘルスチェックのポートをトラフィックポートではなく、上書きを指定しヘルスチェック専用ポートを指定すること。実験開始
まずは、windowsサーバでコンパイルした自作ヘルスチェックを起動
c:\>health.exeヘルスチェック(80)と、サービス(20000)共に起動(LISTEN)している
c:\>netstat -aon | find "80" TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 3552c:\>netstat -aon | find "20000" TCP 0.0.0.0:20000 0.0.0.0:0 LISTENING 3636curlでヘルスチェック先を叩くと200が返ってくる。
~ ❯❯❯ curl -i http://ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com/health HTTP/1.1 200 OK Date: Sat, 30 Mar 2019 03:33:21 GMT Content-Length: 7 Content-Type: text/plain; charset=utf-8 healthyサービスを停止し、再度curlで確認するとちゃんと連動してるのが確認できた。
~ ❯❯❯ curl -i http://ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com/health HTTP/1.1 404 Not Found Date: Sat, 30 Mar 2019 03:34:17 GMT Content-Length: 12 Content-Type: text/plain; charset=utf-8 unhealthy...




