goで書いた血圧アプリ

『アフリカにょろり旅』(講談社)なんて本を読んだ。

地球の歩き方とか、何とか一人旅とか、ましてや今流行りのTVのロケハン記録記ではない。 にょろりは鰻の事。稚魚が取れないから、どうのこうのとか、日本ウナギは絶滅危惧種に 指定されたとか、話題の多いやつである。

世界で初めて、日本ウナギの産卵場所をほぼ特定して、東京大学海洋研究所のウナギグループの 面々が、うなぎ全18種のうち、まだ採取されていない「ラピアータ」なるものを、アフリカに 求めた時の記録である。

ウナギの研究は、遺伝子の探求って方向と姿形の解析っていう2潮流があるそうな。姿形の 研究には、実物が30固体ぐらいは欲しい。まあ、身長、体重、腹回り、ひげのりっぱさとかを 調べる訳だから、これ統計学。母数は最低でも30ってのはうなずけますよ。

灼熱の50度の大地、住血吸虫だらけの真水、そこに水が有っても飲めないつらさ、寝ても 醒めても襲ってくる蚊。マラリアや今年流行って話題になったデング熱の媒介蚊。 蚊避けスプレーよりも、日本の夏キンチョーの夏が有効とかで、蚊取り線香のパラレル運用 (渦巻きを割って、複数本にする)が、効果的とか。日本が、あまりに無菌国だって事を 実感したとか。

水は貴重。1リットルの水で、シャンプーして体を洗う術を、同行者に指南されるくんだり なんかは、泣けてきますね。オイラー、昨日の洗髪にどれだけ水を使った? 大いに反省。

肝心のウナギ採取、自分らで挑むんだけど、やはり限界がある。現地の漁師にお願いする んだけど、言葉がやはり通じない。絵で説明。体クネクネのボディーランゲージ。中々、協力を 得られない。やはり金で釣るのが効果的。そこで作戦。

ありもしない姿形のうなぎを創造。こやつを捕まえてきたら、現地価格で年収1年分。 求めるラビアータ種なら、幾らって具合に提示。これで、ラビアータが集まるか。 まるで、宝くじの特等を煽るようなもんだな。

で、漁師が集まってわいわいやってるから、期待を込めて行ってみると、マンゴー売りの おばちゃん集団だった。喉が渇いていたので、2個買う事にした。お釣りを貰おうとしたら、 お釣りもマンゴーで払われた。知恵の戦争ですな。

最後は、焦りで、うつ病近くをさ迷うはめに。。この結末はいかに。

読書メーター 7冊 / 2114頁 / 10840円

時間について

Go本の逆引きの章を眺めていたら、時間の扱いが出ていた。時間って、実際のアプリでも 比較的よく出てくるので、デフォでパッケージが付いているのは有り難い。(付いてないのは、 そりゃ、ついてない言語を選びましたねって、目利きの目を疑いましょう)

で、今やってるアプリにも、時間を扱ってる所が無いかと思ったら、ある、ある。 だから、あるある辞典を使わない手はないな。

血圧の測定日時を、オレ様フォーマットで使ってた。YYMMDDHH 表現ね。YYYYは冗長だし、 YYがオーバーフローするまでオイラー生きてねぇから。勿論、分とか秒なんてのを追加 するのも論外。朝か夜か分かればいい、ってんでHHを入れておいたんだ。朝か夜の判断なら AM/PMでもいいんだけど、そうしちゃうと、文字列の扱いになっちゃって、それはそれで、 面倒って訳。

前置きが長くなった。上記のフォーマットを、世間一般のフォーマットに変換して、 グラフのX軸ラベルにでも、いつからいつまでのデータですよって表示したいとか、 データの収録開始日と最終収録日が拾えるので、この間にいくつのデータがあるはず ってのが演算できるはず。それと、実録数を比べれば、測定サボリも一目瞭然。

それには、日時同士の引き算が必要。かくして、実用への要求が生まれた。 叩き台があるんで、それを変形。言語の勉強は、真似から変形・応用ってのが王道ですから。 やりたい事の実験コードを書いてみたぞ。

