2011/03/08


下記質問にそれぞれ50文字以内を目安に簡単に説明すること。
  1. パッケージ内に定義した関数を外部に公開するにはどうすれば良いか?
  2. 非同期に処理を行う為の命令は?
  3. 関数を抜けた際に処理を実行するにはどうするか?
  4. goroutineの同時実行数を変更するにはどうするか?
  5. コンパイラやリンカが8g/6g/5g、8l/6l/5lという名前になっている理由は?
  6. Goのガベージコレクションの実装は一般的に何と呼ばれている類か?
  7. レシーバがnilの場合にメソッドを呼び出すと何が起きるか?
  8. 可変個引数はどの様に定義するか?
  9. 関数内で定義されるローカル変数のアドレスを戻り値として外部から参照するとどうなるか?
  10. interfaceとstructの違いは何か?
  11. panicを補足して強制終了させない為にはどうするか?

続きを読む...

Posted at by



2011/03/06


この時をずっとまってた。
go-gtk runs on win32!
Google製の新しいコンパイル型言語Go上で動くGTK GUIライブラリ、go-gtkが今日、windowsで動いた。
Xming上で動いている様にも見えますが、ちゃんとwin32 nativeなGTKで動いてます。
まずは一般的なGUIのサンプル。
go-gtk-win32-demo
そしてgoroutineで非同期にtwitter public timeline(非streaming API)を読み込むサンプル。
go-gtk-win32-twitter

だいたい動きました。ここ数日、cgoのwin32対応に関するsubmitが多くなってたので、そろそろ動くかなと思って着手しました。win32向けに対応した部分としては実はそんなに多くないです。go-gtkはイベントポーリングを自前でやっていて、その排他制御にpthreadを使っているんですが、win32だとpthread_mutex_lock/pthread_mutex_unlockでうまく排他制御出来なかった(gdkのスレッドと相性が良くないのか落ちる)ので、win32ではクリティカルセクションを使う様にお得意の#ifdefで修正しました。
gtksourceviewの一部で落ちるのを1件確認してますが、その他はほぼ動きました。

GUIが扱えるスクリプト言語は数ありますが、nativeにコンパイル出来てオブジェクト思考で、非同期(goroutine)が使えるGoでGUIライブラリがwindows上で動いたのは、正直大きいと個人的には思っています。

Goの高速なコンパイルと、Duck Typingによる自由度の高い型変換により脳内で設計した物が思考停止する事なくアウトプット出来るのです。
同じコードで比べないと比較になりませんが、C言語でcurlとgtkを使ってtwitterのpublic timelineをGUI表示(アイコン表示あり)というコードがあったとして、僕のノートPC(Intel Core i5)ではコンパイル/リンクがだいたい5秒程度くらいかかりますが、go-gtkの同等サンプルexample/twitterだと2秒くらいでビルド出来ます。

これからGoでGUIを使ったアプリケーションを作りたいという方にはぜひ試して欲しいと思います。

Goがwindowsで動き出してまだホヤホヤですが、これから普及していく事を望んでます。

はじめての「Go言語」: Googleが提供する高速コンパイラ言語 (I/O BOOKS) はじめての「Go言語」: Googleが提供する高速コンパイラ言語 (I/O BOOKS)
茨木 隆彰
工学社 単行本 / ¥2,090 (2010年11月01日)
 
発送可能時間:

Posted at by



2011/02/24


新しい言語を始めて慣れてくると、必ず作りたくなるのがblosxomですね。(ですよね?)
て事でGoでblosxomライクなのを作った。cgiも出来なくないけど、GoはWebに強かったりもするので、web.goを使ってサーバとして書いた。
名前は「blogo」(ブロゴー!えっ)。blosxomライクにテキストファイルを読み込んで、タイトルと本文をこしらえます。
今回はHTMLのテンプレートエンジンとしてmustache(マツタケじゃないよ)を選びました。
結構ハマってしまったけど、一通り動いた。設定ファイル(JSON)でフォルダ位置やタイトル、サブタイトル、リンク一覧なんかを変更出来ます。見た目はこんな感じ。
blogo
短いのでソース全部のっける。
package main

import "bytes"
import "html"
import "io"
import "io/ioutil"
import "mustache"
import "os"
import pathutil "path"
import "regexp"
import "strings"
import "time"
import "web"
import "json"

type Tag struct {
    Name string
}

type Entry struct {
    Id       string
    Filename string
    Title    string
    Body     string
    Created  *time.Time
    Category string
    Author   string
    Tags     []Tag
}

func toTextChild(w io.Writer, n *html.Node) os.Error {
    switch n.Type {
    case html.ErrorNode:
        return os.NewError("unexpected ErrorNode")
    case html.DocumentNode:
        return os.NewError("unexpected DocumentNode")
    case html.ElementNode:
    case html.TextNode:
        w.Write([]byte(n.Data))
    case html.CommentNode:
        return os.NewError("COMMENT")
    default:
        return os.NewError("unknown node type")
    }
    for _, c := range n.Child {
        if err := toTextChild(w, c); err != nil {
            return err
        }
    }
    return nil
}

