NoahOrblog

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

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でおなじみのアレやこれやがふんだんに書いてあったのでとても美味しかった。

おわり

Haskell(というか主に遅延評価)について

この記事はAizu Advent Calendar 2016 18日目の記事です。
前の人は、@lycoris0731 さん、次の人は @nktafuse さんです。


自分のこと

Noahです。学部一年です。
特に好きな分野とかは無いのですが、今回は『Haskellいいぞっ』という点と、Haskellの評価戦略(主に遅延評価と正格評価のこと)を書いていきたいと思います。
自分も初めて半年も経っていないのでわからないことも多いのですが、その点はご了承ください。

Haskell とは

f:id:NoahOrberg:20161217192140j:plain
関数型言語です。関数型言語の定義とは、

何をもって関数型プログラミングとするか、関数型プログラミングを行っているコミュニティ内でも正確な定義や合意というものは存在しないが、一般的には、手続き型プログラミングがコマンド実行の列としてプログラムを記述していくのに対し、関数型プログラミングは複数の式を関数の適用によって組み合わせていくプログラミングスタイルである、ということは広く認められている。 by 関数型言語 - Wikipedia

つまり、Cなどが代表される手続き型言語は、

// nは任意の数
sum = 0;
for (i = 0; i < n; i++){
    sum += i;
} 

このような感じで、命令を並べ、順番に実行するのに対し、関数型言語(今回はHaskell)では

sum' :: Int -> Int  --sum'としたのは、すでにsum関数がすでにPreludeというモジュールに定義されているからです。
sum' 0 = 0
sum' n = n + sum' (n - 1)

このように、関数の適用を組み合わせてプログラミングします。(この例では再帰を利用して0-nまでの合計値を出しています)
forは特にそうなのですが、変数に値を代入する行為、いわゆる副作用を排除した考えを関数型プログラミングでは用います。そのため、for(一応、forのようなものはHaskellに存在はするが)は、基本的にはあまり使いません
Haskellでは変数に値を入れることを束縛といいます。
値を束縛することで、変数を利用します。
一旦束縛したら、中身は変えられません。これは副作用を無くすことで生まれる参照透明性を確保するためです。
そんなの不便じゃないか??と思う方もいらっしゃると思いますが、プログラミングスタイルに慣れればこれは理にかなっているものだと感じられるようになります。

関数色々

Haskellは関数を第一級オブジェクトとして扱います。
つまり、関数を変数に束縛することや、関数の返り値として関数を返すことも可能です。
関数を引数に取る例としてmap関数があります。

map' :: (a -> b) -> [a] -> [b]
map' _ [] = []
map' f (x:xs) = f x : map f xs

一番上に書いてあるものは関数の型宣言です。

f :: Int -> Int  -- Int型を受取り、Int型を返す
g :: (Int -> Int) -> Int -- Intを受け取りIntを返す関数、を受取、Intを返す。 

map関数は、a型を受取りb型を返す関数(f) と、a型のリスト( (x:xs) ) を受取り、a型のリストの中身それぞれに関数を適用した値(b型)をリストにして返す関数です。
rubyなどのmapメソッドと同じ挙動ですね。
ちなみに、関数の型宣言で用いられているIntのように最初の文字が大文字のものは型ですが、aなどのように最初が小文字なものは型変数といい、任意の型(CharやInt, Integerなど)にできます。
更に、リスト (x:xs) で、リストの先頭要素それ以外の要素に分けて名前を付ける事ができます。
(:) はコンスと呼ばれる関数で、Lisp等のconsとおなじだと考えればいいです。

遅延評価と正格評価

Haskellの大きな特徴として、遅延評価を採用している点があります。
CやRubyなどでは、標準で正格評価を行います。
遅延評価が採用されると何が嬉しいかというと、簡単な例では無限リストを扱えます。

ghci > take 3 [1,2..]
[1,2,3]

[1,2..]は、[1,2,3,4,5,...100,...10000000,....]と続く無限リストです。
take関数は、引数として与えたリストから指定された分だけ、先頭要素からリストにして返します。
普通こんなものを扱うと引数のリストを評価するために無限に処理が行われますが遅延評価を評価戦略にしていると、必要な分だけ処理するので、上記の例では先頭要素3つ分だけ評価します。
他の例では、たらい回し関数と言うものが有り、

