NoahOrblog

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

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

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

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

vimプラグイン備忘録

Noahです。

使用するエディタがもっぱらVim(vimの使用歴3年程度)なんですが、.vimrcへの設定やプラグインを使うことはあれど、作ったことはまったくなかったので作ってみました。

最初なので純粋にvimscriptだけで書いていきます

何を書こうか迷ったのですが、vimを3年使ってても方向キー使ってしまうときが恥ずかしながらあるためhjklキー矯正のvimプラグインを書こうと思います

…というより、以前このような方向キー矯正プラグインを利用して挫折した経緯があったため、(vimプラグイン書いてみたかったし)せっかくならと自分で実装して、それを忘れないための備忘録。

方向性

先述の通り、3年程度使っておきながら、移動は方向キーという初心者だったので、それの矯正を行うプラグインをテーマにします。

さらに、

「方向キーを使うことは悪魔的行為(デビルズアクション)」

と、天啓を得た(ただのアホ)ので、

「方向キー強制無効化(hjklを使わざるを得ない)、そしてもしも方向キー使用時はサターニャにvim下部のコンソールでツッこまれる」

こんな方向性で行きます

ところでサターニャとは

gabdro.com

の、こんな子

f:id:NoahOrberg:20170624224832j:plain

かわいい。

いざ作る

以下はvimプラグインのフォルダ階層です

- PLUGIN-NAME.vim/
  - autoload/
    - PLUGIN-NAME.vim
  - plugin/
    - PLUGIN-NAME.vim

autoload/ 内に関数定義を書き、plugin/ 内でキーマッピングなどをするのが習慣のようです。

なぜ分けるかというと、 autoload/ 内で 起動時に読み込みではなく必要時に読み込むスクリプト を書いておくことで、起動速度が改善されるからです。

ちなみに、偉大な先人の方がプラグインを書いており、

github.com

このプラグインから、雛形を簡単に作ることが出来ます

Neobundleの場合は ~/.vimrc

NeoBundleLazy 'mopp/layoutplugin.vim', { 'autoload' : { 'commands' : 'LayoutPlugin'} }

dein.vimの場合は $XDG_CONFIG/nvim/init.vim

call dein#add('mopp/layoutplugin.vim')

と記述し、:call dein#update()などでインストール後に

:LayoutPlugin castOfArrow.vim

とすることで、プラグインディレクトリ構成を一発で作れます。

雛形も作れたところで、autoload/ 内に関数を定義していきます

"=============================================================================
" File: castOfArrow.vim
" Author: noahorberg
" Created: 2017-03-05
"=============================================================================

scriptencoding utf-8

if !exists('g:loaded_castOfArrow')
    finish
endif
let g:loaded_castOfArrow = 1

let s:save_cpo = &cpo
set cpo&vim

function! castOfArrow#Cast()
  let l:dist = ["hjklキー使えってこと!?師匠なのに!","こんな方向キー使う奴野放しにしてるなんて天界どうかしてるっ!!","矢印キーで移動するようとするなんて・・・悪っ!!", "どう!?矢印キーなんて最高に悪魔的な行為でしょっ!!", "矢印キーの形態はすべて掌握しているわ それもすべて・・・","上矢印とかkってなに!?"]
  let l:match_end = matchend(reltimestr(reltime()), '\d\+\.') + 1
  let l:rand = reltimestr(reltime())[l:match_end : ] % (len(dist))
  echo dist[rand]
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

シンプルですね。乱数で文字列の配列からランダムに要素を選び、echoするだけです

直で配列に書くのは気が引けましたが、jsonファイルにまとめるにも、JSONパースが面倒そうだったので直書き。

では、plugin/

"=============================================================================
" File: castOfArrow.vim
" Author: noahorberg
" Created: 2017-03-05
"=============================================================================

scriptencoding utf-8

if exists('g:loaded_castOfArrow')
    finish
endif
let g:loaded_castOfArrow = 1

let s:save_cpo = &cpo
set cpo&vim

nnoremap <Right> :call castOfArrow#Cast()<CR>
nnoremap <LEFT>  :call castOfArrow#Cast()<CR>
nnoremap <Up>    :call castOfArrow#Cast()<CR>
nnoremap <Down>  :call castOfArrow#Cast()<CR>
" inoremap <Right> <ESC>:call castOfArrow#Cast()<CR>i
" inoremap <LEFT>  <ESC>:call castOfArrow#Cast()<CR>i
" inoremap <Up>    <ESC>:call castOfArrow#Cast()<CR>i
" inoremap <Down>  <ESC>:call castOfArrow#Cast()<CR>i