func toText(n *html.Node) (stringos.Error) {
    if n == nil || len(n.Child) == 0 {
        return ""nil
    }
    b := bytes.NewBuffer(nil)
    for _, child := range n.Child {
        if err := toTextChild(b, child); err != nil {
            return "", err
        }
    }
    return b.String(), nil
}

func GetEntry(filename string) (entry *Entry, err os.Error) {
    fi, err := os.Stat(filename)
    if err != nil {
        return nil, err
    }
    f, err := os.Open(filename, os.O_RDONLY, 0)
    if err != nil {
        return nil, err
    }
    b, err := ioutil.ReadAll(f)
    if err != nil {
        return nil, err
    }

    in_body := false
    re, err := regexp.Compile("^meta-([a-zA-Z]+):[:space:]*(.*)$")
    if err != nil {
        return nil, err
    }
    for n, line := range strings.Split(string(b), "\n", -1) {
        line = strings.TrimSpace(line)
        if n == 0 {
            entry = new(Entry)
            entry.Title = line
            entry.Filename = pathutil.Clean(filename)
            entry.Tags = []Tag{}
            entry.Created = time.SecondsToUTC(fi.Ctime_ns / 1e9)
            continue
        }
        if n > 0 && len(line) == 0 {
            in_body = true
            continue
        }
        if in_body == false && re.MatchString(line) {
            submatch := re.FindStringSubmatch(line)
            if submatch[1] == "tags" {
                tags := strings.Split(submatch[2], ",", -1)
                entry.Tags = make([]Tag, len(tags))
                for i, t := range tags {
                    entry.Tags[i].Name = strings.TrimSpace(t)
                }
            }
            if submatch[1] == "author" {
                entry.Author = submatch[2]
            }
        } else {
            entry.Body += strings.Trim(line, "\r") + "\n"
        }
    }
    if entry == nil {
        err = os.NewError("invalid entry file")
    }
    return
}

type Entries []*Entry

func (p *Entries) VisitDir(path string, f *os.FileInfo) bool { return true }
func (p *Entries) VisitFile(path string, f *os.FileInfo) {
    if strings.ToLower(pathutil.Ext(path)) != ".txt" {
        return
    }
    if entry, err := GetEntry(path); err == nil {
        *p = append(*p, entry)
    }
}

func GetEntries(path string, useSummary bool) (entries *Entries, err os.Error) {
    entries = new(Entries)
    e := make(chan os.Error)
    pathutil.Walk(path, entries, e)
    for _, entry := range *entries {
        if useSummary {
            doc, err := html.Parse(strings.NewReader(entry.Body))
            if err == nil {
                if text, err := toText(doc); err == nil {
                    if len(text) > 500 {
                        text = text[0:500] + "..."
                    }
                    entry.Body = text
                }
            }
        }
        entry.Id = entry.Filename[len(path):len(entry.Filename)-3] + "html"
    }
    if len(*entries) == 0 {
        entries = nil
    }
    return
}

type Config map[string]interface{}

func (c *Config) Set(key string, val interface{}) {
    (*c)[key] = val
}

func (c *Config) Get(key stringstring {
    val, ok := (*c)[key].(string)
    if !ok {
        return ""
    }
    return val
}

func LoadConfig() (config *Config) {
    root, _ := pathutil.Split(pathutil.Clean(os.Args[0]))
    b, err := ioutil.ReadFile(pathutil.Join(root, "config.json"))
    if err != nil {
        println(err.String())
        return &Config{}
    }
    err = json.Unmarshal(b, &config)
    return
}

func main() {
    config := LoadConfig()
    web.Get("/(.*)"func(ctx *web.Context, path string) {
        config = LoadConfig()
        datadir := config.Get("datadir")
        if path == "" || path[len(path)-1] == '/' {
            dir := pathutil.Join(datadir, path)
            stat, err := os.Stat(dir)
            if err != nil || !stat.IsDirectory() {
                ctx.NotFound("File Not Found")
                return
            }
            var useSummary = false
            if config.Get("useSummary") != "" {
                useSummary = true
            }
            entries, err := GetEntries(dir, useSummary)
            if err == nil {
                ctx.WriteString(mustache.RenderFile("entries.mustache",
                    map[string]interface{}{
                        "config":  config,
                        "entries": entries}))
                return
            }
        } else if len(path) > 5 && path[len(path)-5:] == ".html" {
            file := pathutil.Join(datadir, path[:len(path)-5] + ".txt")
            _, err := os.Stat(file)
            if err != nil {
                ctx.NotFound("File Not Found")
                return
            }
            entry, err := GetEntry(file)
            if err == nil {
                ctx.WriteString(mustache.RenderFile("entry.mustache",
                    map[string]interface{}{
                        "config": config,
                        "entry":  entry}))
                return
            }
        }
        ctx.Abort(500"Server Error")
    })
    web.Config.RecoverPanic = false
    web.Config.StaticDir = config.Get("staticdir")
    web.Run(config.Get("host"))
}
それと、今回初めてCSS FrameworkであるBlueprintを使った。まぁ確かに慣れてると使いやすいのかもしれないけど、CSSそんなに知らない人が使い始められる物じゃないなと思った。Goのコード書くよりこのレイアウト作る方に時間がかかったという...
mattn/blogo - GitHub

blogo : blosxom like blog engine for golang

http://github.com/mattn/blogo
Posted at by