NoahOrblog

某・福島にある大学のコンピュータ理工学部の大学生のお話。

携帯の入力的なアレ

Noahです。

授業後に後輩と演習室で話し込んでたときに、ふと彼がAOJの問題をC++で解き始めたのでその隣で同じ問題をHaskellで実装してました

もともと競プロをやっていたとかではなくて、純粋にHaskellを書きたかっただけです。はい。

gist.github.com

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生配信を聞きながら、つっと書いてみた。

gist.github.com

実行時: f:id:NoahOrberg:20171021025646p:plain

トリプルの一つ目が、演算子の順、二つ目が、数字の順、最後は演算結果。

この例だと(いいかんじに読み取れば)、出力されたリストの一番最初は (9 - 8) * 7 + 3 となるのが容易にわかる

半分寝て書いてるからあまり(というか多分全然)良くない。付けてる名前も適当すぎる。

そもそも乗除算が優先されるのがされない(infixrをうまく使うのか🤔)ので、出力からいいかんじに考えるくらいの思考力がほしいところ。


p.s. 頭悪い

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のパーサと諸々を書いていました

gist.github.com

半分寝落ちしつつ書いていたけどお愛嬌。

Functionally Solving Problems - Learn You a Haskell for Great Good!

H本は畳み込みでやってるけど、どうしても一旦データ構造に落としたかった…。

CockroachDB備忘録【導入編】

Noahです。

CockroachDBを触る機会があったのですが、いかんせん日本語の情報量が少なすぎるので導入する際に躓いたところを備忘録として残しておきます。

CockroachDBとは

f:id:NoahOrberg:20170805105156g:plain

調べた限り、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

と書いてあるが、これでは起動しない

f:id:NoahOrberg:20170805111553p:plain

CAの証明書がないので、insecureで起動させる

$ cockroach start --insecure

しかし、これでもうまく起動しない。

正確には起動はするが、 localhost:8080 での管理画面で

f:id:NoahOrberg:20170805112845p:plain

Connection to CockroachDB node lost. と怒られるのだ。

実際、起動時のターミナルの画面を見てみると、

f:id:NoahOrberg:20170805111017p:plain

ん、admin(管理用ページ)が http://Reo-no-MBP:8080 (Reo-no-MBP とは自分のマシンの名前である)になっている。

アクセスすると

f:id:NoahOrberg:20170805111148p:plain

\(^o^)/

結局

以下で起動するとホストをlocalhostにして起動できます

$ cockroach start --insecure --host=localhost

結局デフォでホストをいい感じに設定できていなかっただけか…?

ちなみに2台のPC(両方共バイナリから導入した)で両方共同じところで躓いていたため、バイナリで落としたのが良くなかったのかも??

GolangでのJSONパース備忘録

Noahです

GolangでGistにPOSTするプログラムを書いていたのですが、JSONのキーの値を変更せねばならないところで躓いたので備忘録します

f:id:NoahOrberg:20170722131801p:plain

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[]byteJSONにパースされたデータが入ります


本題

構造体を扱う場合は、構造体のタグの値は 不変 です

ミュータブルである変数のようにあとから後付できません

タグ情報を取得する関数は reflect パッケージにありますが、再びセットし直すことはできないのです

そのため、

{
  "description" : "説明",
  "public" : false,
  "files" : {
    "hoge.txt" : {      <- ココ!
      "content" : "ファイルの中身"
    }
  }
}

上のようなGistのAPIのPayloadのようにキーにファイル名を持たせる、などには扱えません。

どうしようかと、取り敢えずIssueだけ立てて解決策が出るまで保留にしようと思ったところ、Issueを立てて数分後に @upamune さんから助言をいただきました (サンプルコードまで用意していただきありがとうございます🙇 )

f:id:NoahOrberg:20170722095125p:plain

なるほど、 map で持たせればJSONのキーの値はmapのキーの値となって変更可能になるのか。

1.のほうで map[string]File としてmapで持たせて、2.の方で詰め込んでます

このようにすることで、パースする際にキーの値を変更させることができました🎉

github.com

gRPCでのUserAgent備忘録

Noahです

