MashupAwards(会津)に出てきた
Noahです。
MashupAwardsというのの会津で行われたやつに参加してきました。
以下自分の役割ばかりの話なので最初に思ったことを書きますが、やっぱVRすごいって思いました(僕もやってみたくなった)
あとは、発想と、それをいかにして実現するか、というものが結構大事で、特に発想力を身に着けたいなと思った。
提供APIとかが結構あったのですが今回の自分のものではほとんど利用していなく、ヘルス管理にWatsonを利用してみようともおもったのですがいかんせんデータが少ないのと、活かす方法を考えている間に時間が溶けていったので、物事を柔軟に考える力も欲しいと思いました。
何作ったの
ずばり、赤べこによる健康管理的なモノ
@flying_hato_bus という僕の友人がいるんですけど、彼の赤べこ云々ネタが最近結構続いていて、それにあやかった感じです。はい。
ちなみに彼のエントリのほうに詳しいこと書いているので、自分は自分の所感を書いていくだけにします。
何したの
健康管理アプリのバックエンド担当しました。
やることとしては、SmartWatchから飛んでくる脈拍を逐次記録、クライアント(管理画面用)に管理用データを返す、赤べこの首振り制御用に心拍データをBPMに変換する処理して返す、等。
ユーザ認証つけたかった感がある(手抜きして付けてない)
あと、別にやらなくて良かったのですがNginxを使ってプロキシなどなどの設定をゴテゴテしてました。
セキュリティグループの概念の存在がわかっていなくて前回の石巻ハッカソンでは利用できていなかったのですが(というかその時はAPIサーバーとクライアント接続の時間がなくAPIサーバーは作ったがつかわれなかった)、今回は事前に調査なりしたのでそこそこ有効活用できたと思います。
こぼれ話
やっているとかなりハプニングというか最高のイベントが起こりすぎました。
赤べことサーバー間通信、未実装問題
こればっかりはどうしようもないのですが、赤べこ&その制御用ラズパイの担当のはとバスくんがスライドを作り始めたので、担当箇所終わったのかなと思っていたら、
まさかのラズパイとサーバー間通信できていないという致命的な問題が、開発時間終了30分前に見つかる(ちなみにサーバーのエンドポイント自体は完成しているため、ラズパイ対応のみ)
スライド作成は僕にはセンスが壊滅的にないため無理なので、そこの部分をコーディングし始めたのですが、なんと15分前に自前のモバイルWifiが何故か圏外になるという最高のイベントが発生して軽く死にかけました
が、なんとか企業の方のWifiで持ちこたえることができました
エンドポイントの仕様とか明確になってない
これは完璧自分が悪いのですが、各エンドポイントの仕様が曖昧すぎてクライアントに迷惑をかけすぎました。
モックを早い段階で適当に書いてAWSのEC2上にデプロイしておいたのですが、そんなことするより先に仕様をすべてすぐに決めるべきでした。
というわけで知見
- やっぱり深夜とかまでみんなで一箇所に固まって開発するのは楽しい
- ハッカソン中に行く富士の湯は最高。
- 注意すべきところ
- 仕様ちゃんと決めよう
- ちゃんとしたネット環境を準備しよう
- 役割ちゃんと分担しよう
ちなみに、APIサーバーを完成させた後(最終日の昼前から未実装部分発覚まで)は完全にサーバーのLog監視マンになっていた。
携帯の入力的なアレ
Noahです。
授業後に後輩と演習室で話し込んでたときに、ふと彼がAOJの問題をC++で解き始めたのでその隣で同じ問題をHaskellで実装してました
もともと競プロをやっていたとかではなくて、純粋にHaskellを書きたかっただけです。はい。
Revisionを見ると分かるんですけど、最初は愚直な実装をしてて、
toChar :: Char -> Int -> Char toChar x n = case x of '1' -> solve1 n '2' -> solve2 n '3' -> solve3 n '4' -> solve4 n '5' -> solve5 n '6' -> solve6 n '7' -> solve7 n '8' -> solve8 n '9' -> solve9 n solve1 :: Int -> Char solve1 1 = '.' solve1 n = iSolve1 n '.' where iSolve1 1 s = s iSolve1 n s = case s of '.' -> iSolve1 (n-1) ',' ',' -> iSolve1 (n-1) '!' '!' -> iSolve1 (n-1) '?' '?' -> iSolve1 (n-1) ' ' ' ' -> iSolve1 (n-1) '.' solve2 ...
なんていうコードを solve9
までやるという愚直ぶりだったのですが、後に
preset :: []String preset = [".,!? ", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] toChar :: Char -> Int -> Char toChar x n = solveEx (n - 1) $ preset !! (ord x - ord '0' - 1) solveEx :: Int -> [Char] -> Char solveEx n xs = xs !! (n `mod` length xs)
とすることで、愚直な実装を回避することができました🎉
最近仕事でGolangしか書いていないので、他の言語を触る機会がほぼほぼなかったので新鮮な気分だった👀
加減乗除で10
Noahです。
数字を4つ出されて、加減乗除好きな演算子を使って10になるようにする、という診断メーカーのツイートを見て、深夜に好きな配信者のASMR生配信を聞きながら、つっと書いてみた。
実行時:
トリプルの一つ目が、演算子の順、二つ目が、数字の順、最後は演算結果。
この例だと(いいかんじに読み取れば)、出力されたリストの一番最初は (9 - 8) * 7 + 3
となるのが容易にわかる
半分寝て書いてるからあまり(というか多分全然)良くない。付けてる名前も適当すぎる。
そもそも乗除算が優先されるのがされない(infixrをうまく使うのか🤔)ので、出力からいいかんじに考えるくらいの思考力がほしいところ。
p.s. 頭悪い
双葉杏は『8、3、7、9』をつかって10にしてください。
— のあ (@noah_orberg) 2017年10月20日
https://t.co/0HJuHe3uFw
3 + 7 / floor(9/8)
今脳死状態だからダメ
N+1問題
Noahです。
ペアプロ等で指摘されたことで、重要だと思ったら1つずつメモを書こうと思い、書く。
続…かないかもしれない
今日はN+1問題について(ここのコードは深夜に書いたメモ程度でなんと言うか、補助的役割である👀)
N+1の問題
まず、1つのクエリで複数のidを取得し、その後繰り返しのクエリで、idのスライスのそれぞれをDBから抜いてくること。 idの数をNとすると、合計N+1のクエリが発生する。
// func Query() (Data, error){ // 内部では、1つの構造体Dataのものを返すようになっている // } // func Query2ByID(id string) (User, error){ // 内部では、idから紐付けられる1つの構造体Userを返すようになっている(Userモデル内でid はUNIQUEとする) // } func f() { // あるデータをDBから抜いてくる // 中に複数のidがあるような構造体 // (e.g.) // type Data struct { // Subs []Sub `db:"-"` // } // type Sub struct { // ID string `db:"user_id"` // } // type User struct { // ID string `db:"id"` // Name string `db:"name"` // } //構造体Dataを一件DBから取得するクエリ ......(1) data, err := Query() if err != nil { log.Fatal(err) } // idsをもとにDBからユーザ情報を抜く for i, sub := range data.Subs { //構造体Userを、Sub.IDから紐付けてDBから抜いてくるクエリ ......(2) user, err := Query2ByID(sub.ID) if err != nil { log.Fatal(err) } // なんか処理する } }
愚直にこんな感じで書いてしまうと、(1), (2) で、合計N+1のクエリを実行してしまうため、パフォーマンスが非常に悪い。
良い方法として、mapを利用した解決策がある
解決策
// func Query() (Data, error){ // 内部では、1つの構造体Dataのものを返すようになっている // } // func Query2ByIDs(id []string) ([]User, error){ // 内部では、idから紐付けられる構造体Userを返すようになっている(Userモデル内でid はUNIQUEとする) // 先程との変更点は、複数のidを受け取り、複数の結果を返すようになっている // } func g() { // あるデータをDBから抜いてくる // 中に複数のidがあるような構造体 // (e.g.) // type Data struct { // Subs []Sub `db:"-"` // } // type Sub struct { // ID string `db:"user_id"` // } // type User struct { // ID string `db:"id"` // Name string `db:"name"` // } //構造体Dataを一件DBから取得するクエリ ......(1) data, err := Query() if err != nil { log.Fatal(err) } // idをKeyとして、Userモデルを引っ張れるmapを作成 userMap := map[string]User{} for i, sub := range data.Subs { userMap[sub.ID] = User{} // keyとvalueを初期化しておく } // idのスライスを作る ids := make([]string, len(data.Subs)) for i, sub := range data.Subs { ids[i] = sub.ID } // idのスライスからそれらに紐付けられている複数のUserを引っ張ってくる ......(2) users, err := Query2ByIDs(ids) if err != nil { log.Fatal(err) } // usersをidをkeyにmapに入れていく for _, u := range users { userMap[u.ID] = u } // 以降、userMapからkeyをIDにするだけでモデルを取得できる }
このようにすると、(1), (2) のクエリが2個で済むようになり、パフォーマンスの向上が図れる。
書いてるときは、パフォーマンス等を全く考慮しないで書いていたので、ちゃんと考慮したいと思った次第…
RPN
Noahです
久々にHaskell触ってました
構文解析とかやってみたいなと思い、気づいたら深夜のノリでRPNのパーサと諸々を書いていました
半分寝落ちしつつ書いていたけどお愛嬌。
Functionally Solving Problems - Learn You a Haskell for Great Good!
H本は畳み込みでやってるけど、どうしても一旦データ構造に落としたかった…。
CockroachDB備忘録【導入編】
Noahです。
CockroachDBを触る機会があったのですが、いかんせん日本語の情報量が少なすぎるので導入する際に躓いたところを備忘録として残しておきます。
CockroachDBとは
調べた限り、GoogleのSpannerのオープンソースクローンらしい
いわゆるNewSQLであり、RDBとNoSQLのいいとこどりをしている分散型DB
ちなみに最近(2017年5月頃) にVersion1.0がリリースされた模様
インストール
まだ新しいため、日本語の情報が少ない。
brewでも行けるっぽいが、バイナリを直で落としてPATH通すことに
https://www.cockroachlabs.com/docs/stable/install-cockroachdb.html
このページを参考に、インストールする
リンク中にあるtgzファイルをダウンロード後に、
# 解凍 tar xfz cockroach-v1.0.4.darwin-10.9-amd64.tgz # PATHが通してあるディレクトリに移動 cp -i cockroach-v1.0.4.darwin-10.9-amd64/cockroach /usr/local/bin # 確認 cockroach version
これでバージョン情報が出力されればOK
起動
正直、ここが辛かった。
どこを見ても、
$ cockroach start
と書いてあるが、これでは起動しない
CAの証明書がないので、insecureで起動させる
$ cockroach start --insecure
しかし、これでもうまく起動しない。
正確には起動はするが、 localhost:8080
での管理画面で
Connection to CockroachDB node lost.
と怒られるのだ。
実際、起動時のターミナルの画面を見てみると、
ん、admin(管理用ページ)が http://Reo-no-MBP:8080
(Reo-no-MBP
とは自分のマシンの名前である)になっている。
アクセスすると
\(^o^)/
結局
以下で起動するとホストをlocalhost
にして起動できます
$ cockroach start --insecure --host=localhost
結局デフォでホストをいい感じに設定できていなかっただけか…?
ちなみに2台のPC(両方共バイナリから導入した)で両方共同じところで躓いていたため、バイナリで落としたのが良くなかったのかも??
GolangでのJSONパース備忘録
Noahです
GolangでGistにPOSTするプログラムを書いていたのですが、JSONのキーの値を変更せねばならないところで躓いたので備忘録します
GolangにおけるJSONの取り扱い方
基本的に構造体を使います
type Person struct { name string `json"name"` age int `json:"age"` }
このように json
タグを構造体の各要素に書いて、 encoding/json
パッケージ内の Marshal
関数でパースすることで、JSONを生成できます
noah := Person{ name: "NoahOrberg", age: 20, } data, err := json.Marshal(noah) if err != nil { // エラーハンドリング }
という感じで、 data
に []byte
でJSONにパースされたデータが入ります
本題
構造体を扱う場合は、構造体のタグの値は 不変 です
ミュータブルである変数のようにあとから後付できません
タグ情報を取得する関数は reflect
パッケージにありますが、再びセットし直すことはできないのです
そのため、
{ "description" : "説明", "public" : false, "files" : { "hoge.txt" : { <- ココ! "content" : "ファイルの中身" } } }
上のようなGistのAPIのPayloadのようにキーにファイル名を持たせる、などには扱えません。
どうしようかと、取り敢えずIssueだけ立てて解決策が出るまで保留にしようと思ったところ、Issueを立てて数分後に @upamune さんから助言をいただきました (サンプルコードまで用意していただきありがとうございます🙇 )
なるほど、 map
で持たせればJSONのキーの値はmapのキーの値となって変更可能になるのか。
1.のほうで map[string]File
としてmapで持たせて、2.の方で詰め込んでます
このようにすることで、パースする際にキーの値を変更させることができました🎉