読者です 読者をやめる 読者になる 読者になる

ami_GS's diary

情報系大学院生の備忘録。ネットワークの勉強にハマっています。

GitHubのContributionsをCUI上で表示するツール書いた

Golang

はじめに

僕といえばGitHubへの連続コミットということもあり、タイトルの通り、GitHubのContributions(以下の部分)をCUIで表示するツールを書きました。
f:id:ami_GS:20150124101938p:plain

ちなみにGoで書きました。ソースコードは以下
ami-GS/github_squares · GitHub

細かい技術

goquery

Go言語ではスクレイピングをする際、いろいろな選択肢があるのですが(go.net/html、go-html-transform等)、goqueryというものがjQueryライクにスクレイピングを実現するということで使ってみました。

四角の色付け

1日にどれくらいコミットしたかを色付けするためには”■”に緑の色付けをします。その場合は、

fmt.Sprintf("\x1b[38;5;%dm%v\x1b[0m", color, value)

とすればおk!
256色のリストはここです。

注意点

"https://github.com/ユーザ名"にアクセスすると、その中にcontributionsの緑色の四角の情報が入っている時と入っていない時があります。
なので、四角の情報を取るときは、"https://github.com/users/ユーザ名/contributions"にアクセスするとおそらくデータの取りこぼしがありません。

結果

結果です。
f:id:ami_GS:20150625213128p:plain


ちなみに

オプションで草生やせます
f:id:ami_GS:20150625213333p:plain

GoでCUIでマインスイーパ作った

Golang

はじめに

こんにちは、Goで何か作りたいな〜と思っていたらマインスイーパができていたのでレポです。
全体のコードは長いので要点を載せて説明します。

実装方針

  1. コマンドライン入力によりマス数(行数×列数)を決定
  2. それぞれのマスは -1~18 の値を持つ (-1~8:非オープン状態、9~18:オープン状態)
  3. ランダムで地雷の位置を決定 (-1)、その周りのマスを+1する。
  4. 選択されたマスの周りに地雷がない時、地雷があるマスに隣接するマスまで一度にオープンする
  5. 操作のたびに画面をリフレッシュする

コード達

1, コマンドライン入力によりマス数(行数×列数)を決定
単純明快、パッケージを使えば実現可能です。

        var input string
        var field *Field
        var h, w, m int
set:
        fmt.Printf("Input height, width, (num of mine) (e.g : 8,8(,9))\n>> ")
        fmt.Scanln(&input)
        pos := strings.Split(input, ",") //入力を分割
        if len(pos) == 2 || len(pos) == 3 {
                w, _ = strconv.Atoi(pos[0])
                h, _ = strconv.Atoi(pos[1])
                if len(pos) == 2 {
                        // 地雷数が指定されていない場合はマス数の25%を地雷に
                        m = w * h / 4
                } else {
                        m, _ = strconv.Atoi(pos[2])
                }
                if w == 0 || h == 0 || m == 0 {
                        fmt.Println("Please input 2 or 3 numerical values (value > 0)")
                        goto set //不適切な入力の時、setまでgoto
                }
                field = NewField(byte(h), byte(w), byte(m)) //地雷原構造体を生成
        } else {
                fmt.Println("Please input 2 or 3 numerical values (value > 0)")
                goto set
        }


2, それぞれのマスは -1~18 の値を持つ

type Field struct {
           width  byte
           height byte
           state  [][]int  // 地雷原のもつ値、-1 ~ 18 が入る
}


3, ランダムで地雷の位置を決定 (-1)、その周りのマスを+1する。
実装の簡単のため、入力された行数+2、列数+2の地雷原を生成し、ダミーとして使います。

func NewField(width, height, mineNum byte) *Field {
        field := &Field{width, height, [][]int{}}
        field.state = make([][]int, height+2)
        for i := 0; i < int(height)+2; i++ {
                field.state[i] = make([]int, width+2)
        }

        //生成した地雷の場所を一旦保持
        var pos [][2]byte = make([][2]byte, mineNum)
        // [0 width*height) をランダムに並び替え
        idx := rand.Perm(int(width * height))
        for i := 0; i < int(mineNum); i++ {
                //地雷数分 idx の先頭から取り出す
                pos[i] = [2]byte{(byte(idx[i]) / width) + 1, (byte(idx[i]) % width) + 1}
                // 地雷の周りを+1する
                field.state[pos[i][0]-1][pos[i][1]-1] += 1
                field.state[pos[i][0]-1][pos[i][1]] += 1
                field.state[pos[i][0]-1][pos[i][1]+1] += 1
                field.state[pos[i][0]][pos[i][1]-1] += 1
                field.state[pos[i][0]][pos[i][1]+1] += 1
                field.state[pos[i][0]+1][pos[i][1]-1] += 1
                field.state[pos[i][0]+1][pos[i][1]] += 1
                field.state[pos[i][0]+1][pos[i][1]+1] += 1
        }
        for i := 0; i < int(mineNum); i++ {
                //地雷を置く(-1)
                field.state[pos[i][0]][pos[i][1]] = -1
        }
        return field
}

