2013/06/20


元ネタ: 誰もが一度は陥る日付処理。各種プログラミング言語におけるDateTime型/TimeStamp型の変換方法のまとめ
Go言語が無かったので書いてみた。

現在時刻の取得

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println(time.Now())
}
2013-06-19 21:46:14.186298 +0900 +0900

Time => Unix時刻変換

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println(time.Now().Unix())
}
1371646123

Unix時刻 => Time変換

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println(time.Unix(13716461230))
}
ここからが面白くて、通常日付のフォーマットやパースは殆どの言語では %Y%m%d といった表記を使います。 しかしGo言語では、ある固定の数値を用いて表現する事で、いかにも日付らしく表現出来る手法を取っています。 package main

import (
    "fmt"
    "log"
    "time"
)

func main() {
    t, err := time.Parse(
        "2006-01-02 15:04:05 -0700",    // スキャンフォーマット
        "2013-06-19 21:54:23 +0900")    // パースしたい文字列
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(t)
}
Formatも同じく package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println(time.Now().Format("2006/01/02 15:04:05 MST"))
}
2013/06/19 22:01:01 +0900 この数値とタイムゾーン文字列で構成された文字列を、定数として提供しています。 const (
    ANSIC       = "Mon Jan _2 15:04:05 2006"
    UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
    RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
    RFC822      = "02 Jan 06 15:04 MST"
    RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
    RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
    RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
    RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
    RFC3339     = "2006-01-02T15:04:05Z07:00"
    RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
    Kitchen     = "3:04PM"
    // Handy time stamps.
    Stamp      = "Jan _2 15:04:05"
    StampMilli = "Jan _2 15:04:05.000"
    StampMicro = "Jan _2 15:04:05.000000"
    StampNano  = "Jan _2 15:04:05.000000000"
)
この手法を使う事で、ちょっとした日付フォーマットの差異も自分のプログラムで吸収する事が出来る様になっています。
初めてこの実装を見た時は戸惑いましたが、慣れると非常に心地よくなります。

おまけでもう少し。

Go言語では、timeパッケージを使って経過時間(Duration)も表す事が出来ます。 time.Sleep(1 * time.Second) // 1秒
time.Sleep(2 * time.Minute) // 2分
time.Sleep(3 * time.Hour)   // 3時間
1時間と61秒は、61分 (1 * time.Hour + 61 * time.Second).Minutes()
日付の加算も出来る。閏年も完璧。 package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println(time.Date(20122281213140, time.UTC).AddDate(001))
    // 2012-02-29 12:13:14 +0000 UTC
    fmt.Println(time.Date(20122291213140, time.UTC).AddDate(001))
    // 2012-03-01 12:13:14 +0000 UTC
}
このDurationを使って、数秒後に発動するイベント(channel)も作れます。例えば、3秒以内に CTRL-C を押すかのイベントループ package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    s := make(chan os.Signal)
    signal.Notify(s, syscall.SIGINT)

    select {
    case <- time.After(3 * time.Second):
        fmt.Println("Timeout")
        break
    case <- s:
        fmt.Println("Pressed CTRL-C")
        break
    }
}
3秒後にコールバック package main

import (
    "fmt"
    "os"
    "time"
)

func main() {
    time.AfterFunc(3 * time.Second, func() {
        fmt.Println("Timeout")
        os.Exit(0)
    })

    select {}
}
とまぁ、とても便利になっています。Go言語触った事ないとか、かなりヤバいです。

「えーマジGo言語童貞!?」
「Go言語童貞が許されるのは小学生までだよね」
「キモーイ」
「キャハハハハハハ」
Posted at by



2013/05/28


なんびとたりとも俺の前は走らせねぇ
ガチバトルです。
rapidjson - A fast JSON parser/generator for C++ with both SAX/DOM style API - Google Project Hosting

Rapidjson is an attempt to create the fastest JSON parser and generator. Small but complete. Support...

https://code.google.com/p/rapidjson/
fastest をうたうとは度胸があるなーと思いながら、半信半疑で試してみました。
rapidjson も picojson 同様に、ヘッダファイルだけあればコンパイル出来る C++ 向け JSON パーサ(およびシリアライザ)です。
まずは rapidjson を使ったコード。
#include <rapidjson/document.h>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <time.h>