let &cpo = s:save_cpo
unlet s:save_cpo

こっちには、キーマッピング系のものを書きます。ひとまずノーマルモード時は方向キーを強制的に使えないようにマッピングします。(インサートモード時は挙動が変だったため保留にしてます。)

完成品

という感じでひとまず完成させたのが 3ヶ月前(この記事ドラフトのままにしててすっかり忘れていた。)

f:id:NoahOrberg:20170517191855g:plain

github.com

f:id:NoahOrberg:20170624224446p:plain

サターニャかわいい。

Golang覚書

Noahです

Golangを触る機会があったので備忘録も兼ねて残しておきます

というかぶっちゃけ、

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

この本の覚書が多いです

$GOPATH と $GOROOT

そもそもここからわかっていなかった。

$GOPATHとは、Golangソースコードなどを置くワークスペース

基本どこでもいいらしいが、自分は$HOME/go/ディレクトリにしておいた。

その下にsrc/を配置、その中に各プロジェクトを置いたりする。

githubなんかからgo getした際には$GOPATH/src/github.com/<USER NAME>/<REPOSITORY>のように置かれる。

$GOROOTはGolangのインストールパス。昔は指定する必要があったみたいだが、今は不要のようだ。

vendor

RubyでいうところのBundlerみたいなものがGolangにもある。

それがglide

brew install glideで入手可能。

使い方は簡単で、対象のプロジェクトディレクトリで

$ glide init

とすればglide.ymlができて、自動で依存関係共々そこに記述される。

その後に

$ glide update

とすれば、glide.lock, vendor/ディレクトリが生成され、その中に依存するパッケージなどが配置される。

ディレクトリ構成

以下のようにしている。

project/
  ├ main.go     -- package main
  ├ lib/
  │  └ pkg1/
  │    └ pkg1.go    -- package pkg1
  ├ glide.yml
  ├ glide.lock
  └ vendor/    -- 依存パッケージなど 

ちなみに、$GOPATH/github.com/<MY USER ID>/<PROJECT NAME>/で管理することで、実際にGithubなどにあげたときと同じ環境で出来る。

main.go内でlib/内のパッケージをimportする際にも、このディレクトリ構成なら、import "github.com/<MY USER ID>/<PROJECT NAME>/lib/<PKG NAME>"でいけちゃうので楽。

最後に

Golangを、「Gopher君、超かわいい!!」くらいにしか思ってなかったが、今回触ってみてやはり他言語と比べて割りとシンプルだという印象を受けた。

ろくに書けるわけでは無い(A Tour of Go を一通りやっただけ)のに「みんなのGo言語」を読んで少し駆け足気味だったのでもう少し基本的なところから復習しがてら何か成果物を作っていけたらな、と思います。

【レビュー】グッド・マス

Noahです。

積読が溜まりに溜まってどうしようもない、読んでそれっきりだと身にならない、のでレビューなり感想なり書いておこう、という話。

記事としては、とても短いです。(大学の課題を処理する前に書いている)

読んだ本、グッド・マス

グッド・マス ギークのための数・論理・計算機科学

グッド・マス ギークのための数・論理・計算機科学

なお、自分の数学の知識はほぼ皆無です。

高校は商業高校だったので微積でさえ少ししかやっていなく、そもそも高校数学に関しての知識がほぼ全く無いため、大学入学以降、関心はあるのですがいかんせん全く出来ない自分でも、自然数無理数の話から、黄金比、エジプト分数、特に第4部の論理、第5部の集合、第6部の機械じかけの数学、は読んでおいてよかったな、と思います。

よくある数学の堅苦しい専門書とかではなく、さらに初見殺しと言わんばかりの記号が複雑に絡み合うものでもなく、あくまで読みやすく、わかりやすい文章で、自分がもともと以前から関心のあった、群論オートマトンについても浅くではあるがどんなものかを知ることが出来た。

第6部の後半、24章のラムダ計算からは、カリー化や部分関数適用、遅延評価やコンビネータなどのHaskellでおなじみのアレやこれやがふんだんに書いてあったのでとても美味しかった。

おわり