goでも(3)

義姉さんの所がパラボラを付けたって言ってたんで、見に行った。二階の軒下に付いて いたそ。雪害は大丈夫? 雪が積もったと言って、簡単に箒で払う事も難しそう。 寒冷地仕様のパラボラとかで、熱線ヒーター付きってないのかな?

それはさておき、何で衛星受信って聞いたら、最近民放化してるNHKに乗せられて、BSで しかやってない番組を見たい為とか。わうわうNHK。地デジは、釣り上げ放送だな。ここにも 哀れな獲物がいましたよ。

で、相談された。夜の夜中にやってる見たい番組に付き合わなくちゃいけないの?そんな 話の主人公は、ベテラン視聴者である女房の出番。ハードディスクを付けて、録画しとけば 良いのよ。あら? うちのTVにも付けられるのかしら?

で、取説を見せてもらったら、USBポートが2口も有るじゃないですか。どこのTVかと思ったら、 小雪さんのTVでした。その場で、HDDの取り付けが決定。機材の調達と設置は、 おいらに振られたのは言うまでもありません。まあ、五月蝿い姉妹には勝てないって、昔から 決まってますから。

後日、山田さんを訪問。いろいろ見せてもらうと、オイラーが付けた時より、随分と 高騰してる。これって、安倍ちゃんのインフレ作戦効果? USB3とかいらん機能(for 4K TV)のてんこ盛りで 値段を釣り上げるメーカーばかり。一番安いのを買ってきた。中身のHDDは何が入っているんだろう? HDD約3万5000台を運用した実績からSeagate製品の圧倒的壊れっぷりが明らかに が入っていたら、いやだな。HDDには寿命予測の資料となるようなデータを収集する機能、 SMARTが搭載されてるはずだけど、民生用はどうなんだろう。いろんなパラメータを収集 してるけど、専門家はこんなパラメータに注目してるらしい。

最近はHDDなんて言わないで、レコーダーって名前が通り相場なのね。需要の大半は、TVに 取り付けるものと思うぞ。

接続して電源入れたら、HDDが検出されました。初期化しますか。名前を付けますかぐらいの 質問で使えるようになった。録画、再生の扱いは小百合さんのTVより簡単そうだった。 (実は、家にあるTVの扱い方を知らないオイラーなのさ)

特筆すべきは、ワンプッシュで、コマーシャルをスキップ出来る事。これ楽そうでいいな。 女房がうらやましがってたぞ。消費者が望むのは、最初からコマーシャルをスキップしとけよ なんだけど、どのメーカーも、恐くてやらない。(福山さんの所だったかでやろうとしたけど、TV屋の 圧力に負けたな)

最近の番組は、コマーシャルの間に、細切れで番組を見せるんで、女房ブースカ。そんなの 文句言わんと、黙ってTVなんて見なければいいのに。

読書メーター 6冊 / 1848頁 / 10600円

ウブでもgo

ウブ14.04でもgoしたくなった。いきなりgoって叩いたら、gccgo-goを入れるといいよとお告げが有ったんで、 その通りにしたよ。でも、godocとかが入ってなくてちょっと立ち止まってしまったぞ。 で、メタパッケージgolangを入れろと、再度のお告げ。入れましたよ。そしたら、ぐぐる様 発行のgoと言うか、コンパイラーも入ってきた。

これって、贅沢にGNUとぐぐる様の競争が出来るって事? おいらの目的は、ソース編集、 runのサイクルを速く回して欲しいの一点。どっちが速いの? そしてコンパイラの切り替え 方法は? go help buildあたりに書いてあるに違いない。

        -compiler name
                name of compiler to use, as in runtime.Compiler (gccgo or gc).
        -x
                print the commands.

早速競争。まずはGNU製

sakae@uB:~/godev/src/helo-go$ time go build  -compiler gccgo hello.go

