write ahead log

ロールフォワード用

golangでtsv(csv)を読む

探せばいくらでも出てきそうだけど,メモしておく.

コード見たほうが早いと思うので.

package main

import (
    "encoding/csv"
    "fmt"
    "log"
    "strings"
)

func main() {
    // テスト用文字列
    str := "test\tテスト\nHello\tこんにちは"

    // CSVのReaderを用意
    r := csv.NewReader(strings.NewReader(str))

    // デリミタ(TSVなら\t, CSVなら,)設定
    r.Comma = '\t'

    // コメント設定(なんとコメント文字を指定できる!)
    r.Comment = '#'

    // 全部読みだす
    records, err := r.ReadAll()
    if err != nil {
        log.Fatal(err)
    }

    // 各行でループ
    for _, v := range records {
        // 1列目
        fmt.Print(v[0])

        fmt.Print(" | ")

        // 2列目
        fmt.Println(v[1])
    }
}

実行結果

$ ./tsv.exe
test | テスト
Hello | こんにちは

割と使いやすいライブラリと思う(クセがない)

vsftpdでディレクトリが削除できない

WindowsからFTPクライアントで(私の場合はエクスプローラで)ディレクトリを削除しようとすると以下のエラーが出た.

550 Create directory operation failed.

本当に原因がわからなくて30分くらい悩んだのだけど,どうも.付きのファイルがFTPで取得できていないのが問題らしい.

そこで/etc/vsftpd/vsftpd.conf内に以下を追記してサービス再起動で解決した.

force_dot_files=YES

マジでハマった.

golangでオブジェクトをシリアライズしたい

cacheに乗せたい時やちょっとしたものを作るときには簡単にシリアライズ/デシリアライズできる パッケージがあると便利だ.

gobパッケージを使うと簡単にできるようだ.
rpc向けのパッケージらしい.

他ではencoding/jsonやencoding/xmlだろうか.ここら辺はググればすぐ出ると思う. encoding/binaryも使い道によっては良いと思う.

それでもgobを使ってみたのはgobの方が早いとの記述を何処かで見たからだ.(が,測定はしていない)

使い方は絶対忘れるので作ったサンプルを書いておく.

package main

import (
    "bytes"
    "time"
    "fmt"
    "encoding/gob"
)

// サンプル用の構造体
// 何となくブログ風味
type Entry struct {
    ID         int64 `datastore:"-"`
    Title      string
    Contents   string
    CreateAt   time.Time
    UpdateAt   time.Time
    DeleteAt   time.Time
}

// シリアライズ用のメソッド
// レシーバ(e)の値をシリアライズしてbyte配列にする
func (e *Entry) GobEncode() ([]byte, error) {
    // エンコーダを作る.
    // io.Writerであれば何でも良いのでここではbytes.Bufferを使う
    w := new(bytes.Buffer)
    encoder := gob.NewEncoder(w)

    // 先頭から順番にエンコードしていく
    // 必ず先頭から1つずつエンコードを実行していく必要があるらしい

    if err := encoder.Encode(e.ID); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.Title); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.Contents); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.CreateAt); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.UpdateAt); err != nil {
        return nil, err
    }
    if err := encoder.Encode(e.DeleteAt); err != nil {
        return nil, err
    }
    return w.Bytes(), nil
}

// デシリアライズするためのデコーダ
// byte配列を渡してレシーバへ値を入れる
func (e *Entry) GobDecode(buf []byte) error {
    // デコーダを作る.
    // io.Readerであれば何でも良いのでここではbytes.Bufferを使う
    r := bytes.NewBuffer(buf)
    decoder := gob.NewDecoder(r)

    // 先頭から順番にデコードしていく
    // 必ず先頭から1つずつデコードを実行していく必要があるらしい

    if err := decoder.Decode(&e.ID); err != nil {
        return err
    }
    if err := decoder.Decode(&e.Title); err != nil {
        return err
    }
    if err := decoder.Decode(&e.Contents); err != nil {
        return err
    }
    if err := decoder.Decode(&e.CreateAt); err != nil {
        return err
    }
    if err := decoder.Decode(&e.UpdateAt); err != nil {
        return err
    }
    if err := decoder.Decode(&e.DeleteAt); err != nil {
        return err
    }
    return nil
}

