2009/11/24


最初Goがリリースされた時には、自分がPortingするんだーとか意気込んでましたが、google codeにホスティングされている方のほうが良い物作ってくれそうだったので、ずっと見守ってました。
hectorchu-go-windows - Project Hosting on Google Code

go for Windows

http://code.google.com/r/hectorchu-go-windows/
Windows PEのリンカも入って、適当な物ならばコンパイル&リンク出来る様になってます。
ただ一般的なWindows Portingの問題として、POSIXなAPIであるpipeやfork、ソケットディスクリプタ等といったWindowsで完全に模倣出来ない部分が残っています。例えばhttpクライアントを扱うhttpパッケージは、netパッケージに依存し、かつ内部ではos.Fdを使う為にそのままのコードではWindowsで実行出来ません。さらにgodoc等ファイルおよび外部プログラムを扱う様な物は内部でos.Pipe()、os.Fork()が呼ばれており、こちらもWindowsなAPIで置き換えなければなりません。
現在の所、src/pkg/osとは別にsrc/pkg/windows/osというフォルダで、Win32 APIを使ってosパッケージを模倣され様としています。移植という点ではこれからになりますね。Issue Trackerが開かれていないのでバグ報告出来ないですが、もし気になる事があればメーリングリストに投げようかなと思っています。

ちなみに、スレッドまわりの実装は既に入っているのでgoroutineは動きます。
ぜひ遊んでみましょう!
Posted at by



2009/11/23


昨日書いたGoのIRCボットに手を入れ、mecabで要素解析する様にした。色々弄ってたら、某所で有名な「悪く言うなbot」になってた。
goによるmecab wrapperの実装がある事をtokuhiromさんに教えてもらったので、さっそく使わせて頂いた。
package main

import (
  "fmt";
  "http";
  "bytes";
  "json";
  "strings";
  "io";
  "mecab";
)

func get_json(url string, data map[string]string) json.Json {
  q := "";
  for key, val := range data {
    if (len(q) > 0) { q += "&" }
    q += fmt.Sprintf("%s=%s", key, http.URLEscape(val));
  }
  bb := &bytes.Buffer{};
  bb.Write(strings.Bytes(q));
  r, err := http.Post(url + "?" + q, "application/x-www-form-urlencoded", nil);
  if err == nil {
    b, _ := io.ReadAll(r.Body);
    r.Body.Close();
    j, _, _ := json.StringToJson(string(b));
    return j
  }
  return nil
}

func get_sid(nick string) string {
  j := get_json("http://webchat.freenode.net/e/n", map[string]string{"nick":nick});
  return j.Elem(1).String();
}

func login(nick *string) string {
  sid := get_sid(*nick);
  for {
    j := get_json("http://webchat.freenode.net/e/s", map[string]string{"s":sid});
    if j == nil {
      break;
    }
    for i := 0; i < j.Len(); i++ {
      data := j.Elem(i);
      println(data.String());
      if (data.Elem(0).String() != "c") {
        continue;
      }
      if (data.Elem(1).String() == "433") {
        *nick += "_";
        sid = get_sid(*nick);
      }
      if (data.Elem(1).String() == "376") {
        return sid;
      }
    }
  }
  return "";
}

func join(sid string, room string) bool {
  j := get_json("http://webchat.freenode.net/e/p", map[string]string{"s":sid, "c":"JOIN " + room});
  if j == nil {
    return false;
  }
  return true;
}

func say(sid string, room string, msg string) bool {
  j := get_json("http://webchat.freenode.net/e/p", map[string]string{"s":sid, "c":"NOTICE " + room + " :" + msg});
  if j == nil {
    return false;
  }
  return true;
}

func listen(sid string, nick string, room string) {
  for {
    j := get_json("http://webchat.freenode.net/e/s", map[string]string{"s":sid});
    if j == nil {
      continue;
    }
    for i := 0; i < j.Len(); i++ {
      data := j.Elem(i);
      println(data.String());
      if (data.Elem(0).String() != "c") {
        continue;
      }
      if (data.Elem(1).String() != "PRIVMSG") {
        continue;
      }
      line := data.Elem(3);
      if (line.Elem(0).String() == nick) {
        continue;
      }
      s := line.Elem(1).String();
      m := mecab.New2("");
      r := mecab.SparseToStr(m, s);
      ss := strings.Split(r, "\n", 999);
      for l := 0; l < len(ss); l++ {
        st := strings.Split(ss[l], "\t", 2);
        if (len(st) < 2) {
          continue;
        }
        if (strings.Index(st[1], "固有名詞") != -1 ||
            strings.Index(st[1], "人名") != -1 ||
            strings.Index(st[1], "組織") != -1) {
          say(sid, room, st[0] + "は良い奴だ。悪く言うな。俺が許さん。");
        }
      }
    }
  }
}

