golang色々

暇を見てGo言語を試し中。適当に簡単そうなパッケージとかを使いながらGoっぽい書き方に慣れてみることにする。

オブジェクトの初期化方法色々

サンプルコードとかを見ていると、オブジェクト生成の書き方はいくつかあって、型名{〜} を使う方法、new を使う方法、make を使う方法などがあるようだ。まだそれぞれの使い方とか特徴とかまだよく分かってないけど 型名{〜} の使い方は何となくわかったのでメモ。
↓こんな感じに書けるようだ。

package main

import (
    "fmt";
)

type A struct {
    a int;
    b string;
}

func main() {
    //typeで定義された順番に初期値を代入する感じ
    var a1 A = A{1, "a1"};
    
    //フィールド名を指定した初期化も出来る
    var a2 A = A{a:2, b:"a2"};

    //フィールド順とフィールド名指定を同時に扱うことは出来なくて、コンパイルエラーになる
    //var a3 A = A{3, b:"a3"};
    
    //ポインタの場合はこんな感じでOK
    var a4 *A = &A{4, "a4"};

    //newすることも出来る模様、newによって何が起こるのかの詳細はまだ理解していない(^^;
    var a5 *A = new(A);

    //varと型を書くのが面倒な場合は := を使う方が楽
    a6 := A{6, "a6"};
    a7 := &A{7, "a7"};

    //ポインタは初期化しないとnilが入る
    var a8 *A;

    //2次元配列の初期化とかはちょっとごちゃごちゃする感じだな
    a9 := [][]int{[]int{1,2}, []int{11,22,33}};
    
    //変数の内容確認には %#v が便利!
    fmt.Printf("%#v\n", a1);
    fmt.Printf("%#v\n", a2);
    //fmt.Printf("%#v\n", a3);
    fmt.Printf("%#v\n", a4);
    fmt.Printf("%#v\n", a5);
    fmt.Printf("%#v\n", a6);
    fmt.Printf("%#v\n", a7);
    fmt.Printf("%#v\n", a8);
    fmt.Printf("%#v\n", a9);
}

↑の実行結果がコレ↓

main.A{a:1, b:"a1"}
main.A{a:2, b:"a2"}
&main.A{a:4, b:"a4"}
&main.A{a:0, b:""}
main.A{a:6, b:"a6"}
&main.A{a:7, b:"a7"}
(*main.A)(nil)
[][]int{[]int{1, 2}, []int{11, 22, 33}}

一定時間毎に処理を繰り返すには

timeパッケージのtime.NewTickerかtime.Tickを使えば良いようだ。
NewTickerとTickはどちらも同じような働きをする関数だが戻り値がチャンネルだけなのかTickerかの違いがある。後者の方がStopメソッドでTickerの制御が出来る分良い気がする。実行間隔をナノ秒で指定してTickerを受け取り、あとはticker.CというチャンネルからTicker作成時に指定した時間間隔で、その時点での現在時刻のナノ秒が取り出されるという仕組みのようだ。

package main

import (
    "time";
    "fmt";
)

func main() {
    //1秒単位のティッカーを作成する
    ticker := time.NewTicker(1000*1000*1000);
    for {
        //チャンネルから値が出てくるのを待つ
        ns := <-ticker.C;
        t := time.SecondsToLocalTime(ns /1000/1000/1000);
        fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second);
    }
}

↑の実行結果がコレ↓

2009-11-19 02:22:19
2009-11-19 02:22:20
2009-11-19 02:22:21
2009-11-19 02:22:22
2009-11-19 02:22:23
:

ここではチャンネルの使い方のひとつを理解した。

ディレクトリを再帰的に辿って処理をするには

pathパッケージのpath.Walkを使えば良いようだ。
path.Walk は引数に Visitor というインターフェースを取るので、それを実装したタイプを作って引数に渡すことで find のような処理が出来るようになるらしい。とりあえず適当に使ってみた。

package main

import (
    "fmt";
    "path";
    "os";
)

type SimpleVisitor struct {
}

func (v *SimpleVisitor) VisitDir(path string, d *os.Dir) bool {
    fmt.Printf("d: %s\n", path);
    return true;
}

func (v *SimpleVisitor) VisitFile(path string, d *os.Dir) {
    fmt.Printf("f: %s\n", path);
}

func main() {
    v := &SimpleVisitor{};
    errors := make(chan os.Error);
    path.Walk("/etc/httpd", v, errors);
}

↑これの実行結果はコレ↓

d: /etc/httpd
d: /etc/httpd/conf
f: /etc/httpd/conf/httpd.conf
f: /etc/httpd/conf/magic
d: /etc/httpd/conf.d
f: /etc/httpd/conf.d/README
f: /etc/httpd/conf.d/mod_dnssd.conf
f: /etc/httpd/conf.d/php.conf
f: /etc/httpd/conf.d/proxy_ajp.conf
f: /etc/httpd/conf.d/welcome.conf
f: /etc/httpd/logs
f: /etc/httpd/modules
f: /etc/httpd/run

ほぼ予想通りの動作だ。ちなみにVisitDirでfalseを返すとそのディレクトリの中は辿らなくなる。またシンボリックリンクはVisitFileの方で扱われるのでシンボリックリンクディレクトリも辿りたい場合はVisitFile内でdir.IsSymlink()とかdir.IsDirectory()をチェックして、ここは自分でpath.Walkを改めて実行してやる必要はあるようだ。
ここではインターフェースの実装方法について理解できた気がする。

プログラムの終了方法色々

golang/逆引き/プログラムの終了方法色々

  • main をreturnするだけ
  • os.Exit(code int) を使う
  • panic()、panicln() を使う

などが使えるようです。

はてダでハイライト出来ないことに不満

はてなアイデア - スーパープレ記法でGoogle発のプログラム言語「Go」のハイライトが使えるようにしてほしい。http://golang.org/
とりあえずアイデアとして出してみた。

追記)実装された!