func main() {
    // 記録するデータ
    e1 := &Entry{
        ID: 1,
        Title: "title",
        Contents: "contents",
        CreateAt: time.Now(),
        UpdateAt: time.Now(),
        DeleteAt: time.Now(),
    }
    // bにシリアライズして入れる
    b, err := e1.GobEncode()
    if err != nil {
        fmt.Println("Error occured.", err)
    }

    // デシリアライズするために用意
    e2 := &Entry{}

    // bからe2へデシリアライズ
    if err = e2.GobDecode(b); err != nil {
        fmt.Println("Error occured.", err)
    }

    // デシリアライズしたものを表示
    fmt.Println(e2.ID)
    fmt.Println(e2.Title)
    fmt.Println(e2.Contents)
    fmt.Println(e2.CreateAt)
    fmt.Println(e2.UpdateAt)
    fmt.Println(e2.DeleteAt)
}

コメントも書いたし,多分思い出せるだろう.

golangのtime型のゼロ値はどうやって判定するのか

と思ってちょっとググったらちゃんと公式に記載があった.

time package - time - Go Packages

1年1月1日の0時だそうだ.特に驚かない.

ここがこの言語のいいところだと思う. (Rubyにも驚き最小の法則ってあったね)

いちいち判定に上記時刻を使うのは面倒なのでちゃんとIsZero()が用意されてる あたり気が利いている.

こんな感じ.

t := time.Now()
if t.IsZero() == false {
    fmt.Println("初期値じゃないよ")
}

というわけでtime型のゼロ値(初期値)判定には(t time)IsZero()を使えばいいそうです.

ubuntu16.04でkindle for PCを動かす

Ubuntuに変えてもほとんど困りはしないんだけど, 致命的なものの一つにKindleがある.

私は最近ほとんどの書籍をKindleで購入しているので, 技術書なんかはとても困る.

そういうわけでUbuntuにkindleを導入したのだが,ちょっとハマる箇所があったのでメモ.

1. wineを入れる

やっぱりwineを使います.

32bit版の方が良いという記事があったので従っておいた.64bitでも問題ないのかもしれない.

$ sudo dpkg --add-architecture i386
$ sudo add-apt-repository -y ppa:wine/wine-builds
$ sudo apt update
$ sudo apt install winehq-devel

バージョンは以下の通り

$ wine --version
wine-1.9.21

2. wineを起動

$ wineboot

3. kindle(Windows版)をダウンロード

これは公式からどうぞ

Amazon - kindle for PC

4. wineの設定を変更

最初はこの工程を行わなかったためどうしてもうまく動かずにハマってしまった.

まず以下のコマンドを実行

$ winecfg

すると新しいウィンドウが現れる.

ここで「Windowsバージョン」をWindows10にすると動作した. 8.1やXPではダメだった.

参考

Ubuntu Weekly Recipe 第433回 Kindle UnlimitedをUbuntuで利用する

Ubuntu 16.04: WineでKindle for PC (Windows)を動かす

ubuntu16.04でキーボード設定がおかしくなる(日本語が入力できない)

Ubuntuに久々に戻したところ,キーボード入力がおかしくて1hほど無駄にしてしまった。

ちょっとググると以下のコマンドで対処可能という情報がよく流れてくる.

sudo dpkg-reconfigure keyboard-configuration