func main() {
  nick := "go-japan";
  room := "#mattn";
  sid := login(&nick);
  if (len(sid) > 0) {
    if (join(sid, room)) {
      listen(sid, nick, room);
    }
  }
}
channelに固有名詞や人名等が現れると「XXXは良い奴だ。悪く言うな。俺が許さん。」と発言します。
mecabライブラリについては、やっぱりfeatureとかsurfaceを扱いたいのでこれからに期待したいと思います。まぁ、現状でも上記の様にstrings.Split使えば出来なくないんですけどね。

なんとなくだけど、だんだん「C++書きの為のGo言語」と言われてる理由が分かって来た気がする。
Posted at by




とは言っても、freenodeのWebインタフェース使ってJSONやり取りして、発言するだけの物。以前C++で作った全裸botの簡易版といった所か。
動かすと、freenodeの"golang-daisuki"(仮)にJOINして「郷です!ジャパーーーン!」と発言した後に死亡します。
嫌がらせには持って来いですね。

package main

import (
  "fmt";
  "http";
  "bytes";
  "json";
  "strings";
  "io";
)

func get_json(url string, data map[string]string) json.Json {
  q := "";
  for key, val := range data {
    if (len(q) > 0) { q += "&" }
    q += fmt.Sprintf("%s=%s", key, http.URLEscape(val));
  }
  bb := &bytes.Buffer{};
  bb.Write(strings.Bytes(q));
  // freenodeはPOSTメソッドでGETクエリをおくらなきゃ駄目
  r, err := http.Post(url + "?" + q, "application/x-www-form-urlencoded", nil);
  if err == nil {
    b, _ := io.ReadAll(r.Body);
    r.Body.Close();
    j, _, _ := json.StringToJson(string(b));
    return j
  }
  return nil
}

func get_sid(nick string) string {
  j := get_json("http://webchat.freenode.net/e/n", map[string]string{"nick":nick});
  return j.Elem(1).String();
}

func login(nick string) (string, string) {
  sid := get_sid(nick);
  for {
    j := get_json("http://webchat.freenode.net/e/s", map[string]string{"s":sid});
    if j == nil {
      break;
    }
    for i := 0; i < j.Len(); i++ {
      data := j.Elem(i);
      println(data.String());
      if (data.Elem(0).String() != "c") {
        continue;
      }
      if (data.Elem(1).String() == "433") {
        nick += "_";
        sid = get_sid(nick);
      }
      if (data.Elem(1).String() == "376") {
        return sid, nick;
      }
    }
  }
  return "", "";
}

func join(sid string, room string) bool {
  j := get_json("http://webchat.freenode.net/e/p", map[string]string{"s":sid, "c":"JOIN " + room});
  if j == nil {
    return false;
  }
  return true;
}

func say(sid string, room string, msg string) bool {
  j := get_json("http://webchat.freenode.net/e/p", map[string]string{"s":sid, "c":"NOTICE " + room + " :" + msg});
  if j == nil {
    return false;
  }
  return true;
}

func main() {
  nick := "go-japan";
  sid, _ := login(nick);
  if (len(sid) > 0) {
    if (join(sid, "#golang-daisuki")) {
      say(sid, "#golang-daisuki", "郷です!ジャパーーーン!");
    }
  }
}
作ってみた感想としては、httpまわりにもう少しPOSTに便利なメソッドが欲しいのと、Postメソッドのデフォルトがcheckedになっている(Postメソッドでは変えれない)ので古くさいサーバで動かないんじゃないかと思ったり。

ただC++版に比べて幾らか短く書けたし、今回のコードでは例外等処理してないけど、例外処理を書きたくなった時にも簡単に実装が出来るのも分かった。

ところでpublicメソッドはメソッド名を先頭大文字にするってルールはいいけど、少しだけオモチャっぽく感じてしまうのは私だけだろうか。
Posted at by