real    0m0.212s
user    0m0.100s
sys     0m0.092s
sakae@uB:~/godev/src/helo-go$ ls -l hello
-rwxrwxr-x 1 sakae sakae 22788 Nov 19 16:10 hello

出来上がったものが小さいな。調べてみたら、ダイナミックリンクになってた。libgoなんてのを 参照してるよ。可搬性無しだな。

対する、ぐぐる様製

sakae@uB:~/godev/src/helo-go$ time go build  -compiler gc hello.go

real    0m0.457s
user    0m0.108s
sys     0m0.332s
sakae@uB:~/godev/src/helo-go$ ls -l hello
-rwxrwxr-x 1 sakae sakae 2003856 Nov 19 16:11 hello

時間がGNU製に比べて倍以上。出来た実行ファイルは、スタチックで可搬性有り。

こりゃ、開発をするんならGNU製の方が良いな。 一応、どんなコマンドが実行されてるか見ておく。まずはGNU製

sakae@uB:~/godev/src/helo-go$ go build -x -compiler gccgo hello.go
WORK=/tmp/go-build558638200
mkdir -p $WORK/command-line-arguments/_obj/
cd /home/sakae/godev/src/helo-go
gccgo -I $WORK -c -g -m32 -fgo-relative-import-path=_/home/sakae/godev/src/helo-go -o $WORK/command-line-arguments/_obj/main.o ./hello.go
ar cru $WORK/libcommand-line-arguments.a $WORK/command-line-arguments/_obj/main.o
cd .
gccgo -o hello $WORK/command-line-arguments/_obj/main.o -Wl,-( -m32 -Wl,-)

ぐぐる製

sakae@uB:~/godev/src/helo-go$ go build -x -compiler gc hello.go
WORK=/tmp/go-build732504930
mkdir -p $WORK/command-line-arguments/_obj/
cd /home/sakae/godev/src/helo-go
/usr/lib/go/pkg/tool/linux_386/8g -o $WORK/command-line-arguments/_obj/_go_.8 -p command-line-arguments -complete -D _/home/sakae/godev/src/helo-go -I $WORK ./hello.go
/usr/lib/go/pkg/tool/linux_386/pack grcP $WORK $WORK/command-line-arguments.a $WORK/command-line-arguments/_obj/_go_.8
cd .
/usr/lib/go/pkg/tool/linux_386/8l -o hello -L $WORK $WORK/command-line-arguments.a

はて、どうやって使うコンパイラーを固定する? ぐるぐるしてたら、たまたま見つけたgoのコンパクトな解説 が、為になったぞ。犬も歩けば棒に当たる。

固定方法を見つけられんかった。gccgoに切り替えるには、前回やった、go-modeに変更を加えておけばいいのかな。

いやいや、ウブは最初gccgoになってて、次はググル製になった。独自に切り替えてるんだな。 思い出したぞ。ウブ流と言うかdebian流と言うか、/etc/alternatives/goのリンクで、切り替え てるんだな。

sakae@uB:/etc/alternatives$ ls -l go*
lrwxrwxrwx 1 root root 18 Nov 19 04:48 go -> /usr/bin/golang-go

今は、ご覧-goと言う、ぐぐる様を指しているけど、ここを/usr/bin/gcc-goに変更して、 /usr/bin/goの先を/etc/alternatives/goに変更してあげればいいんだな。リンクを間違えると、 ぐるぐる回って、リンクが深すぎるぞ、ごりゃーと怒られそうだな。きっと、コマンド一発で 切り替えられるようになってるだろうけど、もうこれぐらい分かればいいや。

コード書くぞ

ぐだぐだやっててもしょうがないので、今までいろいろな言語で書いてきた、血圧データの 閲覧プログラムをgoしてみる。まずは、csvファイルを読む所から。

package main

import (
        "encoding/csv"
        _ "fmt"
        "github.com/kr/pretty" // http://godoc.org/github.com/kr/pretty
        "io"
        _ "log"
        "os"
        "strconv"
)

var bld [][4]int // blood data