実際, 上記でもきちんと入力できるようになる.が,私の環境の場合(Let's Note CF-N9)ログアウトして 再度ログインするとまたキーマップがおかしくなる.

結局下記で対応した. Ibus(mozc)使ってる状況だからだったのかもしれない.

sudo vi /usr/share/ibus/component/mozc.xml

上記の15行目と18行目あたりにlayoutが定義される箇所があるので, ここをjaからjpに変更した.

今のところ問題も特にない.

何度か挫折しつつ不純lispインタプリタを作った

作りました.

github.com

詳細はgithubのREADMEをどうぞ.(酷い英語ですが)

当初は純lispを作ろうと思ったのでpureという名前ですが, tak関数を動かしたいなど欲が出てきたので 純粋でも何でもなくなってます.

GCはありますが, マクロはありません.
(私がマクロをちゃんと理解してないので)

作成時の参考文献は一番下に記載するので興味のある方はご覧ください.

以下は駄文です.

作り始めたきっかけ

以前から何か言語処理系を作ってみたいなと思っていました.
Brainf*ckはともかく, 作ったことがなかったので.

それとは全く別に私は今27才で, 25,27,32というのは個人的に特別な節目になる年齢じゃないかと感じていました.

SIerにいると年齢と共にプログラミングする機会が減っていくのでなんとなく腕試ししたくなる事がよくあります.

「プログラマを名乗るならここら辺で小さくてバグだらけでも良いので言語処理系を作っておきたいな」という事で着手してみました.

何から始めたものか

とはいえ何から始めたらよいのかという事でとりあえずググって色々調べてみました.
が, イマイチピンときません.

ここら辺で割と心が折れます.

本やOSSのコードを写経する事を試みる

とりあえず何かを真似したいという事でOSSやコードの載っている本を買って写経する事を試みました.

lispとかなら割とサンプルも多かったので調べるといくつか出てきます.

が, これもまたイマイチピンときません.

作ってる感がないというか, あんまり頭に入ってこなかったんですよね.

わかったよーな, わからんよーな.

rebuildを聞く

弱ったなーという事で気分転換にrebuild.fmを聞く事にしました. この時まで聞いたことなかったんですが, 以前Cコンパイラ作る記事を見たことがあるruiさんが出るという事でタイミング的にもいいし聞いてみようかなと.

この回だったかな?

(そういえばこの後Matzさんも出てますね.たまたまですが時期がよかった)

トークの中で東大のCPU実験の話がありましたが,その文脈でruiさんから出た
「やってみればいいじゃないですか?」的な一言がとても心に残りました.

細かい文脈などは忘れてしまったんですが,それだけが心に残っています.

やってみる

「まぁ,そりゃそうだな」という事で参考文献ばかり漁らずにやってみることにしました.
とりあえず小さく始めることがベストという事で極小のlisp定義を漁ります.

するとwikipediaで以下を見つけました.

純LISP

「あぁ,こいつならなんとかなるかも」という事でターゲットをこれに絞ります.

1st try

Lispならまぁ大体

  • Readで読んでS式にして
  • Evalで実行し
  • Printで実行結果のS式を文字列にする

という3工程だろうと考えました.

あとはデータ構造ですが,これはLispなら大変わかりやすいので(アトムとペアしかない)何となくこの2つを作ればうまくいくだろうということで着手します.

これで8末くらいから初めて9月末くらいでReadして何かしらEvalしてPrintというところまでこぎつけました.(car, cdrとかは動いた)

が,変数スコープなどを足そうとすると一気に難しくなります.

このあたりで既存コードで作り続けるのは辛いなという事で一度挫折しました.

2nd try

ならもう作り直せばいいわ.という事でゼロからもう一度作り始める事にしました.

1度作ってるだけあってリトライは割とうまくいき, 9月末から始めて10月中頃には終える事が出来ました.日数だと8日かな.

実装を終えて

勉強になったというよりはかなり面白かったというのが正直なところです.
勉強がどうとかいうと聞こえはいいですが,結局面白いのが一番だというのが個人的な結論でしょうか. あとはちょっとでもやり続ける事も割と大事でした. 子供もいるのでなかなか時間が作れないのですが, 仕事の休憩中とか5分でも,その5分で1機能作れたりしましたし.

  • やってみればいい
  • 面白ければ何とかなる
  • 1日5分でいいので続ける

あとはあのタイミングでrebuild.fmを聞かなければおそらく挫折していたと思います. ruiさんとmiyagawaさんにはここでひっそりと感謝を.

参考文献

参考サイトが多すぎるので最早覚えてないのですがブラウザのブックマークに残っているものを並べておきます.

純LISP - wikipedia

  • 間違えなく一番参照回数が多い
  • この記事を見なかったらたぶん作ってない
  • あんまり助かったので初めてwikipediaに寄付しました

rui314/minilisp

  • ruiさんによるlisp実装
  • 機能おおすぎ凄い(macroまであるっぽい)
  • コードきれいだしすごい
  • 困って頭抱えた時に参照した.
  • なので似たコードがちょいちょいあると思う(evalListとか名前一緒)

MiSPLi

  • jsで実装されているlisp
  • かなり良く出来ていてカッコイイ
  • たまにlispの一般的な挙動を確認したいときに使いました

Common Lisp で作る micro Scheme

  • 作り方というよりLISP自体に関する解説がわかりやすかった

ガベージコレクションの実装法と評価

  • どこかの大学の資料(どこなんだろ?確認してないですすんません)
  • GC作る時に役立った

Rubyソースコード完全解説 - 第5章 ガ-ベージコレクション

  • GC作る時にとても役立った
  • 実は以前に一度読んでいたので読みやすかった
  • ありがとう, ありがとう

竹内関数 - wikipedia

  • これを動かせるようにしないと終わらない(気がした)