Golang で gRPC, Protocol Buffers を触る機会があり、User Agent の取得で少し戸惑ったのでメモします

gRPCとは

f:id:NoahOrberg:20170717221853p:plain

https://grpc.io/

おなじみGoogleが2015年2月に発表したRPCフレームワークです

対応言語は、C++, Java, Python などはもちろん、Go や Ruby, Android Java, Node.js, Objective-C, PHP も対応しています

シリアライズはデフォルトでProtocol Buffersを利用していますが、好きなものに取り替え可能なようです

かなり速いと謳われるFlat Buffersなんていうものもあるそうですが、今回はProtocol Buffersで行きます

Protobuf定義ファイルの作成

メソッドとメッセージをProtocol Buffersで定義します

今回、リクエストを投げると User Agentを返すようにするので以下のようにします

syntax = "proto3";

package proto;

service UserAgent{
    rpc GetUA(UserAgentReq) returns (UserAgentRes);
}

message UserAgentRes {
    string user_agent = 1;
}

message UserAgentReq {
}

message で、リクエスト、レスポンスの構造を指定します

message UserAgentRes {
    string user_agent = 1;
}

内部の string user_agent = 1 で、 = 1 としているのは、代入というより値をユニークに識別するために行っています


service でメソッドの定義をしています

service UserAgent{
    rpc GetUA(UserAgentReq) returns (UserAgentRes);
}

この辺は普通の言語と変わりなく、メソッド名、引数の message と 返り値の message を指定します


あとは、各言語用にこれをトランスパイルします

protoc はインストール済みとして、Golang用にコンパイルするので以下のプラグインを導入します

$ go get github.com/golang/protobuf/protoc-gen-go

その後、

$ protoc --go_out=plugins=grpc:. api.proto

実行後に api.pb.go が同じディレクトリにあることを確認してください

サーバーの記述

サーバー側を記述します

package main

import (
    "log"
    "net"

    "golang.org/x/net/context"

    "github.com/NoahOrberg/grpc-useragent/protobuf"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/metadata"
)

type UserAgentServer struct {
}

func NewUserAgentServer() *UserAgentServer {
    return &UserAgentServer{}
}

func (s UserAgentServer) GetUA(ctx context.Context, req *proto.UserAgentReq) (*proto.UserAgentRes, error) {
    log.Print(ctx)
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return &proto.UserAgentRes{}, grpc.Errorf(codes.Internal, "Cannot get user-agent")
    }

    return &proto.UserAgentRes{
        UserAgent: md["user-agent"][0],
    }, nil
}

func main() {
    lis, err := net.Listen("tcp", ":50052")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    proto.RegisterUserAgentServer(s, NewUserAgentServer())
    s.Serve(lis)
}

google.golang.org/grpc/metadataを使い、FromIncomingContextメソッドにコンテキストを渡して呼ぶことでメタデータを取得します。

md, ok := metadata.FromIncomingContext(ctx)

メタデータを取得したら、今度はそれからUser Agentを取得します。

これはメタデータ上では配列になっていますが、暫定的に配列の最初のみを取り出して返しています。(配列として返しても、ログとしてこの配列を出力しても確認はできる)

クライアントの実装

サーバー側で認識して処理するので、正直 User Agentを返さなくてもいいのですが、一応。

package main

import (
    "flag"
    "log"

    "github.com/NoahOrberg/grpc-useragent/protobuf"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)

var (
    addrFlag = flag.String("addr", "localhost:50052", "server address host:post")
)

func main() {
    conn, err := grpc.Dial(*addrFlag, grpc.WithInsecure())

    if err != nil {
        log.Fatalf("Connection error: %v", err)
    }

    defer conn.Close()

    c := proto.NewUserAgentClient(conn)

    resp, err := c.GetUA(context.Background(), &proto.UserAgentReq{})
    if err != nil {
        log.Fatalf("RPC error: %v", err)
    }
    log.Printf("\nUserAgent: `%s`", resp.UserAgent)
}

ただ単に呼び出して、結果を吐いてるだけですね。

まとめ

github.com

メタデータから取得する際に、そもそも「メタデータとは?」となっていたからもうダメ

もっと学習が必要だと思った