./ts.go:20: cannot use m (type int) as type time.Month in function argument

そして、早速、怒られたぞ。月だけ、特別扱いですかい。仰せに従って、修正しましょ。

package main

import (
    "fmt"
    "time"
)

func toTime(n int) (time.Time, string) {
    dm := func(n int) (d, m int) {
        return n / 100, n % 100
    }
    div := func(n int) (y, m, d, h int) {
        r, h := dm(n)
        r, d = dm(r)
        y, m = dm(r)
        return y, m, d, h
    }
    y, m, d, h := div(n)
    loc, _ := time.LoadLocation("Local")
    t := time.Date(2000+y, time.Month(m), d, h, 0, 0, 0, loc)
    s := fmt.Sprintf("%d/%02d/%02d", 2000+y, m, d)
    return t, s
}

func main() {
    srt, st := toTime(13100104)
    end, sp := toTime(13101021)
    sub := end.Sub(srt)
    days := int(sub / (24 * time.Hour))

    fmt.Println(st, sp)
    fmt.Println(sub)
    fmt.Println(days)
}

オレ様時間フォーマットを与えて、go worldな時間データと、日本風フォーマットの文字列 を同時に得る関数を書いた。mainの中は、そこ使用例。で、実行結果は、

2013/10/01 2013/10/10
233h0m0s
9

こんなコードにも、5分な魂が有る訳で。。。関数の中で関数の定義は禁止されてるけど、 匿名関数に名前を付ける事は許されている。これって、Javascript流。JSを設計した人は、 Schemeをやりたかったんだけど、世間の総スカンを食いそうなんで、JS文法を発明したらしい。

暫く前に2chのlisp板で喧々諤々となっていた、 SchemeのC言語風のアルゴル系言語表現 も、これと同列かしら。まあ、こういうのも有りで、オイラーはとやかく言う筋合いではないだろう。 (偉そうに、上から目線で何を言うってのは無しの方向で、よしなにね。)

time.TIMEとか

折角パッケージのソースというお宝が付いてきているんで、これを拝まない手は無い。

まず、上で出てきた、数値をTime型に変換すDateの部分。time.goを検索すると

func (t Time) Date() (year int, month Month, day int) {
    year, month, day, _ = t.date(true)
    return
}

のと、