int
main(int argc, char* argv[]) {
  clock_t t;
  t = clock();
  for (int n = 0; n < 1000; n++) {
    std::stringstream ss;
    std::ifstream f;
    f.open("foo.json", std::ios::binary);
    ss << f.rdbuf();
    f.close();

    rapidjson::Document doc;  
    if (doc.Parse<0>(ss.str().c_str()).HasParseError()) {
      std::cerr << "parse error" << std::endl;
      return 1;
    }

    rapidjson::Value& entries = doc["entries"];
    int i, l = entries.Size();
    for (i = 0; i < l; i++) {
      std::cerr << entries[rapidjson::SizeType(i)]["title"].GetString() << std::endl;
    }
  }
  std::cout << "score: " << clock() - t << std::endl;
  return 0;
}
そして picojson を使ったコード #include <picojson.h>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <time.h>

int
main(int argc, char* argv[]) {
  clock_t t;
  t = clock();
  for (int n = 0; n < 1000; n++) {
    std::stringstream ss;
    std::ifstream f;
    f.open("foo.json", std::ios::binary);
    ss << f.rdbuf();
    f.close();

    picojson::value v; ss >> v;
    picojson::array a = v.get<picojson::object>()["entries"].get<picojson::array>();
    int i, l = a.size();
    for (i = 0; i < l; i++) {
      std::cerr << a[i].get<picojson::object>()["title"].to_str() << std::endl;
    }
  }
  std::cout << "score: " << clock() - t << std::endl;
  return 0;
}
picojson については、std::for_each を使っても見たけど結局自前ループで回した方が速い結果になりました。

コンパイルは両者とも g++ -std=c++11 -O2 foo.cxx でやりました。
読み込んでる JSON はこれを使いました。ウチのサイトの JSON 形式のフィードです。やってる内容は
  • ファイルを読み込む
  • パースする
  • entries の一覧を取る
  • 各 title の内容を標準エラーに出力する
これを1000回実行します。標準エラーは捨てながら計測しました。

結果は以下の通り。 rapidjson score: 3559
picojson score: 7961
こうなりました。picojson と比べて rapidjson がほぼ2倍速いという結果になりました。速い理由をソースを見ながら調べてみましたが、rapidjson は STL を使わず、自前で DOM ライクな API を提供しています。また std::string なども使いません。これによってオーバーヘッドを最小限に抑えているのだと思います。
もちろん、picojson の STL フレンドリも僕は好きですし、他の C++ ライブラリと連携する際にも便利です。

Performance - rapidjson - Compares some performance benchmark results of rapidjson 0.1 and other parsers. Also gives some analysis. - A fast JSON parser/generator for C++ with both SAX/DOM style API - Google Project Hosting

Why rapidjson is fast? rapidjson has made use of several techniques to achieve high performance. C++...

https://code.google.com/p/rapidjson/wiki/Performance
それどころか、このスコア表を見ると picojson のスコアは素晴らしいレベルだと分かりました。
唯一難点として rapidjson は確かに速いのですが、パースエラー詳細に取る方法が分かりませんでした。(あるのかもしれません。知ってる人いたら教えて下さい)

という事で、これからも picojson 使って行こうと思います。
Posted at by



2013/05/16


IRC クライアントを使って lingr 上のコミュニケーションを楽しむ場合
ruby であれば lig.rb
psychs/lingr-irc - GitHub
https://github.com/psychs/lingr-irc
perl であれば lingr-ircd
tokuhirom/lingr-ircd - GitHub
https://github.com/tokuhirom/lingr-ircd
がありますが、Go言語にもあるべきだろ!と思ったので書きました。
mattn/go-lingr - GitHub
https://github.com/mattn/go-lingr
まず IRC に関係なく Lingr API を操作するライブラリ。そしてそれを使って IRC サーバと見立てる lingr-ircd を書きました。
lingr-ircd は go-lingr の中に含まれています。

API キーをお持ちの方は下記の様に指定して下さい。
$ lingr-ircd -apikey="XXXXX" これであとはお好きな IRC クライアントから 26667 番ポート(-addr=":26668" の様に変更可能)に接続して lingr をお楽しみ下さい。

ちなみに lingr で Go言語 の部屋が無かったのでさっき作りました。
Go言語 – Lingr
http://lingr.com/room/golang
ゆるふわと、Go言語について語りましょう。
Posted at by