4, 選択されたマスの周りに地雷がない時、地雷があるマスに隣接するマスまで一度にオープンする
RecursiveOpen(row, column)の中身は大したこと無いのに長ったらしいので省略します。

func (self *Field) Open(row, column byte) {
        // オープン => マスに +10
        self.state[row][column] += 10
}

func (self *Field) Choose(row, column byte) (gameover bool) {
        // ダミーマスの関係上、1オリジン
        gameover = false
        if 0 == self.state[row][column] {
                // 周りに地雷がない時、再帰的にオープンする
                self.RecursiveOpen(row, column)
        } else if 0 < self.state[row][column] && self.state[row][column] <= 8 {
                //周りに地雷がある時、そこだけをオープンする
                self.Open(row, column)
        } else if self.state[row][column] == -1 {
                //地雷がある時、全てをオープンし、ゲームオーバーフラグを返す
                self.AllOpen()
                return true
        }
        return
}

5, 操作のたびに画面をリフレッシュする
操作(どこをオープンするかの入力)ごとにFieldString()を呼び出し、画面をリフレッシュします。
"\x1b[2J"という文字列が画面をクリアしてくれるので、それを利用します。

func (self *Field) FieldString() string {
        // headerに列数を入力
        header := " "
        for len(header) < int(math.Log10(float64(self.height)))+2 {
                header += " "
        }
        for c := 0; c < int(self.width); c++ {
                tmp := fmt.Sprintf(" %d", c+1)
                for len(tmp) < 4 {
                        tmp += " " // TODO: here should be optimized
                }
                header += tmp
        }

        // 行数とマスを入力
        field := fmt.Sprintf("%s\n", header)
        for r := 1; r < int(self.height)+1; r++ {
                // 行数
                tmp := fmt.Sprintf("%d", r)
                for len(tmp) < int(math.Log10(float64(self.height)))+2 {
                        tmp += " "
                }
                field += tmp

                // マス
                for c := 1; c < int(self.width)+1; c++ {
                        if -1 <= self.state[r][c] && self.state[r][c] <= 8 {
                                field += CLOSED // "[ ]"
                        } else if self.state[r][c] == 10 {
                                field += OPENED // "___"
                        } else if 10 < self.state[r][c] {
                                // _1_ ~ _8_
                                field += OPEN_NUM[self.state[r][c]-11]
                        } else if self.state[r][c] == 9 {
                                field += MINE // "_*_"
                        }
                        field += " "
                }
                if r < int(self.height) {
                        field += "\n"
                }
        }
        // "\x1b[2J"で画面をリフレッシュ(クリア)
        return fmt.Sprintf("\x1b[2J%s>> ", field) 
}

最後に

これでコアな部分は完成しました!
あとは全体のコードを参照してください!プルリクエスト大歓迎です!
以下が 15行,15列,地雷数25%の時のゲームオーバー画面になります。
f:id:ami_GS:20150531235443p:plain

ALPNを日本語訳しました

HTTP2 翻訳