const ( // rows index name 0 .. 3
        ymdh = iota
        hi
        low
        pls
)

func failOnError(err error) {
        if err != nil {
                //              log.Fatal("Error:", err)
                panic(err)
        }
}

func myread(csvfile string) {
        var rows [4]int
        file, err := os.Open(csvfile)
        failOnError(err)
        defer file.Close()
        reader := csv.NewReader(file)
        for {
                record, err := reader.Read() // read a line
                if err == io.EOF {
                        break
                } else {
                        failOnError(err)
                }
                for i, v := range record {
                        rows[i], err = strconv.Atoi(v)
                        failOnError(err)
                }
                bld = append(bld, rows)
        }
}

func mycsv(csvfile string) {
        var rows [4]int
        file, err := os.Open(csvfile)
        failOnError(err)
        defer file.Close()
        reader := csv.NewReader(file)
        all, err := reader.ReadAll() // read all
        failOnError(err)
        for _, el := range all {
                for i, v := range el {
                        rows[i], err = strconv.Atoi(v)
                        failOnError(err)
                }
                bld = append(bld, rows)
        }
}

func main() {
        //      myread("current.csv")
        mycsv("current.csv")
        pretty.Printf("%# v", bld)
}

データを綺麗に表示したいってんで、探してみたらprettyなんてのが有ったぞ。 GOPATHのtopでgo get すれば、ソースはsrcにコンパイルしたものはpkgに鎮座してくれる。

bldって配列の配列をグローバルに宣言した。ここにcsvファイルから取り込んだデータを 格納予定。goの場合、配列とスライスが在りますって説明が多いんだけど、ごっちゃに なっていないかい。 Go のスライスの内部実装 なんていう記事が出てました。納得しました。でも、スライスってPythonのそれを連想しちゃうんで 紛らわしいな。サイズを拡張出来る配列って方がふさわしいと思うけど、どうよ。

次は、constなんてのが出てきた。定数。変更しようとすると怒られる。今回は、iotaって言う インクリメント演算子と組み合わせて、配列内のインデックス番号を名前で呼べるように してみた。(実例は、次回にでも)

myreadとmycsvは、csvファイルを読んで、値を整数にして格納する。前者は1行づつ処理 する関数。後者は、一気読みしてから整数に変換して格納する関数。メモリー容量さえ気 にしなければ、後者がスマート。EOFの判定なんて分かりきった事だから、裏でやっておいてよだな。

読み込んだ1レコード(1行分)の文字列配列を、数値にしたい。schemeなんかだと、mapを 使って、簡潔に書けるけど、goだと型型言われて、汎用のルーチンは書きにくい。

    var rows [4]int
                for i, v := range record {
                        rows[i], err = strconv.Atoi(v)
                        failOnError(err)
                }

これが定番の書き方になるかな。forにrangeを組み合わせると、配列のサイズ分、勝手に 回ってくれる。今処理してるインデックス番号と値が取り出せるようになっているんで、 それを組み合わせればよい。

実行結果が、どうなったかと言うと

[][4]int{
    {13010103, 118, 80, 60},
    {13010121, 107, 67, 72},
    {13010205, 121, 76, 57},
    {13010221, 121, 72, 71},
    {13022221, 108, 68, 67},
}
Process go-run finished

それぞれのパッケージが提供してる関数は、おおむね、結果とエラーが同時に返ってくる。 schemeの多値だな。エラーを無視するなら、それを積極的に宣言する為、アンダーバーって 変数名にしとくのが、慣習だ。

エラーを検出したら、普通はパニックで停止かな。それをfailofErrorって関数にしてる。 エラーのチェックはきちんとやるべきだ。やらないとどうなるか?

例えば、CSVファイルの数値部分を文字化けさせ、それを読み込む。