tarai :: Int -> Int -> Int -> Int
tarai x y z
  | x <= y = y
  | otherwise =tarai (tarai (x - 1) y z) (tarai (y - 1) z x) (tarai (z - 1) x y)

以下にRubyでのコードも記述します。

def tarai(x, y, z)
  x<=y ? y : tarai(tarai(x-1, y, z), tarai(y-1, z, x), tarai(z-1, x, y))
end

そして、引数として(10, 5, 0)を与えたときのHaskellでの実行時間と、Rubyでの実行時間は以下の通りです。

*Main> tarai 10 5 0
10
(0.01 secs, 90,672 bytes)
% ruby tarai.rb
      user     system      total        real
  0.020000   0.000000   0.020000 (  0.015592)

そんなに変わりませんね。体感時間でも両方一瞬で終わります。
次に引数として(14, 7, 0) を与えたときです。

*Main> tarai 14 7 0
14
(0.01 secs, 99,784 bytes)

Haskellは変わりません。しかしRubyの方は、、、

% ruby tarai.rb
      user     system      total        real
 24.420000   0.090000  24.510000 ( 24.838016)

totalの時間が (10, 5, 0) のときより遥かに大きくなっています。
なぜこうなったかというと、Haskellの遅延評価が上手く効いているのです。
たらい回し関数では関数を実行する際に、引数(x', y', z') = ( tarai(x-1, y, z), tarai(y-1, z, x), tarai(z-1, x, y)) [ 区別を付けるために引数はx', y', z'にしています ] のなかでx', y' を評価し、 y'<=x' となったときは、z'の部分は利用されず評価をしても値を利用することがありません。
遅延評価は、必要になったら評価する評価戦略なので、x'<=y'となったときには評価に不要なz'部分は評価せずに素直にy'を返します。
otherwise、つまりそれ以外になって、z'が必要になったときに初めてz'を評価するのです。


最後に

以上です。少々間違いもあるかもしれませんが、関数型やる人が増えてほしいなと切実に思います。

参考にしたサイトなど

Haskell
時間の計測

おわり

数学ガールとやら

Noahです。

数学ガールを最近ちまちま読んでます。

なにしろ自分は、ここやTwitterに何回書いたか分からないけれど、兎にも角にも数学弱者なので勉強しようと思っても大学講義の微積Iや力学はさっぱりわからなかった。

微積分を高校時代に数IIの範囲までしかやってなかったので地獄を見ることは分かってた。分かってはいたんだ。

一ヶ月くらい前だが、同じクラスの数学好きな某氏に「数学ガール」なる本を勧められて、ちょうど数学のレポートに読書感想文(?)があったので読んでみた。

数学ガール/乱択アルゴリズム (数学ガールシリーズ 4)

数学ガール/乱択アルゴリズム (数学ガールシリーズ 4)

その本にコンピュータ理工学らしく「乱択アルゴリズム」を選んだわけだが、

線形探索や、二分探索などのオーダ記法のところは、

応用情報や基本情報の取得の際に覚えはしたものの理屈では分かってないところもあったのだが、やっとこの本で理解できた。

(試験を受けた当時は log n なんていうのも理解はしていなかった。高校生なのに。)

少々難しかったがなんとか最後まで読んだ。(理解しているかはさておき。)

何周かする必要がある。繰り返し大事。


そこで、先日「数学ガールの秘密ノート」シリーズを思い出した。

数学ガールの秘密ノート」シリーズのほうが易しいことは数学好きな某氏や先輩に聞いていたので、長期貸出を行っている大学内の図書館から、

微分を追いかけて」、

数学ガールの秘密ノート/微分を追いかけて

数学ガールの秘密ノート/微分を追いかけて

「数列の広場」、

数学ガールの秘密ノート/数列の広場

数学ガールの秘密ノート/数列の広場

「ベクトルの真実」

上記三冊を借りることに成功。

それが今日の昼過ぎくらいの出来事だったが、未だ選んだ一冊目の「微分を追いかけて」の2章程度までしか読んではいない。

が、最初の最初から教えてくれるのが自分のような人間には、本当にありがたい。

一ヶ月は借りれるので何周か周って読めると思う。…思う。