func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time {
    if loc == nil {
        panic("time: missing Location in call to Date")
    }
      :

のが出てくる。今回使ってるのは、後者。同じ関数名のくせに機能は正反対になってる。 型が違うね。grep等で検索する時は、要注意。後者の引数並びを見ると、monthの型が月型に なってる。この月型って何や?

type Month int

const (
    January Month = 1 + iota
    February
      :
var months = [...]string{
    "January",
    "February",
       :

Monthはintと同列だけど、範囲を明確にする為、わざわざ型を宣言して、13月なんてのを 排除してるんだな。そして外部表現と言うか、一般ユーザーのための、文字列定義も なされている。日本で使うなら、この部分を、神無月とか弥生とかにしとくと、日本的 情緒が出て、良いかも知れない。

そう言えば、日時の表現もいろいろあるのね。どんなのが定義されてるか? format.goに 有ったぞ。但し、日本的なのは皆無

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"
       :
    Kitchen     = "3:04PM"
        :

RubyDateって、あの人の言語のやつ? Rubyには強力なdateパッケージを提供してくれてる 人が居ましたから、その功績が讃えられて標準となったんでしょうか。RFC822って、メールの 時間表現だったかな。これを引き継いだapacheだったかのログを解析する時、えらい迷惑を こうむったな。と、遠い記憶が蘇ってきましたよ。

まあまあ、硬い事は言わないの。台所で ママにも認識出来る、時間フォーマットも有りますから。3時のおやつは大事だよーー。 ママには、年月日の感覚が欠落してて、ひたすら毎日、食事の用意にいそしんでくれている 姿が目に浮かぶようですよ。

これらのフォーマットは、次のように使います。

fmt.Println(end.Format(time.RubyDate))  // Thu Oct 10 21:00:00 +0900 2013
fmt.Println(end.Format(time.Kitchen))   // 9:00PM

それじゃ、自分でフォーマットを決められないかと言うと、機械学習?させて、出力出来る。

    fmt.Println(end.Format("2006/01/02 15:04:05 MST"))
    fmt.Println(srt.Format("2006/01/02 PM 3:04"))
    fmt.Println(srt.Format("Mon,02.01.2006"))

こういう出力の学習データに対して、結果は

2013/10/10 21:00:00 JST
2013/10/01 AM 4:00
Tue,01.10.2013

こんな具合。学習用データ("2006/01/02 15:04:05 MST")を覚えておかないと、変な結果に なってしまうのは、ご愛嬌? 学習データを良くみると、06は、従来で言うYYに相当、15は HHに相当って具合に、巧妙に仕組まれてて、これらの情報を元に、内部でフォーマットを 作り出している。

format.goの冒頭付近に、

// These are predefined layouts for use in Time.Format and Time.Parse.
// The reference time used in the layouts is:
//  Mon Jan 2 15:04:05 MST 2006
// which is Unix time 1136239445. Since MST is GMT-0700,
// the reference time can be thought of as
//  01/02 03:04:05PM '06 -0700

こんなのが出てる。最後の行に注目。このコードを書いた人の頭の中にある、日時の表し 型だ。左から右に、2桁づつで、01,02...と綺麗に並んでいるではないか。-7時間の時差って、 どのあたりとかプロファイリングして見れ。

3時4分を午後って捉えるのは、きっとおやつが 欲しい、お坊ちゃま(お嬢様)かもね。普通、0304ったら、軍隊式の24時間時刻表記で、 日常風に直すと、 午前様になる。 それを午後ってこじつけるのは、やっぱり、まだ軍隊経験が無い、若い奴だな。

と言うことで、米式日時表記を覚えておけば、日本式に表示した時に変な数字並びだよって そんなの覚えられないって悩みは、すっきり解消します。オイラーは、ヨーロッパ式の 時刻表記が好きなんですけどね。

時間の差分は、Duration型として、Time型とは別に扱われている。このDuration型は、 経過時間を表すのに便利なんだけど、定義は

const (
    Nanosecond  Duration = 1
    Microsecond          = 1000 * Nanosecond
    Millisecond          = 1000 * Microsecond
    Second               = 1000 * Millisecond
    Minute               = 60 * Second
    Hour                 = 60 * Minute
)

になってる。一見、単位付きの演算っぽい。そこまではいいんだけど、最上位単位が時間 ってのはどうよ。もっと上の単位、日とかは無いのか? 弁解がましいコメントが 書いてあったぞ。

// Common durations.  There is no definition for units of Day or larger
// to avoid confusion across daylight savings time zone transitions.

ようするに、冬時間、夏時間を跨ぐと面倒くせえから、やらねぇよって事。日本でも、 省エネに有効ってんで、夏時間を取り入れようって機運があったけど、あんな面倒な ものやらない方がいいよ。海外出張してた時、これに引っかかって、遅刻した事が あったな。

ああ、面倒と言えば、うるう年はちゃんと計算してるか?

const (
    secondsPerMinute = 60
    secondsPerHour   = 60 * 60
    secondsPerDay    = 24 * secondsPerHour
    secondsPerWeek   = 7 * secondsPerDay
    daysPer400Years  = 365*400 + 97
    daysPer100Years  = 365*100 + 24
    daysPer4Years    = 365*4 + 1
)

こんなのが定義されてた。4年に一度、うるう年が巡ってくるよってな具合。こういう天文と 言うか、施政者が自由に決める特権がある暦は、不合理なものの固まり。

一つ例を上げれば、月の日数、31日ある月を大の月、それ以外を小の月なんて言う。 1,3,5,7月は大の月、そうか、奇数月は大の月と、がてんすると、8月を境に今度は偶数の月が 大の月になる。

これ、昔の王様の誕生月が8月だった。これが小の月とはけしからん。9月と入れ替えちゃえ、 後は、バランスを考えて偶数月を大の月にせよ。苦しゅうないぞって具合。

一某人の生死によって、年号が変わるのはどうよ。それが、免許証なりの有効期限に 正式採用されてるって、はなはだ不都合と思うぞ。TPPで叩いてくれよ。某国の人。 外圧が無いと、変わらない(変えられない)国、日本。

不都合と言えば、うるう秒もやり玉に上がってますなあ。どっかのOSがこれの処理にとちって、 大変な事になってた。もう、こういうの経験したくないから、うるう秒は、まじ廃止して くれって、圧力をかけてる団体があるそうな。どうなるんでしょうかね? 国際天文学会とIT業界の戦い。きっと、声の大きい方が勝つぞ。

血圧ノートのまとめ

ipadのアプリで血圧ノートってのが有って、それを入れたんだけど、扱いが面倒。だったら 自分で作れよという事で、いろいろな言語で作ってみた。goでも作ったんで、少し本腰を 入れてまとめてみた。前回から少し進歩させてみたんだ。

まずは、継続は力なりって事で、挫折しないようにデータをちゃんと測定する事。 真面目にデータを集めているか分かる必要がある。

sakae@uB:~/godev/src/bld$ ./bld
2013/01/01 to 2013/12/31 data having.
Nunber of exp: 730, actual: 720.
type 'bld -h' to see command options.

収録の開始日と終了日を元に、幾つのデータが有るはずってのを出したよ。 上記だと、5日分の未測定日があるな。旅行に行ってたんだと言う、言い訳は、、、まあ 有りでしょうな。これを補うには、携帯型の血圧計が必要だけど、身の回り品を最小に するってのは、出張につぐ出張、日本はおろか世界を股にかけたオイラーのポリシーに 反するんで、却下します。

それから、表記が英語(もどき)になってるのは、主稼動環境にWindowsを想定してるから。 CUIのアプリなんで、cmd.comな端末を想定。こやつ、日本語不完全表示症候群に かかっている。 これは、出目が帰国子女と言うか移住子女なんで、シフト日本語は上手に表示するけど(自分で設計したんだから)、 最近流行の国際的UTF日本語は、おぼつかない所があるんだ。だったら、愛嬌のある英語でも いいじゃんって事なのさ。

なお、Go本には、この不完全表示症候群を調教する方法が出てたけど、染み付いた癖は 完全に修正出来ないみたいだ。

次は、データを絞り込み過ぎちゃって、有効なデータが無い場合の警告。

sakae@uB:~/godev/src/bld$ ./bld -am -pm
Picked data size is ZERO, so can not do.

起床時と就寝時のデータを同時に拾い出そうとしたら、そりゃ、データサイズがゼロに なるわな。他にも、無いデータを拾い出そうというケースが有るのでまとめてチェック。

sakae@uB:~/godev/src/bld$ ./bld -eq 1312 -hd 100 -lgp
Warnning: Req size is to big, apply possible size.

13年12月のデータの頭から100個を取り出して、gnuplotでpngファイルにしてねって指令。 データ数は、一月分で最大でも62個。何も対策しておかないと、忌まわしい、Index out of range に見舞われて、落ちちゃったはず。こういうのは、エラーに落としてもいいんだけど、 それじゃあんまり。有るデータで何とかするようにして、警告を出すようにした。 どきっとして、血圧が上昇するよりは、体に優しい仕様が良い。

後は、内蔵のグラフパッケージよりもgnuplotの方が、綺麗な絵を描いてくれるので、 こちらをメインに、なるべく情報を載せるようにしたよ。

以下、400行、バイナリーサイズ、5.6Mの何処でも動くシングルバイナリー用の元です。

// blood check go version
package main

import (
	"code.google.com/p/plotinum/plot"
	"code.google.com/p/plotinum/plotter"
	"code.google.com/p/plotinum/plotutil"
	"encoding/csv"
	"flag"
	"fmt"
	//	"github.com/kr/pretty" // http://godoc.org/github.com/kr/pretty
	"io/ioutil"
	"math"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

const dbfile = "current.csv"

type AAy [][4]int // main data type
var bld AAy       // blood data

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

func failOnError(err error) {
	if err != nil {
		panic(err)
	}
}

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

func savecsv(csf string) {
	buf, _ := ioutil.ReadFile(csf)
	_ = ioutil.WriteFile("Backup.csv", buf, 0666)
	fd, err := os.OpenFile(csf, os.O_CREATE|os.O_WRONLY, 0666)
	failOnError(err)
	err = fd.Truncate(0)
	failOnError(err)
	defer func() {
		fd.Close()
	}()
	w := csv.NewWriter(fd)
	for _, el := range bld {
		rows := make([]string, 0)
		for _, v := range el {
			rows = append(rows, strconv.Itoa(v))
		}
		w.Write(rows)
	}
	w.Flush()
}

func ire(ym int) {
	var a, b, c, d string
	var rs [4]int
	for {
		a, b, c, d = "", "", "", ""
		fmt.Printf("%d> ", ym)
		fmt.Scanln(&a, &b, &c, &d)
		if a == "fin" {
			break
		}
		rs[ymdh], _ = strconv.Atoi(a)
		rs[hi], _ = strconv.Atoi(b)
		rs[low], _ = strconv.Atoi(c)
		rs[pls], _ = strconv.Atoi(d)
		if rs[ymdh] > 3123 || rs[hi] < 80 || rs[low] < 40 || rs[pls] < 40 {
			fmt.Println("Bad")
			continue
		}
		rs[ymdh] += (ym * 10000)
		if rs[ymdh] <= bld[len(bld)-1][ymdh] {
			fmt.Println("Bad Date")
			continue
		}
		bld = append(bld, rs)
	}
	savecsv(dbfile)
}

//func ppp(obj interface{}) {
//	pretty.Printf("%# v\n", obj)
//}

func pp(ds AAy) {
	for _, el := range ds {
		_, js := toTime(el[ymdh])
		fmt.Printf("%s,%4d,%4d,%4d\n", js, el[hi], el[low], el[pls])
	}
}

func w(i int, ds AAy) []int { // take hi,low or pls
	var rs []int
	for _, el := range ds {
		rs = append(rs, el[i])
	}
	return rs
}

func hd(n int, ds AAy) AAy {
	if len(ds) < n {
		fmt.Println("Warnning: Req size is to big, apply possible size.")
		return ds
	}
	return ds[:n]
}

func tl(n int, ds AAy) AAy {
	if len(ds) < n {
		fmt.Println("Warnning: Req size is to big, apply possible size.")
		return ds
	}
	return ds[len(ds)-n:]
}

func am(ds AAy) AAy {
	var rs AAy
	for _, el := range ds {
		if el[ymdh]%100 < 12 {
			rs = append(rs, el)
		}
	}
	return rs
}

func pm(ds AAy) AAy {
	var rs AAy
	for _, el := range ds {
		if el[ymdh]%100 >= 12 {
			rs = append(rs, el)
		}
	}
	return rs
}

func frm(ym int, ds AAy) AAy {
	var rs AAy
	lmt := ym * 10000
	for _, el := range ds {
		if el[ymdh] > lmt {
			rs = append(rs, el)
		}
	}
	return rs
}

func utl(ym int, ds AAy) AAy {
	var rs AAy
	lmt := ym*10000 + 3124
	for _, el := range ds {
		if el[ymdh] < lmt {
			rs = append(rs, el)
		}
	}
	return rs
}

func eq(ym int, ds AAy) AAy {
	var rs AAy
	for _, el := range ds {
		if el[ymdh]/10000 == ym {
			rs = append(rs, el)
		}
	}
	return rs
}

func toTime(n int) (time.Time, string) {
	dm := func(n int) (d, m int) {
		return n / 100, n % 100
	}
	div := func(n int) (y, m, d, h int) {
		r, h := dm(n)
		r, d = dm(r)
		y, m = dm(r)
		return y, m, d, h
	}
	y, m, d, h := div(n)
	loc, _ := time.LoadLocation("Local")
	t := time.Date(2000+y, time.Month(m), d, h, 0, 0, 0, loc)
	//	s := fmt.Sprintf("%d/%02d/%02d", 2000+y, m, d)
	s := t.Format("2006/01/02pm")
	return t, s
}

func lg(png bool, ttl string, ds AAy) {
	var a3 [][]int
	a3 = append(a3, w(hi, ds), w(low, ds), w(pls, ds))
	mn := fmt.Sprintf("mean: " + ssf(mean, a3))
	std := fmt.Sprintf("std:  " + ssf(std, a3))
	_, st := toTime(ds[0][ymdh])
	_, sp := toTime(ds[len(ds)-1][ymdh])
	xl := fmt.Sprintf("%s <-- date --> %s", st, sp)
	s := ""
	if png {
		s += "set terminal png\n"
		s += "set output 'zzz.png'\n"
	}
	s += "set grid\n"
	s += fmt.Sprintf("set label 1 at first 10,92 '%s' font 'monospace'\n", mn)
	s += fmt.Sprintf("set label 2 at first 10,87 '%s' font 'monospace'\n", std)
	s += fmt.Sprintf("set xlabel '%s'\n", xl)
	s += fmt.Sprintf("set title '%s'\n", ttl)
	s += fmt.Sprintln("plot '-' w l")
	for i, v := range w(hi, ds) {
		s += fmt.Sprintln(i, v)
	}
	s += fmt.Sprintln("")
	for i, v := range w(low, ds) {
		s += fmt.Sprintln(i, v)
	}
	s += fmt.Sprintln("end")
	cmd := exec.Command("gnuplot", "-p")
	cmd.Stdin = strings.NewReader(s)
	err := cmd.Run()
	failOnError(err)
	//	fmt.Scanln(&s) // keep running go on emacs(kill for C-x k)
}

func min(a []int) float64 {
	rs := math.MaxInt32
	for _, v := range a {
		if v < rs {
			rs = v
		}
	}
	return float64(rs)
}

func max(a []int) float64 {
	rs := math.MinInt32
	for _, v := range a {
		if v > rs {
			rs = v
		}
	}
	return float64(rs)
}

func mean(a []int) float64 {
	s := 0
	for _, v := range a {
		s += v
	}
	return float64(s) / float64(len(a))
}

func std(a []int) float64 {
	m := mean(a)
	s2 := 0.0
	for _, v := range a {
		s2 += (float64(v) - m) * (float64(v) - m)
	}
	return math.Sqrt(s2 / float64(len(a)))
}

func ssf(fn func([]int) float64, a3 [][]int) string {
	rs := ""
	for _, v := range a3 {
		rs += fmt.Sprintf("%6.1f", fn(v))
	}
	return rs
}

func ss(ds AAy) {
	var a3 [][]int
	a3 = append(a3, w(hi, ds), w(low, ds), w(pls, ds))
	fmt.Printf("size: %d\n", len(ds))
	fmt.Println("min:  " + ssf(min, a3))
	fmt.Println("mean: " + ssf(mean, a3))
	fmt.Println("max:  " + ssf(max, a3))
	fmt.Println("std:  " + ssf(std, a3))
}

func toXYs(av []int) plotter.XYs {
	pts := make(plotter.XYs, len(av))
	for i, v := range av {
		pts[i].X = float64(i)
		pts[i].Y = float64(v)
	}
	return pts
}

func pg(ttl string, ds AAy) {
	p, _ := plot.New()
	p.Title.Text = ttl
	p.X.Label.Text = "Date"
	p.Y.Label.Text = "Value"
	p.Add(plotter.NewGrid())
	plotutil.AddLines(p, "Hi", toXYs(w(hi, ds)))
	plotutil.AddLines(p, "Low", toXYs(w(low, ds)))
	//	plotutil.AddLines(p, "Pls", toXYs(w(pls, ds)))
	width := 6.0
	height := 4.0
	p.Save(width, height, "zzz.png")
}

func mkttl(args string) string {
	ttl := ""
	switch {
	case strings.Index(args, ":am:") >= 0:
		ttl = "at Wakeup"
	case strings.Index(args, ":pm:") >= 0:
		ttl = "at Night"
	default:
		ttl = "All days"
	}
	return ttl
}

func main() {
	var vire *int = flag.Int("ire", 1411, "Input YYMM's data")
	var veq *int = flag.Int("eq", 1411, "Pick YYMM's data")
	var vfrm *int = flag.Int("frm", 1001, "Pick from YYMM's data")
	var vutl *int = flag.Int("utl", 9912, "Pick until YYMM's data")
	var vhd *int = flag.Int("hd", 10, "Pick first n data")
	var vtl *int = flag.Int("tl", 10, "Pick last n data")
	var bam *bool = flag.Bool("am", false, "Pick wakeup data")
	var bpm *bool = flag.Bool("pm", false, "Pick at_night data")
	var bpp *bool = flag.Bool("pp", false, "Show picked data")
	var bss *bool = flag.Bool("ss", false, "Show stats summary")
	var blg *bool = flag.Bool("lg", false, "Show line graph (use GNUPLOT)")
	var blgp *bool = flag.Bool("lgp", false, "Save line graph (use GNUPLOT)")
	var bpg *bool = flag.Bool("pg", false, "Save graph")
	flag.Parse()
	args := ""
	flag.Visit(func(f *flag.Flag) {
		args += fmt.Sprintf(":%s: ", f.Name)
	})
	//	fmt.Println(args) // monitor selected options

	mycsv(dbfile)
	if strings.Index(args, ":ire:") >= 0 {
		ire(*vire)
	}
	z := bld
	if *bpm && strings.Index(args, ":pm:") >= 0 {
		z = pm(z)
	}
	if *bam && strings.Index(args, ":am:") >= 0 {
		z = am(z)
	}
	if strings.Index(args, ":eq:") >= 0 {
		z = eq(*veq, z)
	}
	if strings.Index(args, ":frm:") >= 0 {
		z = frm(*vfrm, z)
	}
	if strings.Index(args, ":utl:") >= 0 {
		z = utl(*vutl, z)
	}
	if strings.Index(args, ":hd:") >= 0 {
		z = hd(*vhd, z)
	}
	if strings.Index(args, ":tl:") >= 0 {
		z = tl(*vtl, z)
	}
	if len(z) == 0 {
		fmt.Println("Picked data size is ZERO, so can not do.")
		os.Exit(1)
	}
	if *bpp && strings.Index(args, ":pp:") >= 0 {
		pp(z)
	}
	if *bss && strings.Index(args, ":ss:") >= 0 {
		ss(z)
	}
	if *blg && strings.Index(args, ":lg:") >= 0 {
		ttl := mkttl(args)
		lg(false, ttl, z)
	}
	if *blgp && strings.Index(args, ":lgp:") >= 0 {
		ttl := mkttl(args)
		lg(true, ttl, z)
	}
	if *bpg && strings.Index(args, ":pg:") >= 0 {
		ttl := mkttl(args)
		pg(ttl, z)
	}
	if args == "" {
		tst, st := toTime(bld[0][ymdh])
		tsp, sp := toTime(bld[len(bld)-1][ymdh])
		dd := tsp.Sub(tst)
		nexp := int(dd/(24*time.Hour))*2 + 2
		fmt.Println(st, "to", sp, "data having.")
		fmt.Printf("Nunber of exp: %d, actual: %d.\n", nexp, len(bld))
		fmt.Println("type 'bld -h' to see command options.")
	}
}

初回は、csvファイルが無いので、1データだけを入力した種ファイルをあらかじめ 作成しておく事。改行は不要。

特に、Windowsの場合、改行はCRLFで構成されてるが、 CRは不用なので、EXCEL等からのファイルを利用する場合、適当なEditorで取り除いて おく。goはやっぱりUnix系で開発されてるんで、Windowsのマナーには馴染まない所が あるな。

上のコードでは、gnuplotへ送り込むデータを文字列に溜め込んでいたけど、下記の例の ようにすると、その都度パイプに送り込める。この技は、Start/Waitを組で使うと可能に なる。

gunplotはエラーをStderrに返してくれるんで、このパイプをモニターしてる。

package main

import "fmt"
import "io/ioutil"
import "os/exec"

func main() {
        xx := [...]int{3, 6, 1, 2, 7, 3, 6, 2, 7, 9, 8, 4, 0,
                3, 5, 3, 4, 2, 1, 6, 3, 2, 1, 4, 5, 6, 7, 5, 3, 2}

        gpCmd := exec.Command("gnuplot", "-p")
        gpIn, _ := gpCmd.StdinPipe()
//      gpOut, _ := gpCmd.StdoutPipe()
        gpOut, _ := gpCmd.StderrPipe()

        gpCmd.Start()
        gpIn.Write([]byte("plot '-' w l\n"))
        for i, v := range xx {
                gpIn.Write([]byte(fmt.Sprintln(i, v)))
        }
        gpIn.Write([]byte("end"))
        gpIn.Close()
        gpBytes, _ := ioutil.ReadAll(gpOut)
        gpCmd.Wait()

        fmt.Println(string(gpBytes))
}

Fedora21

「Fedora 21」が公開、Cloud、Server、Workstationの3エディション構成に ってんで、行ってみたら32Bit版が出てた。やっぱり財政基盤がしっかりしてると、 サービスが違うな。

今、サーバー版を落としてきて、ネットワークインストール中。 完了したら、

yum install haskell-platform

の予定。 それから、こんな釣り記事、 オブジェクト指向や関数型は「敗者の道具」である に誘われて、ocamlも入れておくかな。

まだまだ先は長そうなので、ヒューマンエラー学 を読んでいる。 多様性工学 Diversity Engineering も、面白そう。

ミニマムな構成を選んだのに、後処理の所でまるでハングしたように黙りこくっちゃったなあ。 それでも、1時間ぐらいしたら終わってた。

起動後、何は何でもsshlogin出来るように、sshdを立てる。

systemctl enable sshd.service

FreeBSDも将来的に、こんな風に管理したいってアドバルーンを揚げてる人が居る ようだけど、そんなLinux風にしなくてもいいと思うぞ。でも、Fedoraみたいに、 アマチュア無線ガイド を、用意してくれるなら、許しちゃうかも?

で、ミニマムなんで、何も入っていない。必要なのは、fedora20で探しておいたよ。

[sakae@fedora ~]$ rpm -qf /usr/bin/locate
mlocate-0.26-7.fc21.i686
[sakae@fedora ~]$ rpm -qf /usr/sbin/ifconfig
net-tools-2.0-0.31.20141124git.fc21.i686

必要そうな物を入れても、現在この程度。20の時は、10Gも消費してたから雲泥な違い。

[sakae@fedora ~]$ df -h
ファイルシス                           サイズ  使用  残り 使用% マウント位置
/dev/mapper/fedora--server_fedora-root    14G  2.7G   11G   21% /
devtmpfs                                 369M     0  369M    0% /dev
tmpfs                                    375M     0  375M    0% /dev/shm
tmpfs                                    375M  520K  374M    1% /run
tmpfs                                    375M     0  375M    0% /sys/fs/cgroup
tmpfs                                    375M  4.0K  375M    1% /tmp
/dev/sda1                                477M   91M  357M   21% /boot
tmpfs                                     75M     0   75M    0% /run/user/1000

X無しが効いているのか、Desktop関連が効いているのかは知らん。潔くXも入れていない 弊害が一つあった。XmingでWindows7側にGUI関係を飛ばそうとすると、displayが無いぜ になっちゃう。しょうがないので、

export DISPLAY=Win7IP:0

なんてのを思い出したように指定した。(Win7IPは、Windows7のIPaddress) これ、常時指定しておいてもいいんだけど、emacs24.4がPuTTYの端末で便利に使えるので、 そこまでしなくてもいいかな。

ocaml関係がFedoraでは見事に無視されてたぞ。こういう時は、opamという、パッケージマネージャ兼 バージョン切り替え機をいれるんだな。それから、ゆるゆると、金融機関で使われていると いうcoreを入れる。後はお好みに合わせて、超強力なomakeとか、replを改善してくれると 言うutopあたりを入れればいいか。