こんにちは、タイトルの通り、Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension (ALPN)を日本語訳し、GitHub上に公開しました。(ここからどうぞ


初めてのRFC翻訳&僕がTLSにそこまで詳しくないので微妙な訳になっているかもしれません。また、マークダウンで書いたので見栄えが微妙かもしれません。

ぜひ、間違いの修正や改善のPull Requestをこちらよりお待ちしております。

文字列の数式を計算するやつ

アルゴリズム 文字列 Python

はじめに

ひょんなことから文字列の数式(四則演算)を計算することになり、Pythonで実装したので載せておきます。(floatは計算できません!!)

コード

import os
import re

ZERO = ord("0")
NINE = ord("9")

def paren(st):
    if st[0] == "(":
        ans, idx = first(st[1:])
        return ans, idx+2
    elif ZERO <= ord(st[0]) <= NINE:
        i = 1
        while i < len(st) and ZERO <= ord(st[i]) <= NINE:
            i += 1
        return int(st[:i]), i
    return 0, 0

def second(st):
    ans, idx = paren(st)

    i = idx
    while i < len(st):
        if st[i] == "*":
            tmp, idx = paren(st[i+1:])
            ans *= tmp
            i += idx+1
        elif st[i] == "/":
            tmp, idx = paren(st[i+1:])
            ans /= tmp
            i += idx+1
        else:
            return ans, i
    return ans, i

def first(st):
    ans, idx = second(st)

    i = idx
    while i < len(st):
        if st[i] == "+":
            tmp, idx = second(st[i+1:])
            ans += tmp
            i += idx+1
        elif st[i] == "-":
            tmp, idx = second(st[i+1:])
            ans -= tmp
            i += idx+1
        else:
            return ans, i
    return ans, i

def calc(s):
    if s.count("(") != s.count(")") or re.search("[^\+\-\*\/()0-9]", s):
        return "ERROR"
    else:
        return first(s)[0]

数式において、優先順位は
括弧 > 掛け算割り算 > 足し算割り算
です。
それぞれの処理を順に paren、second、first 関数にまかせています。

calc(s)

最初に数式が入る関数。
括弧の数が合わなかったり、四則演算に相応しくない文字が含まれている場合はERRORを返し、適切な文字列である場合はfirst関数に渡します。

paren(st)

first() -> second() と入力がたらい回しにされて一番最初に処理が行われる関数。
”(”が最初にある場合は”(”の1つ先以降をfirstに渡します(新たな式として処理)。
数字が最初にある場合は数字が続くまでインデックスを進め、intに変換します。
返り値は
(計算値,進んだインデックス)
になります。

second(st)

parenの次に処理される関数
parenで得られた値ansに対し、次にある値を掛けるか割るかします。
返り値はparenと同様です。

first(st)

secondの次に処理される関数
上記同様、こちらは足すか引くかします。
返り値も同様です。


最後に

インデックスの管理が大変だった・・・
"+("や"-("を直接paren内でやろうとしてめっちゃ詰まった・・・
(firstで+,-の後をsecondに渡せば完了)


毎日コードを書いたよ

Github 日記

はじめに

こんにちは。研究楽しいマンです(^q^)。
さて、以下の通り、GitHubの連続コミットが1年達成したのでレポートします。
f:id:ami_GS:20150124101938p:plain

実際、1年間コミットし続けた方は僕以外にも沢山いらっしゃいますが、プログラミング能力がほとんど無い状態からの連続コミットは僕が初めてだと思います。
その視点で自身が得たものをツラツラと書いていきましょうかね。

そもそもなんでこんな…

なんでこんな面倒くさいことしたのか、疑問しか無いですね。
理由は2つあって、

  1. 毎日コードを書くこと - snowlongの日記 この記事に触発された
  2. 自身の能力を高め、尊敬する技術者さんに近づきたかった

からです!!

プログラムを書き続けるというのは実装力を向上させるだけかと思うかもしれませんが、コーディングを通してコンピュータサイエンスの知識の習得にも繋がるのです!

1〜100日

この時期はいろいろと試行錯誤をしており、git、emacsPythonの様々なモジュール、Raspberry Piの使い方等を勉強していました。
gitなんて、

  1. git add * して、
  2. git commit -m "commit message" して、
  3. git push origin master

するだけでした。
ほとんど意味がわからずにおまじないコマンドを打っているだけでした。


Pythonのモジュールの練習ではsocketに楽しさを感じ、画像、音声データの送受信をしていた事を思い出します。
この当時、たまたま研究室の先生がRaspberry Piを買ってきたのでひたすら弄くり回していました。
Linuxをほとんど触った経験が無く、試行錯誤の連続で良い勉強になりました。
ネットワークやソフトウェアの設定を全て手作業で行ったのでとても苦労しましたが、その分成長したと感じています。


101〜200日

この頃から具体的なプログラムを書き始めたと思います。

  • Raspberry Piを使った遠隔カメラ
  • キャッシュ、フィルタリング機能付きのプロキシサーバ
  • 信号処理

これをネタに企業のインターンに応募した覚えがあります。
プロキシサーバの受けが良かったぽいです

201日〜

企業のインターンでプログラムを書きまくり、さらにそこで得た知識からプログラムを書きまくりました。
具体的にはプロトコルの仕様を読んで実装してみるというのが多いです。(HTTP2、MQTT等)
これがほんとうに楽しくて、プログラミングだけでなく、ネットワークの知識がゴリゴリ付くのが実感できました。

gitも上手く使えるようになり、上記おまじないの意味がわかるようになったり、rebaseやらmergeやら、バージョン管理の恩恵を受けれるようになりました。

結局何ができるようになったの?

適当に言うと、
「なんとなくできることが増えた」
と思います。
固く言うと、「基礎力がついた」ですね。

例えば、
Q, TCPUDP)で通信するときはどうするんだったかな?
A,

socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP

でソケット開いてbindしてlistenしてacceptでアクセスを待ち受ければおk
のように、簡単に答えられるようになります。

勉強するものによりますが、特に低レイヤの技術であれば、どの言語でも似た方法の解決法が見つかるんですよね。
例えばGolangであれば、
A,

serv, err := net.Listen("tcp", "address:port")

してserv.Accept()でアクセスを待ち受ければでおk

というのは調べればすぐにわかりできますし、Pythonでツラツラと書く必要がある部分は内部で完結している事も理解できます

基礎力がつくと単純に問題解決にかかる時間が減りますし、新たに調べる事の足がかりになるので、調べる事の時間短縮にもなります。これは大きい。

最後に

まとめると、

  • コードを書くと、コンピュータサイエンスの知識が付く(定着しやすい)
  • 基礎力をつけると、新たな技術の学習コストを下げることができる
  • 新たな技術を勉強する事への抵抗が減る(慣れる)

これらが大きいです。

技術を勉強し続けると、今まで”おまじない”でやっていた事の理解が出来ますし、さらに新たな技術(上記Golangしかり)に対する学習コストは線形的(指数関数的?)に減ると思います。

皆さんも毎日コーディング計画やってみましょう!良い勉強になると思います!!


楽天のインターンに行っていた話

インターン OpenStack

はじめに

こんにちは、ami_GSです。
8/18 ~ 9/12 (1ヶ月)の日程で楽天インターンに行っていた話をまとめていなかったので書こうと思います。
こちら同様、あまり詳しい内容を書いて問題にしたくはないのでかる〜〜く書きます。

どうして楽天インターン

こちらのインターン詳細ページにある、”プライベートクラウドサービスディベロッパー”というポジションのインターンに参加しました。
応募当初は個別ページに詳細が書いてあったのですが、もう閉じられているようですね。

応募した理由としては、

  1. クラウド(低レイヤ技術)に興味があった
  2. 英語を活かしたエンジニア業を体験したかった
  3. 募集要項の、望ましいスキルにPythonがあった(気がする)
  4. お昼ごはんが美味しそうだった

等があげられます。



何したの?

クラウドプラットフォームと言えば、AWSGCP、Azure等がありますが、
それらの機能をオープンソースで実現するOpenStackというプロジェクトのバグ修正をしました。

OpenStackはPythonで書かれているため取っ付きやすかったです。コード全体はメチャクチャでかくて全てを把握するのは難しいですが・・・


詳しく書いていいのかわからないので避けますが、僕の本名でOpenStackのコミュニティを漁るとバグ修正のやりとりが見つかるので見てみるといいでしょう。

その他にはChef、Serverspecを書いてサーバ構築をしたりもしました。これもOpenStackに関する事ですね。



で、どうだった?

簡単にまとめると、

クラウド技術への興味が強まった

最初はGCPインスタンスで遊ぶ程度の知識でしたが、その中身のコードを弄ってみてどんどん興味が湧いた記憶があります。OpenStackは機能毎にプロジェクトが別れており、その機能について調べるのも楽しかったです。コード自体が比較的読みやすいってのも良いですね。

メッチャ調べた

トラブルの連続でめちゃめちゃ調べました・・・。
OpenStackのインスコ方法、ネットワーク周り、当時最新のWindowsVirtualBoxのバグ、ubuntu最新版?のgitのバグ等々・・・いやぁ〜大変でしたね〜〜(´・ω・`)。
メッチャ調べた分、とてもいい勉強になったと思います。

そこそこ英語話した

募集要項には、
英語 ◎、日本語 ☓
と書いてあり不安だったのですが、僕のチームは日本語メインで話していました。
もちろん外国のエンジニアさんもいたのですが、彼らは日本語を話せるタイプだったので日本語がメインでした。
もちろん英語も使いました。Chefのプロフェッショナルの方や、同時にインターンで入った中国の方とは英語で話しましたね。あとはアメリカの方と1対1でお昼ごはんを食べる時も英語でした。

「英語が社内公用語」と言うイメージが強かったのですが、やはり部署、チームに依るらしいです。

最近以下の記事を見つけましたが英語を必須としているのは、目標へ向かって努力できる優秀な人材を集めるためというのが大きいのだと思います。(僕の憶測です)
楽天社員だったけど質問ありますか? ぶる速-VIP

エンジニアさんのレベルが総じて高い

詳しく書いていいかわからないので避けますが、自分の知らない便利技術を沢山教えてもらいました。
例えばtmux、screen、git便利コマンドなどなど。。。
これらは今でも活用していて、本当にいい勉強になったと思います。

お昼ごはん美味しかった

これはデカイ!
また、その日のメニューのカロリーやタンパク質、脂質が書いてあり、女性やビルダーには結構良い環境だと思います!



総括

メンターさん、技術指導をしてくれた方達に面白い人が多くて楽しかったです。
インターン後に開催されたRakuten Technology Conference 2014に遊びに行った時も僕を覚えていてくれて嬉しかったですね。

ただ、このインターンで一番大事なのは自主性だと感じました。
自分で発見したトラブル(上記)を解決したり、ある問題について自身の解決法を考え、議論する等が重要でした。まぁこれが楽しいんですけどね。

2014年を振り返る

日記

はじめに

こんにちは、ami_GSです。
どうせブログやってるなら1年のまとめを描いてみようと思います。

僕の2014年

今年立てた目標

どこに書いたというわけではありませんが、今年立てた目標は以下の通りです。

  • GitHubコミットを続ける
  • 学会に行く
  • 筋トレに励む

この3つですかね、それでは1つずつ振り返りましょう。

GitHubにコミットを続ける

昨年9月末に凄いエンジニアさんに出会ってしまいまして、その方と同じような発言が出来るようなエンジニアになりたいと思って始めました。

もちろんGitHubにコミットし続けることもあってgitを覚えることが出来ましたが、それ以上にコンピュータサイエンス全般に詳しくなれた事が驚きでした。

ネットワークプロトコルを実装するにあたって、型やビット長を意識するようになったり。
サーバを実装する事もあって、複数クライアントのつなげ方やSSLの使い方など、基本的だけど個人では実装する機会が無さそうな事を勉強出来ました。
コンパイル系の言語では、C++やGoを書く時はパフォーマンスを意識して参照渡しにするべき部分などを学びました。
また、言語仕様で勝手に参照渡しになってしまう物も知る事が出来ました。(Goのスライス等)

何はともあれ、この目標はうまくいったと思います。
現在約340日程度なので、1年連続を目指してもう一息頑張りたいと思います。

学会に行く

これは研究に関してです。
今年は学部4年の時の研究テーマから変えて頑張ってきたわけですが、これに関してはうまく言ったと思います。

僕の所属する研究室では全員が同じ事を研究しており、そのおかげで誰もプログラムを書かないんですね。かつて先生が書いたコードを使えば一発で解析が出来て、すぐに成果が出るという点では良い事ではあるのですが・・・。
学部の頃はその研究分野の勉強と言う事もあって先生に従っていましたが、院生にもなると、先生の言った事に疑問を持たず従うだけというのが馬鹿らしくなってしまい、わがままを言ってテーマを変えさせてもらいました。

結果としては、4,5月は完全に試行錯誤の日々で、何度も0からコードを書きまくりました。アレはキツかった・・・。
6月からはまともなプログラムを書けて、さらに仮定通りの良い結果も出たおかげで論文を書くことが出来、アクセプトされ、さらには学会発表をすることが出来ました。やったぜ!

やはり誰も足を踏み入れた事のない領域に一人で挑むのは勇気と努力が必要ですが、うまく言った時の嬉しさは最高ですね!

筋トレに励む

最後にネタっぽいのが入ってきてしまいましたが・・・。
これは夏頃に立てた目標です。
体を動かすことは好きなのですが、チームスポーツばかりやってきた自分としてはすぐに身体を動かす環境と時間というのは取れないものなので、なんとか身体を鈍らせたくないというのが始まりでした。
始めた頃は65キロ程度だったのが62キロ前後まで痩せ、それでいて筋肉を維持、増強することができています。

筋トレ自体は週1〜2で行っています。
食事にもこだわっていて、油物と間食を止め、プロテイン大豆プロテインホエイプロテインを使い分けています。
食事にプロテインが足りない時には大豆プロテインを飲み、筋トレ後にはホエイプロテインを摂取しています。

これからも筋トレは続けていきたいですね。というのも、日本一握力が強い人がエンジニアという驚くべき事実もあるので・・・。僕も同じようになれたらかっこいいと思います。

来年の目標

  • GitHubの継続
    • 今度は他のプロジェクトへのコミットを意識する
  • Go言語(C++)の習得
    • 型、ポインタを意識したプログラミングの習得
  • また学会に行く
  • 筋トレの持続
    • 逆三角の体型を目指す
    • 68キロ、体脂肪1桁を目指す