sakae@uB:~/godev/src/bld$ go run bld.go
[][4]int{
    {13010103, 118, 80, 60},
    {0, 107, 67, 72},
      :

failofErrorをコメントにしたら、落ちなかったけど、変換値に間違った値が入ってしまった。 じゃ、エラーチェックしとくと

sakae@uB:~/godev/src/bld$ go run bld.go
panic: strconv.ParseInt: parsing "xx13010121": invalid syntax

goroutine 1 [running]:
runtime.panic(0x80d9d80, 0x1842d280)
        /usr/lib/go/src/pkg/runtime/panic.c:266 +0xac
main.failOnError(0xb76da838, 0x1842d280)
        /home/sakae/godev/src/bld/bld.go:24 +0x49
main.mycsv(0x80ebd38, 0xb)
        /home/sakae/godev/src/bld/bld.go:39 +0x175
main.main()
        /home/sakae/godev/src/bld/bld.go:46 +0x2d
exit status 2

panicだと、スタックトレースが出てくる。Fatalだと、一番上のエラーのみが出てくる。 お好きな方をどうぞ。

いちいち、エラー検出の関数を1行費やしてかくのは面倒と言うか目障り。

       rows[i], err = strconv.Atoi(v); failOnError(err)

こう書けないか? 書けるよ。但し、gofmtを通すと、強制的に2行に修正されちゃう。 標準の書き方しろよって言う、無言の圧力ですな。GNUスタイル風の、やたらに行を稼ぐ 書き方も矯正してくれるのかな。あれには辟易しますから。K&Rスタイルに馴染みが ある、オイラーとしては、まあ嬉しいね。今、勝手にK&Rスタイルって書いちゃったけど、 細部にこだわりを持つ人がおられた。 K&Rスタイルで納得できないこと と言う記事を見ると、なるほどね。gofmtでは矯正されてますよ。

ああ、スライスが出てきてたんで、Python/rubyみたいに、配列の尻尾から幾つって 出来るかと思ったぞ。

    pretty.Printf("%# v", bld[-2:])

沈没しました。

# command-line-arguments
./bld.go:45: invalid slice index -2 (index must be non-negative)

どうしてもやりたかったら

    pretty.Printf("%# v", bld[len(bld)-2:])

こんな風にしましょう。 こういうノウハウは、 Twelve Go Best Practices に書いてあるのかなあ?

go debug

ものの資料によると、go言語のプログラムをgdbでデバッグするには、ちょいとした用意が 必要みたい。一番は、pythonをサポートしたgdbを使う事。おいらのウブでは、大丈夫そう。

それから、gdbを起動した時、go側に用意されたpythonスクリプトを実行出来るように、.gdbinitに、

add-auto-load-safe-path /usr/share/go/src/pkg/runtime/runtime-gdb.py

こんな設定を書いておく事。が、gdbを起動した時、runtime-gdb.pyの何箇所かで パイソンがエラーと言ってきたんで、適当に手直し。想定してるパイソンが微妙に違うんですかね。

で、例題。壊れたcsvファイルを読もうとして落ちたとします。

sakae@uB:~/godev/src/bld$ ./bld
panic: strconv.ParseInt: parsing "zz76": invalid syntax

goroutine 1 [running]:
runtime.panic(0x80d9e40, 0x1842d2a0)
        /usr/lib/go/src/pkg/runtime/panic.c:266 +0xac
main.failOnError(0xb7778838, 0x1842d2a0)
        /home/sakae/godev/src/bld/bld.go:24 +0x51
main.mycsv(0x80ebe28, 0xb)
        /home/sakae/godev/src/bld/bld.go:39 +0x345
main.main()
        /home/sakae/godev/src/bld/bld.go:104 +0x2d

これなら、failOnErrorがある24行目あたりにBPを置けばよさそう。

sakae@uB:~/godev/src/bld$ gdb -q bld
Reading symbols from bld...done.
(gdb) b 24
Breakpoint 1 at 0x8048c26: file /home/sakae/godev/src/bld/bld.go, line 24.
(gdb) run
Starting program: /home/sakae/godev/src/bld/bld

Breakpoint 1, main.failOnError (err=...) at /home/sakae/godev/src/bld/bld.go:24
24                      panic(err)
(gdb) up
#1  0x08048fa5 in main.mycsv (csvfile="current.csv")
    at /home/sakae/godev/src/bld/bld.go:39
39                              failOnError(err)
(gdb) p v
$1 = "zz76"
(gdb) info locals
i = 2
rows = {13010205, 121, 0, 72, 406900864, 407130136, 406900864, 407032368,
  407130148, -1209135460, 406847784, 407032376, -1208174560, 407032464,
  406847872, 4, 406847872, 4, -1208174536, 407032480, -1208174560, 406847784,
  407032352, 4, 4, 407130112, 5, 8, 407130112, 5, 8, 407032352, 4, 4,
  407032352, 4, 4, 407032448, 2, 2, 134517821, 135183912, 11, 134626269,
  -1208209392, -1208209408, 0, 134801163, 134626269, 8, 135112992, 0,
  134801163, 135112992, 406847776, 59, -1208174784, 406847776, 134646109,
  135236616, 30, -1208174784, 134597668, 134518323, 134584657, 135937532, 0,
  0, 0, 134584384, 0, 1, -1, 0, 135937516, 0, 0, 134593536,
  0 <repeats 83 times>}
reader = 0x1840d080
file = 0x18400128
v = "zz76"
err = {tab = 0xb7fcb838, data = 0x1842d2a0}
Python Exception <class 'TypeError'> 'gdb.Value' object cannot be interpreted as an integer:
all =  [][]string
Python Exception <class 'TypeError'> 'gdb.Value' object cannot be interpreted as an integer:
el =  []string
(gdb) info goroutines
Python Exception <class 'NameError'> name 'long' is not defined:
Error occurred in Python command: name 'long' is not defined

どうやら、自分で書いたスクリプトをdebugする前に、人様のコードをdebugするはめに なりそうだな。万次郎Linuxだと、ちゃんと動くのかな?

たとえ、info goroutinesが動かなくても、info thread で、代用出来るからいいんだけど。。。 偉そうにgoruotinesなんて言ってるけど、実態はスレッド。もっと言うとプロセスなんだな。 まあ、チャンネルとか言って旨く、低位なやりとりを隠しているのには感心するけどね。

事故に備えよ

ちまちまと、血圧監視プログラムを書いているんだけど、あるfuncを追加して走らせた所 gofmtがエラーだと抜かしおる。それも追加した部分じゃなくて、ずっと先の部分がおかしいと。

はて、追加した所に齟齬が有って、それが原因になって先の方でエラーになったのかな? 昔、rubyでコードを書いていた頃、文字列を閉じるダブルクォートを忘れて、先の方(大体、 最終行)でエラーと言われたからな。

emacsだと、文字列の閉じ記号を忘れると、色が変わってすぐ分かるけど、今回はそのたぐいではなさそう。 こういう時の対処は? 追加した所を、コメントにして、おかしな場所を特定してく。

やってみた。エラーだよ。しょうがないので、コメントの範囲を広げて行った。この方法で 困る事がある。goの場合、 いやらしいのは、importしてるけど使ってないぜぇエラーが出てくる事。こういう事故調査の 時は、一時的にこの機能を無効に出来ればと思うぞ。

で、goのコンパイラーになった積もりで、目grepしましたよ。超ローテクだな。ちゃんとコンパイル 出来てた時のコードが残ってれば、diff出来たのに。。。後の祭りですな。

結局、ifの閉じブラケットが一つ欠落してるのを見つけましたよ。こんなんでも、とんでも ない所でエラーと言われるのね。こういう時に備えて、事故訓練をしとくといいかな。

それとも、ちゃんとコンパイル出来たソースの自動保存がいいかな。ちょいと考えて みよう。候補は、emacs君、何とかしてよですけど。。現在のinit.elを晒しておきますので、 改修案、絶賛募集します。

;; golang
(eval-after-load "go-mode"
  '(progn
     (require 'go-autocomplete nil t)
     (global-auto-complete-mode 1)
     (add-hook 'go-mode-hook 'go-eldoc-setup)
     (add-hook 'before-save-hook 'gofmt-before-save)

     (add-hook 'go-mode-hook
      '(lambda () (setq tab-width 4)))

     ;; key bindings
     (define-key go-mode-map (kbd "M-.") 'godef-jump)
     (define-key go-mode-map (kbd "M-,") 'pop-tag-mark)))

emacs 24.4

FreeBSDのパッケージをupgradeしたら、emacs 24.4がやって来た。

起動したら、load-pathに、.emacs.d を含めるんじゃねぇ。後々問題起すよって警告が 出てきた。後からやってきたくせに脅すのはどうよ。でも、親切に、.emacs.d/lisp とかすればいいよって提案してくれたんで、許してあげる。

今回の目玉は、M-x eww で起動する、ブラウザー。もう、w3mとの繋ぎを入れる面倒さとも おさらばかと思ったら、キーバインドが独特なものになってた。いやがらせですか?

eww mode defined in `eww.el':
Mode for browsing the web.

key             binding
---             -------

TAB             shr-next-link
ESC             Prefix Command
SPC             scroll-up-command
&               eww-browse-with-external-browser
-               negative-argument
0 .. 9          digit-argument
B               eww-list-bookmarks
C               url-cookie-list
H               eww-list-histories
b               eww-add-bookmark
d               eww-download
g               eww-reload
l               eww-back-url
n               eww-next-url
p               eww-previous-url
q               quit-window
r               eww-forward-url
t               eww-top-url
u               eww-up-url
v               eww-view-source
w               eww-copy-page-url
DEL             scroll-down-command
S-SPC           scroll-down-command
<delete>        scroll-down-command
<remap>         Prefix Command

C-M-i           shr-previous-link
M-n             eww-next-bookmark
M-p             eww-previous-bookmark

某フラッシュ満載のURLもすいすい潜り抜けてくれるんで快適。こんなに良い物が付いて いるなら、Windows上のemacsも24.4にしようと思った。

64Bit版はちゃんと本家から出てるのに、32Bit版は無視されてる。32Bit版は自前で作って ねって事で、やってる例が数例見つかったけど、棘なんで止めた。

そう言えば、最近リリースされるLinuxのディストロも32Bit版は止めたってのが多いな。 来月出る、Fedoraは、32Bitユーザーを置き去りにするんだろうか?

しょうがないので、FreeBSD 10.1が出てるんで、Upgradeでもしようかな。BSDは、まだ32Bit ユーザーを大事にしてくれているからね。。。

その前に、Xを立ち上げようとしたらfatalで落ちた。ログを見たら、vmwareのdriverで名前 解決出来ないから、諦めますって。困った脳。BSD上のXアプリを実行したいんだけど、、、。

昔を思い出して、Xを飛ばせばいいんか。ウブの/etc/sshd_configで

X11Forwarding yes

を確認しておいて、Xを上げる。ウブの端末から

sakae@uB:~$ ssh -X fb10

で、BSDに乗り入れて、emacs -r とかすれば良い。ewwでぐぐる様に行ったら、検索の 押しボタンが、ちゃんとそれらしく出てきた。また、pngとおぼしき画像もemacs画面に 張り付いて見えたぞ。いろいろ進歩してんのね。

24.4にもう一つ嬉しい機能が有った。それは、Xを上げなくても、ターミナルに引っ付いた emacsでも、メニュー操作が出来るようになった事。

M-x menu-bar-open(若しくは、F10)でメニューが開ける。後は、矢印キーで、メニューを指定出来る。 左右キー(若しくは、C-f,C-b)で、fileとかeditとかの大項目移動。上下キー(若しくは、C-n, C-p)で、縦方向の 項目指定。こういうのは、ずっと前からサポートして欲しかったぞ。